Skip to content

Commit 87c1a11

Browse files
committed
fix: corrected context compression visibility for cached providers
1 parent a23adf3 commit 87c1a11

1 file changed

Lines changed: 12 additions & 3 deletions

File tree

app/src/components/RunOutput/Compression.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,23 @@ export function isExplicitCompression(event: LangfuseEvent): boolean {
2828
return typeof meta === 'string' && /"task"\s*:\s*"compression"/i.test(meta);
2929
}
3030

31-
/** Input-token count reported by a generation event, or null. */
31+
/**
32+
* True prompt-token count for a generation event, or null. Prompt-caching providers (DeepSeek,
33+
* Anthropic, …) report `input` as the cache-MISS tokens only and put the cached prefix in a
34+
* separate field — so the real context size is `input + cache_read + cache_creation`. Summing them
35+
* is essential: without it a warm cache hit (e.g. input 67k → 3k while 69k is cached) looks like a
36+
* context drop and is misread as a compression. These cache fields are reported *separately from*
37+
* `input` (not a subset of it), so the sum never double-counts.
38+
*/
3239
export function inputTokens(event: LangfuseEvent): number | null {
3340
if ((event.body['langfuse.observation.type'] as string | undefined) !== 'generation') return null;
3441
const raw = event.body['langfuse.observation.usage_details'] as string | undefined;
3542
if (!raw) return null;
3643
try {
37-
const usage = JSON.parse(raw) as { input?: number };
38-
return typeof usage.input === 'number' ? usage.input : null;
44+
const usage = JSON.parse(raw) as Record<string, unknown>;
45+
if (typeof usage.input !== 'number') return null;
46+
const num = (k: string) => (typeof usage[k] === 'number' ? (usage[k] as number) : 0);
47+
return usage.input + num('cache_read_input_tokens') + num('cache_creation_input_tokens');
3948
} catch {
4049
return null;
4150
}

0 commit comments

Comments
 (0)