From 7c1c1e6324e80842a964f2938814b65a1200f2af Mon Sep 17 00:00:00 2001 From: Melih Mucuk Date: Wed, 12 Nov 2025 00:01:08 +0200 Subject: [PATCH 1/2] Ensure accurate usage & billing for custom model aliases and cached/reasoning tokens --- packages/opencode/src/provider/provider.ts | 3 ++- packages/opencode/src/session/index.ts | 26 +++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index ed0c1dace9d1..14cc0433df99 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -289,7 +289,8 @@ export namespace Provider { } for (const [modelID, model] of Object.entries(provider.models ?? {})) { - const existing = parsed.models[modelID] + let existing = parsed.models[modelID] + if (!existing && model.id) existing = parsed.models[model.id] const parsedModel: ModelsDev.Model = { id: modelID, name: model.name ?? existing?.name ?? modelID, diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index d0bdfb9e1e2c..eb835b8130b6 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -378,8 +378,13 @@ export namespace Session { metadata: z.custom().optional(), }), (input) => { + const totalInput = input.usage.inputTokens ?? 0 + const cachedRead = input.usage.cachedInputTokens ?? 0 + const uncachedInput = Math.max(0, totalInput - cachedRead) + const tokens = { - input: input.usage.inputTokens ?? 0, + // input should count only uncached input tokens + input: uncachedInput, output: input.usage.outputTokens ?? 0, reasoning: input.usage?.reasoningTokens ?? 0, cache: { @@ -387,16 +392,21 @@ export namespace Session { // @ts-expect-error input.metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ?? 0) as number, - read: input.usage.cachedInputTokens ?? 0, + read: cachedRead, }, } + + const cost = new Decimal(0) + .add(new Decimal(tokens.input).mul(input.model.cost?.input ?? 0).div(1_000_000)) + .add(new Decimal(tokens.output).mul(input.model.cost?.output ?? 0).div(1_000_000)) + // charge reasoning tokens at the same rate as output tokens + .add(new Decimal(tokens.reasoning).mul(input.model.cost?.output ?? 0).div(1_000_000)) + .add(new Decimal(tokens.cache.read).mul(input.model.cost?.cache_read ?? 0).div(1_000_000)) + .add(new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000)) + .toNumber() + return { - cost: new Decimal(0) - .add(new Decimal(tokens.input).mul(input.model.cost?.input ?? 0).div(1_000_000)) - .add(new Decimal(tokens.output).mul(input.model.cost?.output ?? 0).div(1_000_000)) - .add(new Decimal(tokens.cache.read).mul(input.model.cost?.cache_read ?? 0).div(1_000_000)) - .add(new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000)) - .toNumber(), + cost, tokens, } }, From 2c616e4e7ec5752349fc87c5d367f49c6e1c3903 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 12 Nov 2025 12:40:07 -0600 Subject: [PATCH 2/2] revert --- packages/opencode/src/provider/provider.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 14cc0433df99..e30576bf7ccf 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -289,8 +289,7 @@ export namespace Provider { } for (const [modelID, model] of Object.entries(provider.models ?? {})) { - let existing = parsed.models[modelID] - if (!existing && model.id) existing = parsed.models[model.id] + const existing = parsed.models[model.id ?? modelID] const parsedModel: ModelsDev.Model = { id: modelID, name: model.name ?? existing?.name ?? modelID,