From d6d585e20099744d5a67d71a4595d56b9ffc5765 Mon Sep 17 00:00:00 2001 From: Tyler Ford Date: Sun, 17 May 2026 20:11:06 -0400 Subject: [PATCH] fix: extract flat cached_tokens/reasoning_tokens from OpenAI-compatible usage objects (#2350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrated into release/v3.8.0 — flat cached_tokens/reasoning_tokens extraction for Xiaomi MiMo and similar providers --- open-sse/handlers/usageExtractor.ts | 6 +++-- open-sse/utils/usageTracking.ts | 6 +++-- tests/unit/usage-extractor.test.ts | 40 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/open-sse/handlers/usageExtractor.ts b/open-sse/handlers/usageExtractor.ts index e2e87983a9..92affd1c39 100644 --- a/open-sse/handlers/usageExtractor.ts +++ b/open-sse/handlers/usageExtractor.ts @@ -25,10 +25,12 @@ export function extractUsageFromResponse(responseBody, provider) { cached_tokens: responseBody.usage.prompt_tokens_details?.cached_tokens ?? responseBody.usage.input_tokens_details?.cached_tokens ?? - responseBody.usage.prompt_cache_hit_tokens, + responseBody.usage.prompt_cache_hit_tokens ?? + responseBody.usage.cached_tokens, reasoning_tokens: responseBody.usage.completion_tokens_details?.reasoning_tokens ?? - responseBody.usage.output_tokens_details?.reasoning_tokens, + responseBody.usage.output_tokens_details?.reasoning_tokens ?? + responseBody.usage.reasoning_tokens, }; } diff --git a/open-sse/utils/usageTracking.ts b/open-sse/utils/usageTracking.ts index 15e7d681d4..38913a2263 100644 --- a/open-sse/utils/usageTracking.ts +++ b/open-sse/utils/usageTracking.ts @@ -366,10 +366,12 @@ export function extractUsage(chunk) { cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? chunk.usage.input_tokens_details?.cached_tokens ?? - chunk.usage.prompt_cache_hit_tokens, + chunk.usage.prompt_cache_hit_tokens ?? + chunk.usage.cached_tokens, reasoning_tokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? - chunk.usage.output_tokens_details?.reasoning_tokens, + chunk.usage.output_tokens_details?.reasoning_tokens ?? + chunk.usage.reasoning_tokens, }); } diff --git a/tests/unit/usage-extractor.test.ts b/tests/unit/usage-extractor.test.ts index 4d64bf6eca..6c7bdf5273 100644 --- a/tests/unit/usage-extractor.test.ts +++ b/tests/unit/usage-extractor.test.ts @@ -292,3 +292,43 @@ test("extractUsage reads OpenAI streaming chunk with prompt_tokens_details", () assert.equal(usage.cached_tokens, 50); assert.equal(usage.reasoning_tokens, 20); }); + +// ── Flat field extraction tests (Xiaomi MiMo-style providers) ── + +test("extractUsageFromResponse reads flat cached_tokens and reasoning_tokens from OpenAI-compatible usage", () => { + const usage = extractUsageFromResponse( + { + usage: { + prompt_tokens: 258, + completion_tokens: 50, + total_tokens: 308, + cached_tokens: 192, + reasoning_tokens: 49, + }, + }, + "xiaomi-mimo" + ); + + assert.deepEqual(usage, { + prompt_tokens: 258, + completion_tokens: 50, + cached_tokens: 192, + reasoning_tokens: 49, + }); +}); + +test("extractUsage reads flat cached_tokens and reasoning_tokens from streaming chunk", () => { + const usage = extractUsage({ + choices: [{ delta: {}, finish_reason: "stop" }], + usage: { + prompt_tokens: 258, + completion_tokens: 50, + total_tokens: 308, + cached_tokens: 192, + reasoning_tokens: 49, + }, + }); + + assert.equal(usage.cached_tokens, 192); + assert.equal(usage.reasoning_tokens, 49); +});