Skip to content

Count Groq prompt cache hits instead of reporting every request as a miss - #161

Merged
DavertMik merged 1 commit into
mainfrom
groq-cache-usage
Aug 29, 2026
Merged

Count Groq prompt cache hits instead of reporting every request as a miss#161
DavertMik merged 1 commit into
mainfrom
groq-cache-usage

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

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%.

provider model calls input tokens cached hit
groq openai/gpt-oss-120b 257 6,445,271 0 0.0%
groq openai/gpt-oss-20b 280 5,320,826 0 0.0%
openrouter openai/gpt-5.6-luna 142 615,048 279,317 45.4%

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.0 parses that field in its Zod response schema (dist/index.js:729,768) and never assigns it. Usage always came back as noCacheTokens = the full prompt.

Downstream, extractCachedTokens() in src/ai/provider.ts:52 reads inputTokenDetails.cacheReadTokens / cachedInputTokens / raw — all undefined — and returns 0. 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.

@ai-sdk/openai does this mapping correctly at its dist/index.js:203; groq simply lacked the equivalent.

Fix

Bump to 4.0.34, which adds convertGroqUsage() mapping cached_tokens → inputTokens.cacheRead. package.json already allowed it under ^4.0; only the lockfile pinned 4.0.0. No source changes needed — the existing extractCachedTokens() 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:

raw.cached_tokens=27904  →  {"noCacheTokens":175,"cacheReadTokens":27904}

End-to-end through the same LangfuseSpanProcessor + NodeSDK + registerTelemetry wiring the app uses, 14 calls → 5 raw hits → 5 generations in Langfuse carrying cache_read.input_tokens: 27904. First time Groq spans have carried that key.

Tests: bun run test:unit 1126 pass / 0 fail — bun test tests/integration 84 pass / 0 fail.

Notes

  • Groq's cache is per-node, so the hit rate stays uneven by nature: probes suggest roughly a quarter of calls hit, but each hit covers ~99% of the prompt. This PR makes the number visible; it does not raise it.
  • The lockfile also picks up the chalk entry missing from its workspace manifest block. chalk is already declared in package.json on main — this just syncs the record.

🤖 Generated with Claude Code

https://claude.ai/code/session_014xHSmzm2ZczcH2ox3WQAC3

…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
@DavertMik
DavertMik merged commit 2a53e76 into main Aug 29, 2026
2 checks passed
@DavertMik
DavertMik deleted the groq-cache-usage branch August 29, 2026 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant