Persistence & CLI
By default everything lives in memory and disappears when the process exits. Add a SQLite file to keep both the cache and the spend ledger:
parry.install(storage="parry.db")# or, on an existing guard:guard.storage("parry.db")guard.set_cache("sqlite", path="parry.db")guard.set_cache("memory") # back to memory onlyparry.storage("parry.db") # configure the global guardWhat gets stored
Section titled “What gets stored”| Table | Contents |
|---|---|
cache | key, serialised response, tokens, cost and expiry (from cache_ttl) |
ledger | one row per call: timestamp, provider, model, input/output tokens, cost, whether it was estimated |
Lookups go to memory first and SQLite second. A hit from SQLite is copied back into memory. Expired rows are removed on read, and stale or excess rows are trimmed periodically.
With storage on, stats gain two fields:
s = guard.statss.today_cost_usd # since local midnights.lifetime_cost_usd # everything in the fileThe parry CLI
Section titled “The parry CLI”parry stats --db parry.dblifetime $3.1842 1204 requests 2311040 in / 401229 outtoday $0.4210 163 requests
openai:gpt-4o-mini 980 req $ 0.5912 anthropic:claude-opus-5-5 224 req $ 2.5930parry stats --db parry.db --json # machine-readableparry pricing # the built-in pricing tableparry pricing --provider anthropic --jsonparry --versionpython -m parry ... works the same way.
Failure behaviour
Section titled “Failure behaviour”- The database uses WAL mode where available and is safe to read with the CLI while your app writes to it.
- One file per process is the intended use. Several processes can share a file, since SQLite serialises the writes, but rate limits stay per process.
- Cache entries are only reloaded into objects from the
openai.*andanthropic.*packages. A tampered file can’t make parry import arbitrary modules.