diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts index 5c938ff693..1f1b80f35c 100644 --- a/packages/opencode/src/session/session.ts +++ b/packages/opencode/src/session/session.ts @@ -356,8 +356,11 @@ export const getUsage = (input: { model: Provider.Model; usage: LanguageModelUsa return value } const inputTokens = safe(input.usage.inputTokens ?? 0) - const outputTokens = safe(input.usage.outputTokens ?? 0) - const reasoningTokens = safe(input.usage.outputTokenDetails?.reasoningTokens ?? input.usage.reasoningTokens ?? 0) + const outputTokens = Math.max(0, safe(input.usage.outputTokens ?? 0)) + const reasoningTokens = Math.min( + Math.max(0, safe(input.usage.outputTokenDetails?.reasoningTokens ?? input.usage.reasoningTokens ?? 0)), + outputTokens, + ) const cacheReadInputTokens = safe( input.usage.inputTokenDetails?.cacheReadTokens ?? input.usage.cachedInputTokens ?? 0, @@ -387,7 +390,7 @@ export const getUsage = (input: { model: Provider.Model; usage: LanguageModelUsa const tokens = { total, input: adjustedInputTokens, - output: safe(outputTokens - reasoningTokens), + output: outputTokens - reasoningTokens, reasoning: reasoningTokens, cache: { write: cacheWriteInputTokens, diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index cde9c1397f..c66e981b45 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -2011,6 +2011,30 @@ describe("SessionNs.getUsage", () => { expect(result.tokens.total).toBe(1500) }) + test("clamps reasoning tokens to completion tokens", () => { + const model = createModel({ context: 100_000, output: 32_000 }) + const result = SessionNs.getUsage({ + model, + usage: { + inputTokens: 1000, + outputTokens: 45, + totalTokens: 1045, + inputTokenDetails: { + noCacheTokens: undefined, + cacheReadTokens: undefined, + cacheWriteTokens: undefined, + }, + outputTokenDetails: { + textTokens: undefined, + reasoningTokens: 47, + }, + }, + }) + + expect(result.tokens.output).toBe(0) + expect(result.tokens.reasoning).toBe(45) + }) + test("does not double count reasoning tokens in cost", () => { const model = createModel({ context: 100_000,