openclaw - ✅(Solved) Fix [Bug]: DashScope provider usage always zero due to field name mismatch [5 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#54859Fetched 2026-04-08 01:35:08
View on GitHub
Comments
1
Participants
2
Timeline
19
Reactions
0
Timeline (top)
cross-referenced ×7referenced ×7labeled ×2closed ×1

Root Cause

The root cause is that buildAssistantMessageFromResponse directly accesses input_tokens and output_tokens from the API response. However, DashScope (and many OpenAI-compatible APIs) returns these fields as prompt_tokens and completion_tokens. While the codebase already contains a normalizeUsage function capable of handling these variations, it is not being utilized in this specific path.

Fix Action

Fixed

PR fix notes

PR #54867: fix: use normalizeUsage for DashScope usage metrics

Description (problem / solution / changelog)

Summary

  • buildAssistantMessageFromResponse directly accessed input_tokens/output_tokens from the API response, but DashScope returns prompt_tokens/completion_tokens instead
  • Replaced direct field access with normalizeUsage() which already handles all provider naming variants
  • This fixes zero usage reporting for DashScope and any other provider using OpenAI-compat naming

Fixes #54859

Test plan

  • Existing buildAssistantMessageFromResponse tests pass (49 tests)
  • Usage normalization tests pass (26 tests)
  • All lint/format/type checks pass
  • Manual verification with DashScope provider showing non-zero usage

🤖 Generated with Claude Code

Changed files

  • src/agents/openai-ws-stream.ts (modified, +11/-5)

PR #54871: Normalize OpenAI-compatible websocket usage fields

Description (problem / solution / changelog)

Summary

  • normalize websocket response usage through the existing normalizeUsage helper
  • preserve cache usage fields when present instead of dropping them on the floor
  • add regression coverage for providers that return prompt_tokens / completion_tokens

Testing

  • pnpm exec vitest run --config vitest.config.ts src/agents/openai-ws-stream.test.ts
  • pnpm exec vitest run --config vitest.config.ts src/agents/usage.test.ts src/agents/usage.normalization.test.ts

Closes #54859

Changed files

  • src/agents/openai-ws-stream.test.ts (modified, +14/-1)
  • src/agents/openai-ws-stream.ts (modified, +7/-3)

PR #54922: fix: use normalizeUsage in buildAssistantMessageFromResponse for DashScope compatibility

Description (problem / solution / changelog)

Fixes #54859

Problem

DashScope provider (and other OpenAI-compatible APIs) returns prompt_tokens and completion_tokens in usage data, but buildAssistantMessageFromResponse directly accesses input_tokens and output_tokens, resulting in all usage metrics being recorded as zero.

Fix

Use the existing normalizeUsage function which already handles all field name variations (prompt_tokens, input_tokens, completion_tokens, output_tokens, etc.) instead of directly accessing specific field names.

Impact

  • Fixes cost tracking for DashScope and other OpenAI-compatible providers
  • No behavior change for providers that already use input_tokens/output_tokens

Changed files

  • src/agents/openai-ws-stream.ts (modified, +7/-3)

PR #54940: fix: normalize openai-completions usage fields for DashScope and compatible providers

Description (problem / solution / changelog)

Summary

When using DashScope (or other OpenAI-compatible providers) configured with "api": "openai-completions", all usage metrics (input, output, cost) are recorded as zero in session transcripts.

Root Cause

In buildAssistantMessageFromResponse (src/agents/openai-ws-stream.ts), usage was read with direct field access:

usage: buildUsageWithNoCost({
  input: response.usage?.input_tokens ?? 0,   // undefined for DashScope
  output: response.usage?.output_tokens ?? 0, // undefined for DashScope
  totalTokens: response.usage?.total_tokens ?? 0,
})

DashScope (and many OpenAI-compatible APIs) return prompt_tokens / completion_tokens instead of input_tokens / output_tokens. The codebase already had a normalizeUsage() helper in src/agents/usage.ts that handles all field name variants — it just wasn't being called here.

Fix

Replace the direct field access with normalizeUsage():

const normalized = normalizeUsage(response.usage);
usage: buildUsageWithNoCost({
  input: normalized?.input ?? 0,
  output: normalized?.output ?? 0,
  totalTokens: normalized?.total ?? response.usage?.total_tokens ?? 0,
})

Testing

Added a test in src/agents/openai-ws-stream.test.ts covering the prompt_tokens/completion_tokens path. All 57 existing tests continue to pass.

Fixes #54859


AI Assistance

  • AI-assisted (Claude Sonnet 4.6 via OpenClaw)
  • Lightly tested — pnpm check (lint, format, type checks, boundary checks) passed; targeted unit tests added/verified
  • Full pnpm build && pnpm test not run (CI will validate)
  • Code reviewed and understood by human author
  • Codex review not run locally; CI Codex review will apply

Changed files

  • src/agents/openai-ws-stream.test.ts (modified, +11/-0)
  • src/agents/openai-ws-stream.ts (modified, +8/-5)

PR #54997: fix: prevent full re-render on every keystroke in webchat input

Description (problem / solution / changelog)

Fixes #54874

Root Cause

chatMessage was decorated with @state() in app.ts, causing Lit to trigger a full component re-render on every keystroke. With reasoning/verbose content in the chat history, this re-render is expensive (500ms–1s per key).

This explains why disabling reasoning/verbose partially mitigated the issue — less content to re-render.

Fix

Remove @state() from chatMessage. The textarea value is managed natively by the browser DOM; Lit only needs to sync it when other reactive state changes (e.g. on send/clear). This eliminates the per-keystroke re-render while keeping all existing behavior intact.

Bonus fix

Also normalizes prompt_tokens/completion_tokens aliases in cli-runner/helpers.ts for DashScope and other OpenAI-compatible providers (partial fix for #54859).

Changes

  • ui/src/ui/app.ts — remove @state() from chatMessage
  • src/agents/cli-runner/helpers.ts — add prompt_tokens/completion_tokens fallback aliases

Changed files

  • src/agents/cli-runner/helpers.ts (modified, +2/-2)
  • ui/src/ui/app.ts (modified, +3/-1)

Code Example

{
      "models": {
        "providers": {
          "dashscope": {
            "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
            "apiKey": "${DASHSCOPE_API_KEY}",
            "api": "openai-completions",
            "models": [{ "id": "kimi-k2.5", "name": "Kimi K2.5" }]
          }
        }
      }
    }

---

grep -A5 '"provider":"dashscope"' agents/main/sessions/*.jsonl

---

"usage": {
      "input": 0,
      "output": 0,
      "cacheRead": 0,
      "cacheWrite": 0,
      "totalTokens": 0,
      "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0, "total": 0 }
    }

---

// Existing logic in normalizeUsage
const rawInput = asFiniteNumber(
  raw.input ??
  raw.inputTokens ??
  raw.input_tokens ??
  raw.promptTokens ??
  raw.prompt_tokens  // DashScope uses this
);

const output = asFiniteNumber(
  raw.output ??
  raw.outputTokens ??
  raw.output_tokens ??
  raw.completionTokens ??
  raw.completion_tokens  // DashScope uses this
);

---

{
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 10,
    "total_tokens": 20
  }
}

---

// ❌ Fails for DashScope
usage: buildUsageWithNoCost({
  input: response.usage?.input_tokens ?? 0,      // undefined
  output: response.usage?.output_tokens ?? 0,    // undefined
  totalTokens: response.usage?.total_tokens ?? 0 // works
})

---

// ✅ Fixed Implementation
const normalized = normalizeUsage(response.usage);

usage: buildUsageWithNoCost({
  input: normalized?.input ?? 0,
  output: normalized?.output ?? 0,
  totalTokens: normalized?.total ?? response.usage?.total_tokens ?? 0
});
RAW_BUFFERClick to expand / collapse

Here is the refined and formatted version of your bug report, optimized for clarity, readability, and submission to a GitHub issue tracker or development team.


Bug Report: DashScope Usage Metrics Always Zero in Session Transcripts

Bug Type: Regression (Functioned previously, now fails)
Severity: Medium (Cost tracking broken; API functionality remains intact)
OpenClaw Version: 2026.3.23-2
OS: Ubuntu 22.04
Install Method: pnpm global


Summary

When using the DashScope provider configured with "api": "openai-completions", all usage metrics (input, output, cost) are recorded as zero in session transcripts.

The root cause is that buildAssistantMessageFromResponse directly accesses input_tokens and output_tokens from the API response. However, DashScope (and many OpenAI-compatible APIs) returns these fields as prompt_tokens and completion_tokens. While the codebase already contains a normalizeUsage function capable of handling these variations, it is not being utilized in this specific path.


Steps to Reproduce

  1. Configure OpenClaw with the DashScope provider:
    {
      "models": {
        "providers": {
          "dashscope": {
            "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
            "apiKey": "${DASHSCOPE_API_KEY}",
            "api": "openai-completions",
            "models": [{ "id": "kimi-k2.5", "name": "Kimi K2.5" }]
          }
        }
      }
    }
  2. Send any message through the agent.
  3. Inspect the generated session file (e.g., agents/main/sessions/*.jsonl):
    grep -A5 '"provider":"dashscope"' agents/main/sessions/*.jsonl
  4. Observation: Usage fields are consistently zero:
    "usage": {
      "input": 0,
      "output": 0,
      "cacheRead": 0,
      "cacheWrite": 0,
      "totalTokens": 0,
      "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0, "total": 0 }
    }

Expected Behavior

Usage metrics should be correctly populated from the API response. The existing normalizeUsage utility already supports the necessary field mappings:

// Existing logic in normalizeUsage
const rawInput = asFiniteNumber(
  raw.input ??
  raw.inputTokens ??
  raw.input_tokens ??
  raw.promptTokens ??
  raw.prompt_tokens  // DashScope uses this
);

const output = asFiniteNumber(
  raw.output ??
  raw.outputTokens ??
  raw.output_tokens ??
  raw.completionTokens ??
  raw.completion_tokens  // DashScope uses this
);

Actual Behavior

All usage fields default to zero because buildAssistantMessageFromResponse bypasses normalizeUsage and performs direct property access on non-existent fields.

Verified DashScope Response Structure:

{
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 10,
    "total_tokens": 20
  }
}

Current Broken Implementation (pi-embedded-CbCYZxIb.js):

// ❌ Fails for DashScope
usage: buildUsageWithNoCost({
  input: response.usage?.input_tokens ?? 0,      // undefined
  output: response.usage?.output_tokens ?? 0,    // undefined
  totalTokens: response.usage?.total_tokens ?? 0 // works
})

Proposed Fix

Replace direct field access with the existing normalizeUsage helper to ensure compatibility across all OpenAI-compatible providers.

Code Change:

// ✅ Fixed Implementation
const normalized = normalizeUsage(response.usage);

usage: buildUsageWithNoCost({
  input: normalized?.input ?? 0,
  output: normalized?.output ?? 0,
  totalTokens: normalized?.total ?? response.usage?.total_tokens ?? 0
});

Impact Analysis

  • Affected Users: All users utilizing DashScope or other OpenAI-compatible APIs that adhere to the standard prompt_tokens/completion_tokens naming convention.
  • Frequency: 100% of requests when using the affected configuration.
  • Consequence: Users cannot track token consumption or estimate costs via OpenClaw session logs; they are forced to rely on external provider dashboards.
  • Note: The openai-codex provider functions correctly, likely because it uses a different response parsing path. Other providers using the openai-completions API type may also be susceptible to this issue.

Additional Context

  • The normalizeUsage function is already present in the codebase and correctly handles the field name variants.
  • This appears to be a regression where the specific integration point for openai-completions was updated to use direct access instead of the normalized helper.

extent analysis

Fix Plan

To resolve the issue of DashScope usage metrics always being zero in session transcripts, follow these steps:

  1. Update the buildAssistantMessageFromResponse function:
    • Replace direct field access with the existing normalizeUsage helper.
    • Ensure compatibility across all OpenAI-compatible providers.

Code Change:

// Fixed Implementation
const normalized = normalizeUsage(response.usage);

usage: buildUsageWithNoCost({
  input: normalized?.input ?? 0,
  output: normalized?.output ?? 0,
  totalTokens: normalized?.total ?? response.usage?.total_tokens ?? 0
});
  1. Verify the normalizeUsage function:
    • Confirm it correctly handles field name variants for different providers.
    • Ensure it returns the expected values for input, output, and total tokens.

Verification

To verify the fix worked:

  1. Reconfigure OpenClaw with the DashScope provider.
  2. Send a message through the agent.
  3. Inspect the generated session file for correct usage metrics.

Expected Output:

"usage": {
  "input": <non-zero value>,
  "output": <non-zero value>,
  "cacheRead": 0,
  "cacheWrite": 0,
  "totalTokens": <non-zero value>,
  "cost": {
    "input": <non-zero value>,
    "output": <non-zero value>,
    "cacheRead": 0,
    "cacheWrite": 0,
    "total": <non-zero value>
  }
}

Extra Tips

  • Review other provider integrations for similar issues.
  • Test with multiple OpenAI-compatible APIs to ensure broad compatibility.
  • Consider adding automated tests to prevent regressions in usage metric parsing.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING