mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-19 13:23:50 +03:00
fix(translator): stop double-counting cached tokens in Gemini to Claude usage (#12863)
* fix(translator): stop double-counting cached tokens in Gemini→Claude usage * docs(changelog): add fragment for gemini-to-claude cached input tokens fix
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- **fix(translator):** Gemini to Claude usage no longer double-counts the cached prompt prefix — `input_tokens` now excludes `cache_read_input_tokens`, matching the Anthropic Messages semantics ([#12863](https://github.com/diegosouzapw/OmniRoute/pull/12863))
|
||||
@@ -397,7 +397,7 @@ export function geminiToClaudeResponse(chunk, state) {
|
||||
typeof usageMeta.cachedContentTokenCount === "number" ? usageMeta.cachedContentTokenCount : 0;
|
||||
|
||||
state.usage = {
|
||||
input_tokens: inputTokens,
|
||||
input_tokens: Math.max(0, inputTokens - cachedTokens),
|
||||
output_tokens: candidatesTokens + thoughtsTokens,
|
||||
};
|
||||
if (cachedTokens > 0) {
|
||||
|
||||
@@ -107,7 +107,7 @@ test("Gemini -> Claude stream: functionCall becomes tool_use and MAX_TOKENS maps
|
||||
assert.equal(result[2].delta.partial_json, JSON.stringify({ path: "/tmp/a" }));
|
||||
assert.equal(result[3].type, "content_block_stop");
|
||||
assert.equal(result[4].delta.stop_reason, "tool_use");
|
||||
assert.equal(result[4].usage.input_tokens, 5);
|
||||
assert.equal(result[4].usage.input_tokens, 4);
|
||||
assert.equal(result[4].usage.output_tokens, 5);
|
||||
assert.equal(result[4].usage.cache_read_input_tokens, 1);
|
||||
assert.equal(result[5].type, "message_stop");
|
||||
@@ -190,3 +190,52 @@ test("Gemini -> Claude stream: response wrapper is supported and promptFeedback-
|
||||
assert.equal(wrapped[2].delta.text, "wrapped");
|
||||
assert.equal(geminiToClaudeResponse({ promptFeedback: { blockReason: "SAFETY" } }, {}), null);
|
||||
});
|
||||
|
||||
test("Gemini -> Claude stream: input_tokens excludes cached tokens", () => {
|
||||
const result = geminiToClaudeResponse(
|
||||
{
|
||||
responseId: "resp-6",
|
||||
modelVersion: "gemini-2.5-pro",
|
||||
candidates: [
|
||||
{
|
||||
content: { parts: [{ text: "done" }] },
|
||||
finishReason: "STOP",
|
||||
},
|
||||
],
|
||||
usageMetadata: {
|
||||
promptTokenCount: 100,
|
||||
candidatesTokenCount: 7,
|
||||
cachedContentTokenCount: 90,
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const delta = result.find((event) => event.type === "message_delta");
|
||||
assert.equal(delta.usage.input_tokens, 10);
|
||||
assert.equal(delta.usage.cache_read_input_tokens, 90);
|
||||
});
|
||||
|
||||
test("Gemini -> Claude stream: input_tokens equals promptTokenCount when nothing is cached", () => {
|
||||
const result = geminiToClaudeResponse(
|
||||
{
|
||||
responseId: "resp-7",
|
||||
modelVersion: "gemini-2.5-pro",
|
||||
candidates: [
|
||||
{
|
||||
content: { parts: [{ text: "done" }] },
|
||||
finishReason: "STOP",
|
||||
},
|
||||
],
|
||||
usageMetadata: {
|
||||
promptTokenCount: 100,
|
||||
candidatesTokenCount: 7,
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const delta = result.find((event) => event.type === "message_delta");
|
||||
assert.equal(delta.usage.input_tokens, 100);
|
||||
assert.equal(delta.usage.cache_read_input_tokens, undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user