Skip to main content

prompt-cache-fit

Reorder LLM prompt blocks from least- to most-volatile so provider prefix caches stay hot, and measure whether it actually worked. This is the Python port of the JavaScript package of the same name.

The problem

Anthropic, OpenAI, xAI and DeepSeek all cache prompt prefixes. The cache is only reusable up to the first byte that differs between two requests. Teams that put a timestamp, a request id, or "today's date" near the top of the system prompt throw away the cache for every token below it, even though the rest of the prompt (the system message, the tool schema, the RAG chunks) did not actually change. The published advice is "order your prompt from least-variable to most-variable", but every team hand-rolls that ordering, and almost nobody measures whether the reordering actually raised the hit rate. This package does the reordering and the measurement, nothing else: no tokenizer, no provider SDK, no network calls.

Install

pip install prompt-cache-fit

Usage

from prompt_cache_fit import fit, hit_rate

static_block = {"text": "You are a helpful coding assistant.", "volatility": "static"}
tools_block = {"text": "Tools: search_docs(query), run_tests()", "volatility": "shared"}
session_block = {
    "text": "User profile: senior backend engineer, prefers concise answers.",
    "volatility": "session",
}
timestamp1 = {"text": "Timestamp: 2026-09-04T10:00:00Z", "volatility": "turn"}
question1 = {"text": "Turn: how do I retry a failed HTTP request?", "volatility": "turn"}
timestamp2 = {"text": "Timestamp: 2026-09-04T10:05:00Z", "volatility": "turn"}
question2 = {"text": "Turn: how do I cancel an in-flight fetch?", "volatility": "turn"}

# Hand-rolled: the volatile timestamp sits in the middle of the prompt.
hand_rolled1 = "\n\n".join(
    b["text"] for b in [static_block, timestamp1, tools_block, session_block, question1]
)
hand_rolled2 = "\n\n".join(
    b["text"] for b in [static_block, timestamp2, tools_block, session_block, question2]
)

# fit(): volatile blocks (rank 'turn') sort to the end automatically.
fitted1 = fit([static_block, timestamp1, tools_block, session_block, question1])
fitted2 = fit([static_block, timestamp2, tools_block, session_block, question2])

print(hit_rate([hand_rolled1, hand_rolled2]).mean)  # 0.2916666666666667
print(hit_rate([fitted1.text, fitted2.text]).mean)  # 0.7777777777777778

Moving the two turn-tier blocks to the end of the prompt takes the measured prefix hit rate on the second request from about 29% to about 78%, because the shared system prompt, tool schema and session block now form one unbroken prefix instead of being interrupted by the timestamp.

API

fit(blocks, *, separator="\n\n") -> FitResult

  • blocks: List[Dict[str, Any]], each dict with a required text: str key and optional volatility: str, id, pin: bool keys. Plain dicts are accepted; no class is required.
  • volatility is one of "static" | "shared" | "session" | "turn", ranked in that order (static sorts first, turn sorts last). Missing or unrecognized values default to "turn"; unknown strings never raise.
  • pin=True on a block keeps that block at its original index. Every other block is sorted around it.
  • The sort is stable: two blocks with the same rank keep their original relative order.
  • separator (keyword-only, default "\n\n"): the string joined between rendered blocks.
  • Returns a frozen FitResult dataclass:
    • blocks: the input blocks, reordered.
    • text: the reordered blocks joined with separator.
    • moved: count of blocks whose index differs from the input.
    • stable_prefix_chars: characters from the start of text through the end of the last block ranked below turn, including the separator that follows it if another block comes after. 0 if every block is turn.

shared_prefix(a, b) -> int

Number of shared leading characters between two strings. shared_prefix("abc", "abd") is 2. Returns 0 for empty strings.

hit_rate(prompts) -> HitRateReport

prompts is a list of already-rendered prompt strings, in send order. For each prompt after the first, computes shared_prefix(prompts[i-1], prompts[i]) / len(prompts[i]). Returns a frozen HitRateReport dataclass:

  • per_request: one fraction (0..1) per request after the first.
  • mean: mean of per_request, 0 if fewer than 2 prompts were given.
  • cached_chars: sum of shared prefix lengths across all requests after the first.
  • total_chars: sum of len(prompts[i]) for i >= 1.

An empty prompt string contributes 0 instead of dividing by zero.

VOLATILITY

{"static": 0, "shared": 1, "session": 2, "turn": 3}, exported as a plain dict so forkers can add their own tiers or change the ranks.

How it works

fit() is a stable sort by volatility rank plus a reinsertion pass for pinned blocks; hit_rate() is a character-by-character common-prefix scan. That is the whole library. It does not know how any provider actually tokenizes or chunks its cache, so stable_prefix_chars and hit_rate are character-level proxies, not a guarantee of a provider-side cache hit: real caches key on token boundaries and have their own minimum prefix length and TTL. This package also does not inject provider-specific cache markers (Anthropic's cache_control, OpenAI's automatic prefix caching, etc.) into the output; ordering the blocks correctly is a precondition for those markers to help, but adding the markers themselves is left to the caller, since the marker format is provider-specific and out of scope here.

The JavaScript version of this package lives at the repository root: https://github.com/pjdurden/prompt-cache-fit

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

prompt_cache_fit-0.1.0.tar.gz (6.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

prompt_cache_fit-0.1.0-py3-none-any.whl (5.7 kB view details)

Uploaded Python 3

File details

Details for the file prompt_cache_fit-0.1.0.tar.gz.

File metadata

  • Download URL: prompt_cache_fit-0.1.0.tar.gz
  • Upload date:
  • Size: 6.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for prompt_cache_fit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c1e38feb9f37f743e3ab987e0d83d15931915af5bc30f8f4c2207cc77257c68d
MD5 4293bd1275bd848f925cf108dd60031d
BLAKE2b-256 625553878b9602efcaf5dbb7c4c1f966080925db21154eecc2541d77a25caa75

See more details on using hashes here.

File details

Details for the file prompt_cache_fit-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: prompt_cache_fit-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 5.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for prompt_cache_fit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 100665d102d000c4cae05dedd6d22e3d846b3456b2a74f07f85cdad6cbf6b0ba
MD5 b5f46f7d8f541c6fde81d27c0787e1d9
BLAKE2b-256 70a218b2ffb06d48f709c4a335a04508e3b475605baee33ac55b35b60748b82d

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page