mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Merge pull request #909 from christopher-s/fix/responses-api-flush-total-tokens
fix(translator): emit response.completed with total_tokens for Responses API clients
This commit is contained in:
@@ -267,6 +267,15 @@ export function translateResponse(targetFormat, sourceFormat, chunk, state) {
|
||||
finalResults.push(...(Array.isArray(converted) ? converted : [converted]));
|
||||
}
|
||||
}
|
||||
// Flush: pass null to source-format translator even when Step 1 produced no output.
|
||||
// This is critical for formats like openai-responses that emit terminal events
|
||||
// (e.g., response.completed with total_tokens) in their flush handler.
|
||||
if (chunk === null && results.length === 0) {
|
||||
const converted = fromOpenAI(null, state);
|
||||
if (converted) {
|
||||
finalResults.push(...(Array.isArray(converted) ? converted : [converted]));
|
||||
}
|
||||
}
|
||||
results = finalResults;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,24 @@ export function openaiToOpenAIResponsesResponse(chunk, state) {
|
||||
return flushEvents(state);
|
||||
}
|
||||
|
||||
if (!chunk.choices?.length) {
|
||||
// Capture usage from usage-only chunks (stream_options.include_usage)
|
||||
if (chunk.usage) {
|
||||
state.usage = chunk.usage;
|
||||
// Capture usage from any chunk that carries it (usage-only chunks OR final chunks with finish_reason)
|
||||
// Normalize Chat Completions format (prompt_tokens/completion_tokens) to Responses API format
|
||||
// (input_tokens/output_tokens) so response.completed always has the fields Codex expects.
|
||||
if (chunk.usage) {
|
||||
const u = chunk.usage;
|
||||
const input_tokens = u.input_tokens ?? u.prompt_tokens ?? 0;
|
||||
const output_tokens = u.output_tokens ?? u.completion_tokens ?? 0;
|
||||
state.usage = {
|
||||
input_tokens,
|
||||
output_tokens,
|
||||
total_tokens: u.total_tokens ?? input_tokens + output_tokens,
|
||||
};
|
||||
if (u.prompt_tokens_details?.cached_tokens) {
|
||||
state.usage.input_tokens_details = { cached_tokens: u.prompt_tokens_details.cached_tokens };
|
||||
}
|
||||
}
|
||||
|
||||
if (!chunk.choices?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -369,7 +369,9 @@ test("Chat→Responses streaming: usage-only chunk is captured (not dropped)", (
|
||||
const completedEvent = finishEvents.find((e) => e.event === "response.completed");
|
||||
assert.ok(completedEvent, "should have completed event");
|
||||
assert.ok(completedEvent.data.response.usage, "completed event should include usage");
|
||||
assert.equal(completedEvent.data.response.usage.prompt_tokens, 10);
|
||||
assert.equal(completedEvent.data.response.usage.input_tokens, 10);
|
||||
assert.equal(completedEvent.data.response.usage.output_tokens, 5);
|
||||
assert.equal(completedEvent.data.response.usage.total_tokens, 15);
|
||||
});
|
||||
|
||||
test("Chat→Responses streaming: completed event includes accumulated output", () => {
|
||||
|
||||
Reference in New Issue
Block a user