Skip to content

Scoping a guard

A guard is a parry.Parry object. It owns one cache, one set of rate-limit buckets, one spend ledger and one retry policy. You choose where it applies.

import parry
guard = parry.install() # default guard
guard = parry.install(max_cost_usd=10) # guard with options
guard = parry.install(parry.Parry(...)) # a guard you built yourself

Every OpenAI and Anthropic client in the process is covered, including clients created by frameworks and clients created before this line.

When more than one scope applies, the most specific wins:

  1. with parry.bypass(): turns parry off for everything inside it
  2. a client bound with parry.wrap(client, guard)
  3. the innermost with Parry(): block
  4. the guard from parry.install()
  5. no guard: the call goes straight to the SDK
parry.install() # global guard A
with Parry() as b: # B wins inside the block
client.chat.completions.create(...)
c = Parry()
special = c.wrap(openai.OpenAI()) # C always wins for `special`
with parry.bypass():
client.chat.completions.create(...) # no parry at all

install() is idempotent. SDK methods are never wrapped twice.

CallEffect
parry.install() again with no argumentsreturns the guard that is already installed
parry.install(cache=False)creates a new guard with those options and installs it
parry.install(guard, cache=False)TypeError: pass a guard or options, not both
parry.uninstall()removes every hook and forgets the global guard
parry.install(anthropic=False) # OpenAI only
parry.install(sync=False) # async clients only
parry.install_async() # same as above
parry.get_guard() # with-block guard, else the installed guard, else None
parry.is_installed() # hooks applied and a global guard set
parry.stats() # stats of the active guard
guard.is_intercepting # True if this guard receives SDK calls through the hooks