Count Groq prompt cache hits instead of reporting every request as a miss - #161
Merged
Conversation
…miss Groq returns prompt_tokens_details.cached_tokens, but @ai-sdk/groq@4.0.0 parses the field in its response schema and never assigns it, so the usage object always came back as noCacheTokens = the full prompt. extractCachedTokens() in src/ai/provider.ts reads inputTokenDetails.cacheReadTokens, which was therefore always undefined, and the AI SDK OTEL exporter emitted no cached-token attribute — so Groq spans in Langfuse carried only input_tokens and output_tokens while OpenRouter spans carried the cache keys. Cache hit rate read as zero for every Groq call whether or not the prompt was actually reused. 4.0.34 adds convertGroqUsage(), which maps cached_tokens to inputTokens.cacheRead. package.json already allowed it under ^4.0; only the lockfile pinned 4.0.0. Verified with a fetch interceptor around generateText: 12 identical 28k-token prompts gave 3 raw cache hits and 3 SDK-reported hits, exact match. An end-to-end run through the same LangfuseSpanProcessor wiring the app uses produced 5 raw hits and 5 generations carrying cache_read.input_tokens in Langfuse. The lockfile also picks up the chalk entry that was missing from its workspace manifest block; chalk is already declared in package.json on main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014xHSmzm2ZczcH2ox3WQAC3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Every Groq request was recorded as a full-price cache miss. Across the last ~1200 generations in Langfuse, Groq accounted for 95% of all input tokens (11.7M) with exactly zero cached tokens reported on 537 calls, pinning the overall cache hit rate at 2.26%.
This was a reporting bug, not a prompt problem. Diffing consecutive tester calls in Langfuse showed a 100% stable prefix (11312/11313, 17706/17707, 38851/38852 shared chars) — the prompts were perfectly cacheable the whole time.
Cause
Groq returns
prompt_tokens_details.cached_tokens, but@ai-sdk/groq@4.0.0parses that field in its Zod response schema (dist/index.js:729,768) and never assigns it. Usage always came back asnoCacheTokens= the full prompt.Downstream,
extractCachedTokens()insrc/ai/provider.ts:52readsinputTokenDetails.cacheReadTokens/cachedInputTokens/raw— all undefined — and returns 0. The AI SDK OTEL exporter emitted no cached-token attribute, so Groq spans in Langfuse carried onlyinput_tokensandoutput_tokens, while OpenRouter spans carried the cache keys.@ai-sdk/openaidoes this mapping correctly at itsdist/index.js:203; groq simply lacked the equivalent.Fix
Bump to 4.0.34, which adds
convertGroqUsage()mappingcached_tokens → inputTokens.cacheRead.package.jsonalready allowed it under^4.0; only the lockfile pinned 4.0.0. No source changes needed — the existingextractCachedTokens()reads exactly the key the new version populates.Verification
Fetch interceptor around
generateText, 12 identical 28k-token prompts — raw hits 3/12, SDK-reported 3/12, exact match:End-to-end through the same
LangfuseSpanProcessor+NodeSDK+registerTelemetrywiring the app uses, 14 calls → 5 raw hits → 5 generations in Langfuse carryingcache_read.input_tokens: 27904. First time Groq spans have carried that key.Tests:
bun run test:unit1126 pass / 0 fail —bun test tests/integration84 pass / 0 fail.Notes
chalkentry missing from its workspace manifest block.chalkis already declared inpackage.jsononmain— this just syncs the record.🤖 Generated with Claude Code
https://claude.ai/code/session_014xHSmzm2ZczcH2ox3WQAC3