GovernedToolbox
When you have many tools and your agent loop dispatches them by name, GovernedToolbox is the right shape — register once, call by name, governance runs automatically on every call. It's the abstraction every framework adapter is built on top of.
from kitelogik import GovernedToolbox, OPAClient, PolicyGate, SessionContext
gate = PolicyGate(opa_client=OPAClient())
context = SessionContext(
session_id="sess_001",
user_role="support_agent",
session_scopes=["read_customer", "approve_refund_under_100"],
)
toolbox = GovernedToolbox(gate=gate, context=context)
toolbox.register("get_customer_record", get_customer_record)
toolbox.register("approve_refund", approve_refund)
result = await toolbox.call(
"approve_refund",
{"customer_id": "cust_001", "amount": 50.0},
)API
register(name, fn, action=None)
Add a tool. Returns self for chaining:
toolbox.register("a", fn_a).register("b", fn_b).register("c", fn_c)| Param | Use |
|---|---|
name | Tool name used in agent calls and OPA policy lookups |
fn | Underlying function — sync or async |
action | Override OPA action name when it differs from name |
await call(name, args: dict)
Execute a tool through the governance pipeline. Args is a flat dict — the same shape your agent framework parses from the model's tool call.
| Raise | Cause |
|---|---|
KeyError | name isn't registered |
GovernanceError | Policy denied or escalated to HITL — exc.decision carries the full PolicyDecision |
Sync variant: call_sync(name, args) — bridges to the async gate. Safe inside running event loops (Jupyter, FastAPI).
tool_names() -> list[str]
Returns the registered tool names in registration order. Useful for debugging and for asserting the toolbox is what you expect.
tool_schemas() -> list[dict]
Generates Anthropic-format tool definitions by inspecting each function's signature and first docstring line. Use the returned list directly as the tools parameter to anthropic.Client.messages.create():
import anthropic
client = anthropic.AsyncAnthropic()
tools = toolbox.tool_schemas()
response = await client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Refund $50 to cust_001"}],
)The schema shape:
{
"name": "<tool name>",
"description": "<first docstring line, or 'Call <name>'>",
"input_schema": {
"type": "object",
"properties": {<param>: {"type": "<json type>"}},
"required": [<params without defaults>],
},
}Parameter types are mapped from Python annotations:
| Python type | JSON Schema type |
|---|---|
str, no annotation, anything unmapped | "string" |
int | "integer" |
float | "number" |
bool | "boolean" |
list | "array" |
dict | "object" |
For OpenAI-shaped schemas use the OpenAI adapter — it takes explicit per-tool schemas via register(..., schema=...) since the OpenAI tool API expects {"type": "function", "function": {...}} wrappers.
await evaluate_plan(steps: list[dict])
Pre-flight a multi-step plan against the policy gate before any step fires. Wraps PolicyGate.evaluate_plan():
plan = [
{"tool_name": "get_customer_record", "args": {"customer_id": "c1"}},
{"tool_name": "approve_refund", "args": {"customer_id": "c1", "amount": 50}},
]
try:
decision = await toolbox.evaluate_plan(plan)
except GovernanceError as exc:
print(f"Plan rejected: {exc}")See Plan-before-execute for the deeper treatment.
Constructor
GovernedToolbox(
gate: PolicyGate,
context: SessionContext,
sanitize: bool = True,
)Same parameters as @governed. sanitize runs the prompt-injection sanitiser on string return values from tools.
Use directly vs use an adapter
GovernedToolbox is framework-agnostic — it dispatches (name, args_dict) pairs and returns sanitised results. That works directly with Anthropic's tool-use protocol and any custom agent loop.
For OpenAI / Agents SDK / LangChain / LangGraph / etc., the framework adapters wrap GovernedToolbox with framework-shaped tool definitions and result formatting:
| Framework | Use |
|---|---|
| Anthropic (Claude) | GovernedToolbox directly + tool_schemas() |
| OpenAI Chat Completions | OpenAIAdapter |
| OpenAI Agents SDK | OpenAIAgentsAdapter |
| LangChain / LangGraph | LangChain / LangGraph |
Source
kitelogik/governed.py (class GovernedToolbox). Runnable example: examples/02_governed_toolbox.py.