Skip to content

[billing] 真实用量统计与计费分析#36

Merged
atoncooper merged 5 commits into
mainfrom
feat/billing-usage-tracking
Jul 22, 2026
Merged

[billing] 真实用量统计与计费分析#36
atoncooper merged 5 commits into
mainfrom
feat/billing-usage-tracking

Conversation

@atoncooper

@atoncooper atoncooper commented Jul 22, 2026

Copy link
Copy Markdown
Owner

背景

用量统计长期失真:对话产生的 token 与费用始终为 0,且 provider/credential 归属错误(硬编码为 openai / 系统默认)。本 PR 重写用量统计链路,使计费面板能真实反映各凭证、模型、服务商的 token 用量与估算费用。

根因与修复

根因 修复
UsageTrackingCallback.on_llm_end 跨线程写入失败(no current event loop in thread 'asyncio_0' 改用 run_coroutine_threadsafe 投递回主 loop
callbacks 被塞进 agent state(而非 run_config),on_llm_end 从不触发 agent_run → dispatch → invoke → _invoke_locked 全链路将 callbacks 作为显式参数进入 run_config
流式响应不返回 token(llm_output=None harness LLM 与 build_llm 启用 stream_usage=True
provider 硬编码 openai、credential_id 硬编码 None build_llm(uid) 读取真实 credential/provider/model
价格表无 qwen3-maxinfer_provider 不识别 dashscope 补充 qwen3/qwen2.5/moonshot 价格,infer_provider 识别 dashscope/aliyun,跨 provider 模型名兜底
路由 LLM、Memory Agent 调用未统计 route() 接收 uid;delegate 工具经 invoke_reentrant 传 callback
Quiz 用量归属系统默认 key 出题/批改改用 build_llm(uid)

新增能力

后端

  • GET /billing/timeseries — 按天聚合用量趋势(无数据日期补零)
  • GET /billing/by-model — 模型维度明细
  • GET /billing/summary 增强:输入/输出 token 分项、均次费用、by_model
  • CredentialUsage 表新增 cost_estimate 列(自动迁移)

前端

  • 汇总卡片:输入/输出 token 分项与占比、均次费用
  • 用量趋势图(双轴:Token 面积线 + 费用虚线)
  • 模型明细表(模型/服务商/输入/输出/调用次数/费用)
  • 凭证明细显示费用

基础设施

  • pricing.py 价格表(openai/anthropic/deepseek/dashscope/moonshot)
  • buffered_usage_writer.py 重写为真缓冲批量写入(Queue + 后台 flush loop)
  • token_count.py 从 AI 消息提取 token(兜底)

测试

新增/更新 4 个测试文件,101 项测试全部通过:

  • 价格表查找与费用估算(含跨 provider 兜底)
  • 缓冲批量写入(批量/定时/shutdown 排空)
  • usage tracker 跨线程调度、费用计算、attach_usage_tracking
  • repository 聚合(by_model / timeseries / summary 分项)

提交结构

  1. [billing] feat: 用量统计基础设施(价格表/缓冲写入/追踪回调/token 提取)
  2. [billing] fix: 聊天与 Quiz 链路真实 credential 归属与跨线程用量写入
  3. [billing] feat: 完善计费统计 - 趋势/模型维度/输入输出分项
  4. [frontend] feat: 用量趋势图与模型明细表
  5. [billing] test: 补充用量追踪/写入/计费/聚合测试

Test Plan

  • ruff check 全部通过
  • TypeScript 类型检查通过
  • 101 项单元测试通过
  • 手动验证:配置 DeepSeek/OpenAI credential 对话后,billing 面板显示正确 provider/credential/费用
  • 手动验证:流式对话后 logs/app.log 出现 [USAGE_TRACKER] on_llm_end fired ... total=>0[CHAT_ORCH] stream done: tokens=...

🤖 Generated with Claude Code

- pricing.py:新增 LLM 价格表,覆盖 openai/anthropic/deepscope/dashscope/moonshot
  常见模型,支持跨 provider 模型名兜底查找,估算 cost_estimate
- buffered_usage_writer.py:重写为真缓冲批量写入(asyncio.Queue + 后台
  flush loop,定时/定量批量 INSERT,shutdown 排空防丢数据)
- usage_tracker.py:UsageTrackingCallback 增加费用估算与 in-process token
  累加;on_llm_end 用 run_coroutine_threadsafe 修复工作线程跨 loop 写入
- token_count.py:从 AI 消息 response_metadata/usage_metadata 提取 token
- chat/llm.py:build_llm 自动挂载 UsageTrackingCallback 并启用 stream_usage
- models.py/database.py/system.sql:CredentialUsage 新增 cost_estimate 列

影响范围:用量统计写入链路,不改业务接口契约
修复用量统计一直为 0 / 归属错误的多个根因:
- orchestrator:移除硬编码 credential_id=None / provider="openai",改用
  build_llm(uid) 读取真实 credential/provider/model;新增 _make_usage_callback
- dispatcher/agent_run:callbacks 作为显式参数进入 run_config(此前被塞进
  agent state 导致 on_llm_end 从不触发)
- harness/app.py + lifecycle/manager.py:dispatch/invoke/_invoke_locked 全链路
  透传 callbacks 到 run_config
- harness/orchestrator.py:route() 接收 uid,路由 LLM 调用也记录用量
- delegate.py:Memory Agent 子调用经 invoke_reentrant 传入 usage callback
- agent_sse.py:监听 root on_chain_end 提取 token(兜底)
- main.py:harness LLM 启用 stream_usage=True,流式响应才返回 token 用量
- quiz_generator/quiz_grader:改用 build_llm(uid),用量归属到用户真实凭据

影响范围:chat/quiz 用量统计链路,不改对外接口
- usage_repository.py:新增 get_by_model(模型维度聚合)、get_timeseries
  (按天聚合,无数据日期补零,兼容 SQLite/MySQL date 类型);get_summary
  增加 total_prompt/completion_tokens、avg_cost_per_call、by_model
- credentials.py:ProviderUsage/CredentialUsageItem 增加 prompt/completion
  分项;新增 ModelUsage、UsageTimeseriesPoint;UsageSummary 增加分项与均次费用
- routers/billing.py:新增 GET /billing/by-model、GET /billing/timeseries

影响范围:计费查询接口(新增字段与端点,向后兼容)
- billing.tsx:汇总卡片增加输入/输出 token 分项与均次费用;新增用量趋势图
  (双轴:Token 面积线 + 费用虚线)、模型明细表(输入/输出/调用次数/费用)
- api.ts:新增 ModelUsage/UsageTimeseriesPoint 类型与 billingApi.getByModel/
  getTimeseries;UsageSummary 补充分项字段

影响范围:计费面板可视化
- test_pricing.py(新增):价格表查找、费用估算、跨 provider 兜底、大小写
- test_usage_writer.py:重写为真缓冲批量写入测试(批量触发/定时/shutdown 排空)
- test_usage_tracker.py:费用计算、零 token 仍记录 api_calls、attach_usage_tracking
- test_usage_repository.py:by_model/timeseries/summary 聚合测试,修正旧 session_id 用例

影响范围:仅测试
@atoncooper
atoncooper merged commit df5655e into main Jul 22, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant