nanny-sdk
Python SDK for Nanny — the enforcement primitive for autonomous AI agents.
@tool, @rule, and @agent decorators that enforce step limits, token budgets, tool allowlists, and custom rules per function call. Works with LangChain, CrewAI, or any Python agent framework.
pip install nanny-sdk
Full docs: docs.nanny.run
How it works
Nanny runs as a parent process via nanny run. The SDK decorators communicate with it at each tool call to check limits before the function body executes. Outside nanny run, every decorator is a no-op — zero overhead in development and CI.
# Governed — enforcement active
nanny run
# Passthrough — decorators silent, agent runs normally
python agent.py
uv run agent.py
@tool — declare a governed tool
from nanny_sdk import tool
@tool(tokens=10)
def fetch_page(url: str) -> str:
import httpx
return httpx.get(url).text
Before fetch_page runs, Nanny checks the allowlist, per-tool call limits, and charges 10 tokens against the budget. If any check fails, a NannyStop exception is raised and the function body never executes.
Async functions work identically:
@tool(tokens=10)
async def fetch_page(url: str) -> str:
async with httpx.AsyncClient() as client:
r = await client.get(url)
return r.text
instrument — automatic LLM token tracking
import nanny_sdk, openai
client = openai.OpenAI()
nanny_sdk.instrument(client) # one line — done
Call once at startup. Every LLM completion response is intercepted and its token counts are reported to Nanny's budget automatically — no @tool decorator needed on the LLM call itself.
Supported: OpenAI, Groq, Together AI, Azure OpenAI, LiteLLM, Anthropic, Mistral, Google Gemini (google-genai), Cohere v2. No-op in passthrough mode.
For providers that report prompt-caching usage (OpenAI, Anthropic, DeepSeek, Gemini), instrument also captures cache_read/cache_write — a finer, reporting-only split of input, never additional tokens and never used for enforcement.
@rule — enforce a custom policy
from nanny_sdk import rule
@rule("no_sensitive_files")
def block_sensitive(ctx) -> bool:
path = ctx.last_tool_args.get("path", "")
return ".env" not in path and "secret" not in path
Rules run before every @tool call. Return False to stop execution with RuleDenied. The ctx object exposes requested_tool, last_tool_args, and counters.
@agent — activate named limits for a scope
In a multi-agent system, each agent has a different role and a different risk profile. @agent activates the right named limit set when each role runs, then reverts automatically when it's done:
from nanny_sdk import agent
@agent("researcher")
def run_research_loop(query: str) -> str:
...
Activates [limits.researcher] from nanny.toml for the duration of the function. Limits revert on exit, including on exception. Each role gets its own tool allowlist — the analysis agent cannot call the reporter's tools. Budgets are a different story: tokens spent and steps taken are one running total for the whole run, not a separate pool per role, so hitting the analysis ceiling stops the run there, the reporter never gets to run at all. Want each role to genuinely start from a clean budget instead? See fresh_run below.
fresh_run — starting a genuinely independent budget
@agent changes which ceiling the run's one running total is checked against; it does not give a role its own budget (see above). If your process runs multiple independent phases back to back and want each one to start from zero instead, that's a new run:
import nanny_sdk
nanny_sdk.fresh_run() # everything governed after this point is a fresh run
Only meaningful under a network server (nanny run --serve / --join), which tracks each run's budget independently. Under local nanny run, one process is already always exactly one run, so it's a safe no-op there.
nanny.toml example
[start]
cmd = "uv run agent.py"
[limits]
steps = 50
tokens = 200
timeout = 120000
[limits.researcher]
steps = 30
tokens = 100
[tools]
allowed = ["fetch_page", "search"]
Cloud sync isn't a config field. Run nanny auth login once on a machine and every nanny run there forwards its event log automatically. No login, no sync.
Stop reasons
When a limit is exceeded, a NannyStop exception is raised with one of these reasons:
| Reason | Cause |
|---|---|
BudgetExhausted |
Token ceiling reached |
MaxStepsReached |
Step limit reached |
TimeoutExpired |
Wall-clock limit reached |
ToolDenied |
Tool not in the allowlist |
RuleDenied |
A rule returned False |
AgentCompleted |
Clean exit |
AgentNotFound |
Named limit set in @agent does not exist in nanny.toml |
BridgeUnavailable |
Enforcement was active but became unreachable — fails closed, never continues ungoverned |
Requirements
- Python 3.11+
httpx(only runtime dependency)nannyCLI:- macOS:
brew tap nanny-run/nanny && brew install nannyd - Linux:
curl -fsSL https://install.nanny.run | sh - Windows:
irm https://install.nanny.run/windows | iex
- macOS:
Links
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 nanny_sdk-0.5.0.tar.gz.
File metadata
- Download URL: nanny_sdk-0.5.0.tar.gz
- Upload date:
- Size: 56.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54734610174b777b0f25616fa580422253fe89529b9d67346e4b6852e55f3e16
|
|
| MD5 |
24567bd56f4d8c95d90ca013577a0638
|
|
| BLAKE2b-256 |
a965a315e3214714e5dfa63cf7d436da60a902bbbc229244116a96bd274393cf
|
Provenance
The following attestation bundles were made for nanny_sdk-0.5.0.tar.gz:
Publisher:
release.yml on nanny-run/nanny
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nanny_sdk-0.5.0.tar.gz -
Subject digest:
54734610174b777b0f25616fa580422253fe89529b9d67346e4b6852e55f3e16 - Sigstore transparency entry: 2383708333
- Sigstore integration time:
-
Permalink:
nanny-run/nanny@8d7379e8ef079c09f83889b0939ff7aab0c106a5 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/nanny-run
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8d7379e8ef079c09f83889b0939ff7aab0c106a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file nanny_sdk-0.5.0-py3-none-any.whl.
File metadata
- Download URL: nanny_sdk-0.5.0-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9258d96f77751c74378ad566721cb1490b080b41987fc864a990a061b45f53e
|
|
| MD5 |
3cf49a87780037c0d78a496be3ecc6d0
|
|
| BLAKE2b-256 |
d1a083a7e35fff5d058ba40c3b51b3864a9be80c8da5890a881f3cb26e82eb11
|
Provenance
The following attestation bundles were made for nanny_sdk-0.5.0-py3-none-any.whl:
Publisher:
release.yml on nanny-run/nanny
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nanny_sdk-0.5.0-py3-none-any.whl -
Subject digest:
b9258d96f77751c74378ad566721cb1490b080b41987fc864a990a061b45f53e - Sigstore transparency entry: 2383709135
- Sigstore integration time:
-
Permalink:
nanny-run/nanny@8d7379e8ef079c09f83889b0939ff7aab0c106a5 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/nanny-run
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8d7379e8ef079c09f83889b0939ff7aab0c106a5 -
Trigger Event:
push
-
Statement type: