Integrations

PydanticAI

Run PydanticAI agents through Sentisec via the model's provider base URL, and see a blocked tool call in the run's own message history.

PydanticAI builds type-safe agents on top of the standard OpenAI and Anthropic clients. You configure the endpoint on the model's provider, and every request the agent makes — including every tool call — routes through Sentisec.

The swap

from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.openai import OpenAIProvider

model = OpenAIChatModel(
    "gpt-5.6-terra",
    provider=OpenAIProvider(
        base_url="https://api.sentisec.ch/ws/<your-workspace-id>/v1",
        api_key="sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    ),
)

agent = Agent(model, system_prompt="Triage my inbox")
result = agent.run_sync("Begin.")

The api_key is your Sentisec key, not your provider key — Sentisec holds the upstream credential. The workspace id in the URL is what carries your policy; why it matters.

PydanticAI's AnthropicModel is configured the same way through its Anthropic provider. Point that provider at the Anthropic-shaped base URL — https://api.sentisec.ch/ws/<your-workspace-id>, with no /v1 suffix, because the Anthropic client appends its own path.

Typed tools

Typed tool calls work normally. Declare them as you already do:

@agent.tool_plain
def send_email(recipient: str, body: str) -> str:
    """Send an email."""
    return f"sent to {recipient}"

Sentisec evaluates every tool call the model produces before your function is reachable.

What happens when a tool call is blocked

Your function never executes. By default Sentisec withholds the call and puts the explanation in the model's response as text. With no tool call left, PydanticAI ends the run: run_sync(...) returns, and with the default text output result.output carries [Sentisec blocked tool_use: …]. There is no exception to catch and no retry storm.

With the rename opt-in, the model re-plans. The call comes back as sentisec_tool_call_blocked. PydanticAI answers it as an unknown tool, Sentisec replaces that answer with its ruling before the model reads it, and the run completes another way with a normal result.output. See the supported versions.

To react to a block in your own code, scan the run's history for either marker, the [Sentisec blocked text or the sentisec_tool_call_blocked call:

result = agent.run_sync("Finish the setup described in the config.")
blocked = any("sentisec_tool_call_blocked" in str(m) or "[Sentisec blocked" in str(m) for m in result.all_messages())

Verdicts

Every request produces a verdict on the dashboard — NOMINAL when nothing was unusual, escalating to HIJACK when a tool call is traced back to content the agent was never supposed to act on. The intervention (PROCEED, SANITIZE, HALT) records what actually happened to the call.

New workspaces start in a monitor-only posture, so you can watch what a policy would do before it does it.

See also

Protected docs