Paying twice for the same answer
Re-running a notebook, an eval or a retry loop sends the identical prompt again, and you pay again.
parry puts caching, per-model rate limits, cost tracking, budgets and retries in front of the SDKs you already use. There's nothing to configure and no call site to rewrite.
import openai
import parry
parry.install() # the only line you add
client = openai.OpenAI()
answer = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hi!"}],
)
print(parry.stats().summary())None of these are hard problems on their own. Together they turn into the same glue code copied into every project, and it usually gets skipped until something breaks.
Re-running a notebook, an eval or a retry loop sends the identical prompt again, and you pay again.
Batch jobs hit the provider's limit, everything fails at once, and naive retries make it worse.
An agent loops, a prompt grows, and you find out at the end of the month.
Make the safe path the default path. No config files, no external services, no new SDK to learn. Add one line, and remove it when you no longer need it.
parry.install() hooks the SDK's own create() methods, so every client in the process is covered, including the ones LangChain and LlamaIndex create for you.
Every feature is on by default and has defaults that suit most apps. You can tune any of them when you need to.
Identical requests come back from a bounded LRU in about 10 µs. The key covers every field that changes the answer, plus the base URL.
cache_ttl=3600Learn more →One token bucket per provider and model, with defaults set to the published tier-1 limits. parry waits for a slot instead of collecting 429s.
set_rate_limit("gpt-4o", 60)Learn more →Exact token usage priced from a dated table and broken down by model. Add SQLite for daily and lifetime totals.
stats.total_cost_usdLearn more →A request that would push spend past the limit raises BudgetExceeded before it leaves your process. Cached answers stay free.
max_cost_usd=10Learn more →Exponential backoff with jitter. Honours Retry-After, retries 429, 5xx and timeouts, and never retries a 401.
max_attempts=3Learn more →Streams reach you untouched and are metered as they flow. A finished stream fills the cache for the next call.
stream=TrueLearn more →The homemade version covers one client, one model and one code path. parry covers every client, every model and every code path, including async, streaming and framework calls.
cache, spend = {}, 0.0
bucket = TokenBucket(rate=500 / 60)
def ask(msgs, model="gpt-4o"):
global spend
key = sha256(dumps([model, msgs]))
if key in cache:
return cache[key]
if spend > BUDGET:
raise RuntimeError("over budget")
for attempt in range(5):
time.sleep(bucket.wait_time())
try:
r = client.chat.completions.create(
model=model, messages=msgs)
break
except (RateLimitError, APITimeoutError):
time.sleep(2 ** attempt + random())
u, p = r.usage, PRICES[model]
spend += (u.prompt_tokens * p[0]
+ u.completion_tokens * p[1]) / 1e6
cache[key] = r
return r
# ...again for async, streaming, Anthropicimport parry
parry.install(max_cost_usd=10)
r = client.chat.completions.create(
model="gpt-4o", messages=msgs)The hot path is Rust: hashing, the LRU, the token buckets and the pricing ledger. These numbers are measured with the real OpenAI SDK over an in-memory transport, so only parry's own cost is counted.
per cache hit, with no HTTP at all
added to a cache miss
of configuration required
to install it, and one to remove it
One abi3 wheel per platform covers Python 3.10 to 3.14. It has no required dependencies.
pip install llm-parryPut it at startup. Clients created before or after it are covered.
import parry
parry.install()Check hits, spend and throttling at any point.
print(parry.stats().summary())
# cache 2 hits / 1 misses (67%)
# spend $0.000006 this sessionparry.install()Every client in the processwith Parry() as g:One block, thread- and task-localparry.wrap(client)A single client objectwith parry.bypass():Turn it off for one callStart with the quickstart, or see every option in the reference.