Skip to content

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.

python
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:

python
toolbox.register("a", fn_a).register("b", fn_b).register("c", fn_c)
ParamUse
nameTool name used in agent calls and OPA policy lookups
fnUnderlying function — sync or async
actionOverride 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.

RaiseCause
KeyErrorname isn't registered
GovernanceErrorPolicy 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():

python
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:

python
{
    "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 typeJSON 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():

python
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

python
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:

FrameworkUse
Anthropic (Claude)GovernedToolbox directly + tool_schemas()
OpenAI Chat CompletionsOpenAIAdapter
OpenAI Agents SDKOpenAIAgentsAdapter
LangChain / LangGraphLangChain / LangGraph

Source

kitelogik/governed.py (class GovernedToolbox). Runnable example: examples/02_governed_toolbox.py.

Released under the Apache 2.0 License.