OpenAI Agents SDK budget control with SpendGuard
The OpenAI Agents SDK runs your agent in a loop and ships a model request per step. Without a gate, a stuck agent can re-invoke the same
Runner.rununtil something OOMs or the API rate-limits you. SpendGuard wraps the agent’s model so every step reserves against a budget — and refuses the call when the budget is exhausted.
Why you’d want this
Section titled “Why you’d want this”- Step-level budgeting. Every model call inside
Runner.run()is a reservation point. A runaway loop hits the gate, not your provider invoice. - SDK-native shape.
SpendGuardAgentsModelmirrors the inner model’s surface area, so agent-to-agent handoff and tool-use loops continue to work without changes elsewhere. - Same ledger as the rest of your stack. If you also run Pydantic-AI or LangChain agents, all decisions land in the same audit chain.
Setup (60 seconds)
Section titled “Setup (60 seconds)”pip install 'spendguard-sdk[openai-agents]'Sidecar via demo stack:
git clone https://github.com/m24927605/agentic-spendguard.gitcd agentic-spendguard && make demo-upWire it up
Section titled “Wire it up”import asyncio
from agents import Agent, Runner
from spendguard import SpendGuardClient, new_uuid7from spendguard.integrations.openai_agents import SpendGuardAgentsModelfrom spendguard._proto.spendguard.common.v1 import common_pb2
async def main() -> None: client = SpendGuardClient( socket_path="/var/run/spendguard/adapter.sock", tenant_id="00000000-0000-4000-8000-000000000001", ) await client.connect() await client.handshake()
guarded = SpendGuardAgentsModel( inner_model_name="gpt-4o-mini", client=client, budget_id="my-budget", window_instance_id="my-window", unit=common_pb2.UnitRef( unit_id="usd_micros", token_kind="usd_micros", model_family="gpt-4", ), pricing=common_pb2.PricingFreeze(pricing_version="2025-q4"), claim_estimator=lambda messages: [ common_pb2.BudgetClaim( budget_id="my-budget", window_instance_id="my-window", amount_micros=1_000_000, ) ], )
agent = Agent( name="my-agent", instructions="Be terse.", model=guarded, ) result = await Runner.run(agent, "Hello") print(result.output)
asyncio.run(main())What you get
Section titled “What you get”- Per-step reservation. Every
Runner.runstep calls through the wrapped model and reserves against your budget. - Audit chain. Same signed ledger format as the other framework integrations.
- Composability preserved. Handoff and tool-use semantics flow through the wrapper unchanged.
Common patterns
Section titled “Common patterns”Multiple sub-agents
Section titled “Multiple sub-agents”Each Agent(model=...) needs its own SpendGuardAgentsModel
wrapper. Share one SpendGuardClient across sub-agents — the client
is thread-safe and pools the UDS connection.
Tracing alongside SpendGuard
Section titled “Tracing alongside SpendGuard”The OpenAI Agents SDK emits its own trace events. SpendGuard’s
decision_id is tagged into the model wrapper’s log so you can
correlate the SDK trace with the SpendGuard audit row after the run.
Related
Section titled “Related”- Quickstart — full stack up in 5 minutes
- Contract DSL reference — author allow/stop rules
- Other integrations: Pydantic-AI · LangChain & LangGraph · Microsoft AGT