pydantic-deep supports requiring human approval for sensitive operations through the interrupt_on configuration.
agent = create_deep_agent(
interrupt_on={
"execute": True, # Shell command execution
"write_file": True, # Creating/overwriting files
"edit_file": True, # Modifying existing files
}
)When a tool requires approval:
- Agent calls the tool
- Tool execution is deferred
DeferredToolRequestsreturned instead of result- You review and approve/deny
- Resume execution with decisions
import asyncio
from pydantic_deep import create_deep_agent, DeepAgentDeps, StateBackend
async def main():
agent = create_deep_agent(
interrupt_on={
"execute": True,
"write_file": True,
}
)
deps = DeepAgentDeps(backend=StateBackend())
# Initial run
result = await agent.run(
"Create a script that prints hello world and run it",
deps=deps,
)
# Check if approval needed
if hasattr(result, 'deferred_tool_calls'):
print("Approval needed for:")
for call in result.deferred_tool_calls:
print(f" - {call.tool_name}: {call.args}")
# In a real app, you'd prompt the user
# For this example, approve all
approved = result.approve_all()
# Resume with approvals
result = await agent.run(
approved,
deps=deps,
message_history=result.all_messages(),
)
print(result.output)
asyncio.run(main())You can approve or deny individual tool calls:
if hasattr(result, 'deferred_tool_calls'):
decisions = []
for call in result.deferred_tool_calls:
if call.tool_name == "execute":
# Review command before approving
if "rm" in call.args.get("command", ""):
decisions.append(call.deny("Destructive command not allowed"))
else:
decisions.append(call.approve())
elif call.tool_name == "write_file":
# Always approve writes
decisions.append(call.approve())
# Resume with decisions
result = await agent.run(
decisions,
deps=deps,
message_history=result.all_messages(),
)For CLI applications:
async def interactive_run(agent, prompt, deps):
result = await agent.run(prompt, deps=deps)
while hasattr(result, 'deferred_tool_calls'):
for call in result.deferred_tool_calls:
print(f"\nTool: {call.tool_name}")
print(f"Args: {call.args}")
response = input("Approve? [y/n]: ").lower()
if response == 'y':
call.approve()
else:
reason = input("Reason for denial: ")
call.deny(reason)
result = await agent.run(
result.get_decisions(),
deps=deps,
message_history=result.all_messages(),
)
return resultFor web apps with async approval:
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
pending_approvals = {}
@app.post("/agent/run")
async def run_agent(prompt: str):
result = await agent.run(prompt, deps=deps)
if hasattr(result, 'deferred_tool_calls'):
# Store for later approval
request_id = generate_id()
pending_approvals[request_id] = {
"result": result,
"calls": result.deferred_tool_calls,
}
return {
"status": "pending_approval",
"request_id": request_id,
"tools": [
{"name": c.tool_name, "args": c.args}
for c in result.deferred_tool_calls
]
}
return {"status": "complete", "output": result.output}
@app.post("/agent/approve/{request_id}")
async def approve(request_id: str, decisions: list[dict]):
pending = pending_approvals.pop(request_id)
for i, decision in enumerate(decisions):
call = pending["calls"][i]
if decision["approved"]:
call.approve()
else:
call.deny(decision.get("reason", "Denied"))
result = await agent.run(
pending["result"].get_decisions(),
deps=deps,
message_history=pending["result"].all_messages(),
)
return {"status": "complete", "output": result.output}By default:
| Tool | Requires Approval |
|---|---|
execute |
Yes (if enabled) |
write_file |
No |
edit_file |
No |
task |
No |
| Other tools | No |
!!! tip
Even without approval, execute only works with sandbox backends.
interrupt_on={"execute": True}Shell commands can be dangerous. Always review.
interrupt_on={
"write_file": True,
"edit_file": True,
}In production environments, review file modifications.
import logging
logger = logging.getLogger(__name__)
for call in result.deferred_tool_calls:
logger.info(f"Approving: {call.tool_name} with {call.args}")
call.approve()import asyncio
try:
approval = await asyncio.wait_for(
get_user_approval(call),
timeout=300, # 5 minute timeout
)
except asyncio.TimeoutError:
call.deny("Approval timeout")