Response caching
Identical requests come back from a bounded LRU in about 10 µs, for $0. Caching →
parry docs
Guard rails for every LLM call
Caching, per-model rate limits, cost tracking, hard budgets and retries for the OpenAI and Anthropic SDKs, with one line and no rewrites.
import parryparry.install() # every OpenAI / Anthropic call is now guardedparry sits between your code and the SDKs you already use. It doesn’t wrap
your client in a new API, ask you for a config file, or need Redis. It hooks
the SDK’s own create() methods, so existing code keeps working, including
code inside LangChain and LlamaIndex.
LLM apps tend to fail in the same boring, expensive ways. They pay twice for identical prompts, hit 429 storms during batch jobs, and run up bills nobody noticed. The fixes are well known but tedious, so every project rewrites the same glue code, or skips it.
parry’s aim is to make the safe path the default path:
Response caching
Identical requests come back from a bounded LRU in about 10 µs, for $0. Caching →
Per-model rate limits
A token bucket for each provider:model, with tier-1 defaults. Rate limiting →
Cost tracking
Exact usage priced from a dated table, broken down per model and per day. Cost tracking →
Hard budgets
BudgetExceeded is raised before the request that would cross your limit. Budgets →
Retries that behave
Backoff with jitter, Retry-After, and no retries on 4xx. Retries →
Streaming & async
Streams pass through untouched and are metered. A finished stream fills the cache. Streaming →
| SDK / framework | Covered by install() |
|---|---|
openai | chat.completions.create, responses.create, sync and async, stream=True, .with_raw_response |
anthropic | messages.create, beta.messages.create, sync and async, stream=True, .with_raw_response |
| LangChain | ChatOpenAI, ChatAnthropic (they call the SDKs above), plus ParryCallback for anything else |
| LlamaIndex | OpenAI, Anthropic LLMs, plus ParryHandler for event-level guarding |