fix(dashboard): 总 TOKEN 数统计补全 Anthropic 缓存 token#6233
Conversation
Why: - 数据看板 Models 视图的“总 TOKEN 数”来自 quota_data.token_used,其写入值为 PromptTokens + CompletionTokens。 - DeepSeek / Anthropic 等使用 /v1/messages(Anthropic 语义)的上游,input_tokens 仅表示 未命中缓存的 fresh token,cache_read_input_tokens / cache_creation_input_tokens 未计入, 导致看板数字远低于真实消耗(实测 7 月某日看板约 900 万 vs 上游面板 1.2 亿)。 - 相关上游诉求:QuantumNous#5069(数据看板增加缓存 token 统计)、QuantumNous#4395(Anthropic usage 语义)。 What: - model/log.go:RecordConsumeLogParams 新增 TotalTokens 字段;RecordConsumeLog 在写入 quota_data.token_used 时优先使用 TotalTokens,未提供时回退 PromptTokens + CompletionTokens, 保持旧行为兼容(不影响 RecordTaskBillingLog 等其它路径)。 - service/text_quota.go:新增 logTotalTokensFromSummary,按 UsageSemantic 计算带缓存的总 token: Anthropic 语义下补回 cache_read + cache_creation,OpenAI 语义下保持 PromptTokens + CompletionTokens (PromptTokens 已含缓存,避免重复计算)。PostTextConsumeQuota 将其通过 TotalTokens 传入日志层。 - service/text_quota_test.go:新增 TestLogTotalTokensFromSummary 覆盖两种语义与无缓存场景。 Impact: - 仅改变 quota_data.token_used 的统计口径,使其包含缓存 token;计费逻辑未改动 (summary.PromptTokens 仍按原语义参与计费)。 - 不改数据库:复用已有 token_used 列,无需迁移。 - 历史数据无法自动修正,修复后仅影响新写入的数据。
WalkthroughConsume-log quota accounting now accepts explicit total tokens. Text quota processing calculates totals from prompt, completion, and Claude cache tokens, then passes them through to quota exports with fallback behavior. ChangesConsume token accounting
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
service/text_quota_test.go (1)
743-772: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
assert.Equalfor non-fatal value checks.As per coding guidelines, new or substantially rewritten Go backend tests must use
testify/assertfor non-fatal value checks, reservingtestify/requirefor setup and fatal assertions. Usingassertensures that if one test case fails, the remaining assertions in the function will still execute and report their results.♻️ Proposed refactor
func TestLogTotalTokensFromSummary(t *testing.T) { // OpenAI 语义:PromptTokens 已包含缓存,总 token 不应再加缓存部分,避免重复计算。 openAISummary := textQuotaSummary{ PromptTokens: 1000, CompletionTokens: 200, CacheTokens: 800, CacheCreationTokens: 100, IsClaudeUsageSemantic: false, } - require.Equal(t, 1200, logTotalTokensFromSummary(openAISummary)) + assert.Equal(t, 1200, logTotalTokensFromSummary(openAISummary)) // Anthropic 语义:PromptTokens 仅表示 fresh token,需要把 cache_read/cache_creation 加回。 claudeSummary := textQuotaSummary{ PromptTokens: 100, CompletionTokens: 50, CacheTokens: 200, CacheCreationTokens: 30, IsClaudeUsageSemantic: true, } - require.Equal(t, 380, logTotalTokensFromSummary(claudeSummary)) + assert.Equal(t, 380, logTotalTokensFromSummary(claudeSummary)) // Anthropic 语义但无缓存:结果与 PromptTokens + CompletionTokens 相同。 claudeNoCacheSummary := textQuotaSummary{ PromptTokens: 100, CompletionTokens: 50, IsClaudeUsageSemantic: true, } - require.Equal(t, 150, logTotalTokensFromSummary(claudeNoCacheSummary)) + assert.Equal(t, 150, logTotalTokensFromSummary(claudeNoCacheSummary)) }(Note: Please ensure
github.com/stretchr/testify/assertis imported in this file.)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/text_quota_test.go` around lines 743 - 772, Update TestLogTotalTokensFromSummary to use assert.Equal for all three non-fatal token-value checks, and ensure the testify/assert package is imported. Keep require reserved for setup or assertions whose failure must stop the test.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@service/text_quota_test.go`:
- Around line 743-772: Update TestLogTotalTokensFromSummary to use assert.Equal
for all three non-fatal token-value checks, and ensure the testify/assert
package is imported. Keep require reserved for setup or assertions whose failure
must stop the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f4319ec3-1c75-4bb4-a717-df8dbae01687
📒 Files selected for processing (3)
model/log.goservice/text_quota.goservice/text_quota_test.go
📝 变更描述 / Description
数据看板 Models 视图的“总 TOKEN 数”取自
quota_data.token_used,原写入值为prompt_tokens + completion_tokens。对于走
/v1/messages(Anthropic 语义)的上游(如 DeepSeek、Anthropic 官方),input_tokens仅表示未命中缓存的 fresh token,cache_read_input_tokens/cache_creation_input_tokens未被计入,导致看板数字系统性偏低(实测某日看板约 900 万,而上游面板约 1.2 亿,主要差异来自大量命中的上下文缓存)。本 PR 做最小修复:在 Anthropic 语义下把缓存 token 加回到
token_used,让看板“总 TOKEN 数”反映真实消耗。为什么这样改生效:
service/text_quota.go新增logTotalTokensFromSummary,按UsageSemantic判断:PromptTokens + CompletionTokens + CacheTokens + CacheCreationTokensPromptTokens + CompletionTokens(prompt_tokens已含缓存,不再重复加,避免双计)RecordConsumeLogParams.TotalTokens传给日志层,RecordConsumeLog写入quota_data.token_used时优先使用,未提供时回退原逻辑,保持其它调用路径兼容。summary.PromptTokens仍按原语义参与计费,本次只调整看板统计口径。🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
token_used,不新增列、不改数据库;如需单独展示缓存列可在此基础上继续)/v1/messages下 input_tokens 与缓存 token 语义)✅ 提交前检查项 / Checklist
quota_data.token_used统计口径变化。go test ./service ./model通过(见下方运行证明)。📸 运行证明 / Proof of Work
新增测试
TestLogTotalTokensFromSummary覆盖两种语义与无缓存场景:model包全量通过:说明:
service包中TestObserveChannelAffinityUsageCacheByRelayFormat_*2 个测试在本分支与 origin/main 上均失败(既有 flaky/全局计数器污染,与本次改动无关,本 PR 未触碰 channel_affinity 相关代码)。Summary by CodeRabbit
Bug Fixes
Tests