跳到正文
GitHub·· 7 天前AI 评分62

prompt-cache-linter:定位 LLM 提示词缓存断点并估算漏计成本

AlexRyan92/prompt-cache-linter

AI 导读

prompt-cache-linter 是一个零依赖工具,用于找出 LLM 提示词缓存失效的确切字节位置、失效原因以及每次未命中带来的月度成本。它支持单条 payload 扫描秒级时间戳、UUID、epoch 时间戳等易变模式,也支持对比两条连续 payload 做字节级比较,定位分歧点并统计断点下方被浪费的稳定内容。

正文

Find the exact byte where your LLM prompt cache breaks, what broke it, and what the miss costs per month. Zero dependencies.

Prompt caches (Anthropic, OpenAI, Gemini) match exact byte prefixes. One timestamp at the top of a system prompt silently re-bills your entire prefix on every request, at full price, forever. Teams discover this months later, in the invoice, if they discover it at all.

Two modes:

  • Single payload: scan for volatile patterns (second-level timestamps, UUIDs, epoch stamps, trace IDs, wall-clock times), each with its position, severity, and the fix.
  • Two consecutive payloads (the honest test): byte-level comparison finds the exact divergence point, then a re-sync pass measures the stable content stranded below the break, the span that would cache if the volatile bytes above it moved. Serialization drift (key order, escaping) that no pattern scanner can see shows up here.

There is a hosted version that runs entirely in the browser, and the economics are worked through in Prompt Caching Is Free Money.

Install

npm install prompt-cache-linter   # library
npx cache-lint --help             # CLI, no install

CLI

Capture two consecutive request bodies from the same session (an HTTP proxy, your SDK's debug logging, or your gateway's request log), then:

$ npx cache-lint request-a.json request-b.json --rpd 5000 --price 4
mode:           dual
cache ceiling:  22 of 222 tokens (9.9%)
breaks at:      character 89
stranded:       ~181 stable tokens below the break
cost of miss:   $97.74/month at 5,000 req/day

findings:
  [break] second-level timestamp at 58: "2026-10-11T04:22:17Z"
          Changes every request. Drop it, coarsen to a date, or move it below the stable prefix.

Exit code is 2 when a break-severity finding exists, so it slots into CI:

- run: npx cache-lint fixtures/request-a.json fixtures/request-b.json

Library

import { lint, scan, commonPrefixLength, stableBelow } from 'prompt-cache-linter';

const report = lint(requestA, requestB, { requestsPerDay: 5000, inputPricePerM: 4 });
report.cacheCeilingTokens;    // tokens that can ever hit the cache
report.divergenceIndex;       // exact character where the prefix breaks
report.strandedStableTokens;  // identical content below the break
report.monthlyCostOfMiss;     // USD/month left on the table
report.findings;              // [{ name, severity, index, match, advice }]

Assumptions, stated plainly

Tokens are estimated at ~4 characters each. The cost model uses Anthropic-style economics (cache reads at 10% of input price, the default cacheDiscount: 0.9); OpenAI caches automatically at a smaller discount, so treat dollar figures as an upper bound there. The location of the break is provider-independent. Pattern scanning is necessary but not sufficient: your SDK's serialization order only shows up in the two-payload comparison, which is why that mode exists.

License

MIT © Alexander Ryan

来源:GitHub · github.com