PydanticAI Agent Guardrails with Veto
Type-safe agents with validation-based authorization. PydanticAI's model-agnostic framework combined with Veto's runtime policy enforcement gives you agents that respect boundaries by design.
Why PydanticAI + Veto
PydanticAI brings type safety to AI agents through Pydantic models. Veto extends that safety to the runtime layer, ensuring every tool call passes through explicit authorization before execution. Together, they give you agents that are both type-safe and permission-aware.
The integration is transparent to your agent. Wrap your tools with Veto'sprotect() function, define policies in YAML, and every tool invocation gets validated against your rules.
Installation
pip install veto pydantic-ai
Quick start
Wrap your PydanticAI agent's tools with Veto's protection layer. The agent remains unaware of the authorization checks happening behind the scenes.
from pydantic_ai import Agent, RunContext
from pydantic import BaseModel
from veto import protect
from typing import Literal
class ToolArgs(BaseModel):
recipient: str
amount: float
currency: Literal["USD", "EUR", "GBP"]
# Define your tools with Pydantic validation
async def transfer_funds(ctx: RunContext, args: ToolArgs) -> str:
"""Transfer funds to a recipient."""
# Your transfer logic here
return f"Transferred {args.amount} {args.currency} to {args.recipient}"
# Wrap with Veto protection
protected_transfer = protect(transfer_funds, policy="transfers")
# Create the agent with protected tools
agent = Agent(
"openai:gpt-4o",
tools=[protected_transfer],
system_prompt="You are a financial assistant."
)
# Run with automatic authorization
result = await agent.run("Send $500 to alice@example.com")
print(result.data)Type-safe policy patterns
Veto policies work directly with your Pydantic models. Define rules that inspect validated arguments, enforce domain constraints, and require approvals for sensitive operations.
version: "1.0"
name: Transfer authorization rules
rules:
- id: block-unverified-recipients
name: Block transfers to unverified recipients
action: block
tools: [transfer_funds]
conditions:
- field: arguments.recipient
operator: not_in
value: "verified_recipients.list"
- id: limit-transfer-amount
name: Enforce daily transfer limits
action: block
tools: [transfer_funds]
conditions:
- field: arguments.amount
operator: greater_than
value: 10000
- field: user.tier
operator: equals
value: "standard"
- id: review-large-transfers
name: Require approval for large transfers
action: require_approval
tools: [transfer_funds]
conditions:
- field: arguments.amount
operator: greater_than
value: 5000
approval:
timeout_minutes: 30
notify: ["finance@company.com"]
- id: restrict-currency
name: Restrict currency by region
action: block
tools: [transfer_funds]
conditions:
- field: arguments.currency
operator: equals
value: "GBP"
- field: user.region
operator: not_equals
value: "UK"Using with dependencies
Pass user context and dependencies through PydanticAI's dependency injection. Veto uses this context to evaluate policies with user-specific constraints.
from pydantic_ai import Agent, RunContext
from pydantic import BaseModel
from veto import protect, VetoContext
class UserDeps(BaseModel):
user_id: str
tier: str
region: str
verified_recipients: list[str]
class TransferArgs(BaseModel):
recipient: str
amount: float
currency: str
async def transfer_funds(
ctx: RunContext[UserDeps],
args: TransferArgs
) -> str:
# Access dependencies via ctx.deps
return f"Transferred {args.amount} to {args.recipient}"
# Inject context into Veto
protected_transfer = protect(
transfer_funds,
policy="transfers",
context_fn=lambda ctx: VetoContext(
user_id=ctx.deps.user_id,
user={"tier": ctx.deps.tier, "region": ctx.deps.region},
metadata={"verified_recipients": ctx.deps.verified_recipients}
)
)
agent = Agent("openai:gpt-4o", tools=[protected_transfer])
# Run with user context
user_deps = UserDeps(
user_id="user_123",
tier="premium",
region="US",
verified_recipients=["alice@example.com", "bob@example.com"]
)
result = await agent.run(
"Send $200 to alice@example.com",
deps=user_deps
)FAQ
Does Veto work with PydanticAI's streaming mode?
Can I use Veto with multiple LLM providers in PydanticAI?
How do type validations interact with authorization?
What happens when a tool call is blocked?
require_approval actions, the tool pauses until a human approves or denies the request through the Veto dashboard or API.Related integrations
Ready to add guardrails to your PydanticAI agents?