Skip to content

fix(openai-api): report real token usage in chat completions - #8

Open
fancyboi999 wants to merge 1 commit into
MemTensor:v1.0.5from
fancyboi999:fix/openai-api-usage
Open

fix(openai-api): report real token usage in chat completions#8
fancyboi999 wants to merge 1 commit into
MemTensor:v1.0.5from
fancyboi999:fix/openai-api-usage

Conversation

@fancyboi999

@fancyboi999 fancyboi999 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

问题

memmy serve 的 OpenAI 兼容 API /v1/chat/completions 返回的 usage 恒为 {"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}。把 memmy 当 OpenAI 端点接入其他工具时计量完全失真。

根因

src/entrypoints/openai-like-api/server.tschatCompletionResponse() 硬编码了三个 0。真实 usage 一直存在:AgentRunner.run() 跨工具调用迭代累计 usage,AgentLoop.runAgentLoop() 也做了归一化(lastUsage),但从未随 turn 结果传到 API 入口。

修复

沿既有的 per-turn 对象传递 usage,不依赖共享的 lastUsage 字段,避免并发 turn 竞争:

  • runAgentLoop()v1.0.5 已有的 errorCategory 之后追加本 turn 的归一化 usage
  • TurnContext.usagestateRun() 填充,assembleOutbound() 再挂到 OutboundMessage.metadata.usage
  • 非流式 API 从 metadata 读取真实 usage;空响应重试路径将两次真实 LLM 调用的 usage 求和
  • 流式 API 支持 stream_options.include_usage,设置时在 [DONE] 前追加 choices: [] 的 usage 尾块;未设置时行为不变

冲突处理

本分支已 rebase 到当前 v1.0.5。唯一冲突来自 runAgentLoop() 返回元组:base 新增了 errorCategory,本 PR 新增了 usage。最终同时保留两者,顺序为 errorCategory 第 7 项、usage 第 8 项,避免改变 base 现有调用方语义。

验证

  • focused tests:3 个文件,42 passed / 0 failed
  • npm run typecheck:通过
  • npm run build:通过
  • memmy-agent 全量测试:4633 passed / 3 skipped / 1 failed
  • 真实服务 + curl(OpenAI-compatible BYOK gateway):
    • 非流式:usage={"prompt_tokens":6304,"completion_tokens":5,"total_tokens":6309},总数校验通过
    • 流式带 include_usage: true:finish chunk 之后、[DONE] 之前收到 choices: [] 的 usage chunk;6341+5=6346
    • 流式未设置 include_usage:0 个 usage chunk,保留兼容行为

未覆盖说明

  • 空响应重试的 usage 求和仅以测试验证,无法稳定诱导真实 LLM 返回空内容
  • processSystemMessage()(cron/系统通道,不经 OpenAI API)未接 usage,属于有意边界
  • usage 使用 per-call 对象传递,未增加专门的并发竞态测试
  • GitHub 当前未为该 PR 返回 CI checks;以上为本地和真实运行验证结果

@syzsunshine219
syzsunshine219 requested a review from ZongYue99 July 20, 2026 08:27
@fancyboi999

Copy link
Copy Markdown
Contributor Author

@syzsunshine219 maintainer follow-up: this PR remains clean and mergeable, and current main still hard-codes both OpenAI-compatible chat-completion usage counters to zero. This branch reports the token usage collected by the agent path and includes focused regression coverage.

No conflict rebuild is needed here. Could you review it when convenient?

@ZongYue99
ZongYue99 requested a review from Wang-Daoji August 4, 2026 08:31
@ZongYue99
ZongYue99 changed the base branch from main to v1.0.5 August 4, 2026 08:38
The OpenAI-compatible /v1/chat/completions endpoint always returned
usage: {prompt_tokens: 0, completion_tokens: 0, total_tokens: 0},
because chatCompletionResponse() hardcoded zeros instead of reading
the usage the agent runtime already tracks (AgentRunner accumulates
per-iteration usage in runner.ts, and AgentLoop.runAgentLoop already
normalizes it into `lastUsage`, but that value never left the loop).

Thread the turn's accumulated usage through the existing pipeline
instead of introducing a new channel: runAgentLoop now returns it
alongside its other turn outputs, TurnContext carries it, and
assembleOutbound attaches it to OutboundMessage.metadata.usage. The
API entrypoint reads it from there, sums it across the empty-response
retry path, and reports it in the JSON response. Streaming responses
now also honor stream_options.include_usage by emitting a trailing
usage-only chunk (choices: []), matching the OpenAI SSE convention.
@fancyboi999
fancyboi999 force-pushed the fix/openai-api-usage branch from 0ec980c to 6c8cc1b Compare August 4, 2026 09:08
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