callguard
Guardrails for AI-agent tool / function calls. Framework-agnostic, zero heavy
dependencies (only jsonschema). Makes agent tool calls reliable and
cost-bounded — the two things every agent framework reinvents from
scratch and gets wrong.
Why this exists
In production, agents don't fail with clean exceptions. They fail with:
- JSON that has single quotes, trailing commas, unquoted keys (LLM output)
- Wrong types (
"123"instead of123) - Missing required args (the model "forgot")
- Typo'd tool names (
send_emialvssend_email) - $40 runaway loops because nothing capped spend
- Retry storms against a tool that's 5xx-ing
callguard handles all six in one decorator.
Install
pip install agent-callguard
(PyPI name is agent-callguard; you import it as callguard.)
Quick start
from callguard import Guard
g = Guard(
budget=g.budget(max_cost=1.0, cost_per_call=0.002), # stop the $40 loop
breaker=g.breaker(failure_threshold=5, reset_after=30), # halt retry storms
cache=g.cache(ttl=120), # skip dup idempotent reads
)
@g.tool(schema={
"type": "object",
"properties": {
"to": {"type": "string"},
"subject": {"type": "string"},
"priority": {"type": "string", "enum": ["low", "normal", "high"]},
"retry": {"type": "integer"},
},
"required": ["to", "subject"],
})
def send_email(to, subject, body="", priority="normal", retry=0):
... # your real tool body
return f"sent:{to}"
# The LLM produced this MESSY blob — callguard fixes it automatically:
g.call("send_email", "{'to': 'a@b.com', 'subject': 'Hi', 'retry': '3'}")
# -> single quotes repaired, '3' coerced to int 3, tool executed OK
# Typo'd tool name? Auto-recovered:
g.call("send_emial", {"to": "a@b.com", "subject": "Hi"}) # redirects to send_email
# Missing required arg? Structured, actionable error (not a stack trace):
try:
g.call("send_email", {"to": "a@b.com"})
except ToolCallError as e:
print(e.errors[0].suggestion) # "provide 'subject'"
What it does
| Feature | Module | Behavior |
|---|---|---|
| JSON repair | errors.repair_json |
single quotes, trailing commas, unquoted keys, True/False/None, code fences |
| Type coercion | schema.validate |
"3"→3, "true"→True, "1.5"→1.5 before failing |
| Actionable validation | schema.validate |
errors name the field + show the fix: "field 'x' expected number, got str" |
| Tool-name recovery | errors.suggest_tool |
Levenshtein match redirects send_emial→send_email |
| Error classification | errors.classify |
transient/retryable (timeout, 429, 5xx) vs hard (401, 404, auth) |
| Cost & quota budget | budget.Budget |
raises before a hard cap; soft-warn callback at 80% |
| Circuit breaker | breaker.CircuitBreaker |
opens after N consecutive failures, half-opens to probe recovery |
| Response cache | cache.Cache |
short-TTL dedupe for idempotent reads |
Lower-level pieces
Every component is importable and usable on its own:
from callguard import repair_json, validate, classify, suggest_tool, parse_args
from callguard import Budget, CircuitBreaker, Cache, tool, Guard
repair_json("{'a': 1,}") # '{"a": 1}'
classify("503 Service Unavailable") # CallError(kind='transient', recoverable=True)
suggest_tool("send_emial", ["send_email", "get_weather"]) # 'send_email'
Backtest
The repo ships a regression corpus of real-world malformed agent calls
(callguard/backtest.py) — single-quote JSON, trailing commas, missing
args, enum typos, tool-name typos, transient vs hard errors, budget
enforcement, circuit-breaker trips. Run it:
python -m callguard.backtest
Current status: 18 / 18 cases passing.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agent_callguard-0.1.0.tar.gz.
File metadata
- Download URL: agent_callguard-0.1.0.tar.gz
- Upload date:
- Size: 63.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6605e67879c22d48a754323575a3a1709451b32447d602f0cae8134df2297a81
|
|
| MD5 |
0640833df71b58b7ca3ce375dd5b5d3a
|
|
| BLAKE2b-256 |
2c4927f1b44bbaf75407e5553c26388ae1883df8ff30ca9521f0876c0981fd86
|
File details
Details for the file agent_callguard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agent_callguard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8626f471f36598e1a1586cfd96b9ddd074c52aad61cd421c030f7eb10c11a2ea
|
|
| MD5 |
ab0e948dd66ac5457185d452bfb3af04
|
|
| BLAKE2b-256 |
0d98148a6c74412c18188a0c396a790d363304defa645c98d167308032e48053
|