mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 22:52:19 +03:00
fix(sse): include prompt cache usage fields on message_stop fallback path (#10545)
Mirrors the existing prompt_tokens_details cache-field mapping from the message_delta finish path into the message_stop fallback, so OpenAI-compatible clients see cache_read/cache_creation counters when the finish signal arrives without usage on the same event. Fixes #10535. Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 2 files): - 16/16 focused tests pass (translator-resp-claude-to-openai.test.ts), including the new regression test for this exact fallback path. - check-file-size, check-changelog-integrity: OK. - typecheck:core: clean. - check-complexity / check-cognitive-complexity: OK, both under baseline. Co-authored-by: NahuSaruf <NahuSaruf@users.noreply.github.com>
This commit is contained in:
@@ -332,6 +332,8 @@ export function claudeToOpenAIResponse(chunk, state) {
|
||||
if (!state.finishReasonSent) {
|
||||
const finishReason =
|
||||
state.finishReason || (state.toolCalls?.size > 0 ? "tool_calls" : "stop");
|
||||
const cachedTokens = state.usage?.cache_read_input_tokens || 0;
|
||||
const cacheCreationTokens = state.usage?.cache_creation_input_tokens || 0;
|
||||
const usageObj =
|
||||
state.usage && typeof state.usage === "object"
|
||||
? {
|
||||
@@ -347,6 +349,16 @@ export function claudeToOpenAIResponse(chunk, state) {
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(cachedTokens > 0 || cacheCreationTokens > 0
|
||||
? {
|
||||
prompt_tokens_details: {
|
||||
...(cachedTokens > 0 ? { cached_tokens: cachedTokens } : {}),
|
||||
...(cacheCreationTokens > 0
|
||||
? { cache_creation_tokens: cacheCreationTokens }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {};
|
||||
|
||||
@@ -403,6 +403,40 @@ test("Claude stream: message_stop falls back to tool_calls when tool use already
|
||||
assert.equal(result[0].choices[0].finish_reason, "tool_calls");
|
||||
});
|
||||
|
||||
test("Claude stream: message_stop includes prompt_tokens_details when usage arrived on an earlier message_delta without stop_reason (#10535)", () => {
|
||||
const state = createState();
|
||||
claudeToOpenAIResponse(
|
||||
{ type: "message_start", message: { id: "msg1", model: "claude-sonnet-4-6" } },
|
||||
state
|
||||
);
|
||||
|
||||
// Usage lands on a message_delta that carries no stop_reason (e.g. an
|
||||
// upstream that reports usage and the finish signal in separate events),
|
||||
// so the finalChunk branch in the message_delta case never runs and
|
||||
// finishReasonSent stays false.
|
||||
const deltaResult = claudeToOpenAIResponse(
|
||||
{
|
||||
type: "message_delta",
|
||||
delta: {},
|
||||
usage: {
|
||||
input_tokens: 8,
|
||||
output_tokens: 5,
|
||||
cache_read_input_tokens: 2000,
|
||||
cache_creation_input_tokens: 0,
|
||||
},
|
||||
},
|
||||
state
|
||||
);
|
||||
assert.equal(deltaResult, null);
|
||||
|
||||
const result = claudeToOpenAIResponse({ type: "message_stop" }, state);
|
||||
|
||||
assert.equal(result[0].usage.prompt_tokens, 2008);
|
||||
assert.equal(result[0].usage.completion_tokens, 5);
|
||||
assert.equal(result[0].usage.total_tokens, 2013);
|
||||
assert.equal(result[0].usage.prompt_tokens_details.cached_tokens, 2000);
|
||||
});
|
||||
|
||||
test("Claude stream: unsupported events return null", () => {
|
||||
assert.equal(claudeToOpenAIResponse({ type: "error" }, createState()), null);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user