How RequireApprovalPolicy Controls Tool Execution in aisuite: A Complete Guide

RequireApprovalPolicy is a built-in safety mechanism that intercepts every LLM tool invocation in aisuite and delegates the execution decision to a custom callback function, enabling human-in-the-loop approval or automated policy enforcement before any Python function runs.

When building AI agents with the andrewyng/aisuite framework, controlling which tools the LLM can execute is critical for security and compliance. The RequireApprovalPolicy class, defined in aisuite/agents/policies.py, provides a programmable gatekeeper that evaluates every tool call against custom business logic before the underlying Python callable is invoked.

What is RequireApprovalPolicy?

RequireApprovalPolicy is one of the built-in tool policies available in aisuite's agent framework. It is designed to execute a user-defined callback for every tool invocation, allowing developers to implement approval workflows, audit logging, or automated risk assessment before the actual tool function executes.

The policy is exposed at the top-level aisuite namespace and is configured when initializing an Agent instance through the tool_policy parameter.

The ToolPolicyContext Object

When a tool is called during agent execution, aisuite constructs a ToolPolicyContext containing the complete invocation details. According to the implementation in aisuite/agents/policies.py, this context includes:

  • tool_name: The string identifier of the tool being invoked
  • arguments: The JSON-decoded arguments supplied by the model
  • tool_metadata: Optional ToolMetadata attached to the callable via the @ai.tool decorator
  • agent: The Agent instance that requested the tool execution
  • client: The Client used for the LLM request

This context object is passed as the single argument to your approval callback, giving you full visibility into what the model is attempting to do.

Implementing the Approval Callback

The core of RequireApprovalPolicy is the callback function you provide during instantiation. As implemented in lines 43-61 of aisuite/agents/policies.py, this callback must return one of two types:

  1. Boolean: True allows execution, False denies it
  2. ToolPolicyDecision: An object specifying both allowed (bool) and reason (str)

If you return a boolean, aisuite automatically wraps it in a ToolPolicyDecision with the reason set to "approved" or "denied". Returning any other type raises a TypeError.

The callback signature should accept a ToolPolicyContext and return the decision:

def approval_callback(ctx: ai.ToolPolicyContext) -> ai.ToolPolicyDecision:
    if ctx.tool_metadata.risk_level == "high":
        return ai.ToolPolicyDecision(
            allowed=False,
            reason="high-risk operations require manual review"
        )
    return True  # Equivalent to ToolPolicyDecision(allowed=True, reason="approved")

Execution Flow and Decision Handling

Once the callback returns a decision, aisuite processes the result as follows:

  • If allowed=False, the tool's Python function is not executed. The denial reason is returned to the agent as the tool result.
  • If allowed=True, the tool executes normally with the provided arguments.
  • The final ToolPolicyDecision is stored in the execution trace at step.data["allowed"] and step.data["reason"], providing full observability for debugging and audit trails.

This mechanism is tested in tests/agents/test_tool_metadata_policy.py (lines 15-30), where callbacks record invocation details and explicitly deny high-risk operations to verify the policy enforcement chain.

Complete Implementation Example

The following example demonstrates configuring an agent with RequireApprovalPolicy to gate database deletion operations based on risk metadata:

import aisuite as ai

# 1. Define a tool with metadata indicating it requires approval

@ai.tool
def delete_user(user_id: str) -> str:
    """Delete a user from the database."""
    return f"User {user_id} deleted"

# Attach metadata to mark as high-risk

delete_user = ai.tool(
    delete_user,
    metadata=ai.ToolMetadata(
        category="admin",
        risk_level="high",
        requires_approval=True,
    ),
)

# 2. Define the approval callback with business logic

def approval_callback(ctx: ai.ToolPolicyContext) -> ai.ToolPolicyDecision:
    # Automatically deny high-risk tools in this example

    if ctx.tool_metadata and ctx.tool_metadata.risk_level == "high":
        return ai.ToolPolicyDecision(
            allowed=False,
            reason="high-risk operation blocked by policy"
        )
    return True

# 3. Create agent with the RequireApprovalPolicy

agent = ai.Agent(
    name="assistant",
    model="openai:gpt-4o",
    tools=[delete_user],
    tool_policy=ai.RequireApprovalPolicy(approval_callback),
)

# 4. Execute - policy is consulted before delete_user runs

result = ai.Runner.run_sync(agent, "Remove user 12345")
print(result.final_output)   # "high-risk operation blocked by policy"

print(result.steps[-1].data) # {'allowed': False, 'reason': '...', ...}

In this implementation, the approval_callback receives the full ToolPolicyContext including the ToolMetadata, allowing risk-based decisions. The execution trace preserves the denial reason for observability.

Summary

  • RequireApprovalPolicy acts as a programmable gatekeeper for all tool executions in aisuite, defined in aisuite/agents/policies.py
  • The policy receives a ToolPolicyContext containing complete invocation details including tool_name, arguments, tool_metadata, agent, and client
  • Approval callbacks return boolean values or ToolPolicyDecision objects; invalid return types raise TypeError
  • When denied, the tool function is skipped entirely and the reason is recorded in step.data["reason"]
  • The policy integrates with ToolMetadata defined in aisuite/utils/tools.py to enable risk-based access control

Frequently Asked Questions

What happens when the approval callback returns False?

When the callback returns False or a ToolPolicyDecision with allowed=False, aisuite skips the Python function execution entirely. The denial reason is stored in the step's trace data at step.data["allowed"] and step.data["reason"], then returned to the agent as the tool result instead of the actual function output.

Can I access custom tool metadata in the approval callback?

Yes. The ToolPolicyContext passed to your callback includes a tool_metadata field containing the ToolMetadata object attached to the callable via the @ai.tool decorator. This metadata is defined in aisuite/utils/tools.py and can include custom fields like risk_level or category to implement granular policies.

What return types are valid for the approval callback?

The callback must return either a boolean (True to allow, False to deny) or a ToolPolicyDecision object specifying both allowed and reason fields. According to the source in aisuite/agents/policies.py, any other return type raises a TypeError. Boolean returns are automatically wrapped in ToolPolicyDecision with default reasons.

How do I configure an agent to use RequireApprovalPolicy?

Instantiate RequireApprovalPolicy with your callback function and pass it to the tool_policy parameter when creating an Agent. The class is exported at the top-level aisuite namespace, making it accessible as ai.RequireApprovalPolicy. You can combine it with tools that have ToolMetadata attached to create sophisticated approval workflows.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →