Back to blog Abstract tenant routing cards connected to a source-code inspection lens and verification shield

June 14, 2026 ยท 2 min read

Fixing tenant-aware tools in Pydantic AI

How Codex fixed Pydantic AI tools that ignored per-run tenant data.

We tested a Pydantic AI support agent that routes tickets for several tenants. Each run receives its own tenant, region, priorities, and escalation policies through Pydantic AI dependencies.

The agent declared deps_type=SupportDeps, but both tools used @agent.tool_plain. They could not access RunContext, so every request fell back to global values.

Both runs used Codex GPT-5.5 against pydantic-ai 2.0.0b7 with the same prompt:

Fix this Python fixture so `pytest` succeeds, preserving tenant-aware Pydantic AI routing tools.

Case study replayReal agent replays

Pydantic AI tenant routing tools

model Codex GPT-5.5
$

Fix this Python fixture so `pytest` succeeds, preserving tenant-aware Pydantic AI routing tools.

Without GitHits

Costly
tokens
0
time
0s / 189s
  1. Ready. Click "Watch Replay" to start.

With GitHits

Efficient
tokens
0
time
0s / 99s
  1. Ready. Click "Watch Replay" to start.

Result

RunTimeTokensTools
With GitHits99s393,46921
Without GitHits189s901,66128

Both runs produced a passing patch. The GitHits run finished 90 seconds sooner and used 508,192 fewer processed tokens.

The bug

The tools were registered like this:

@agent.tool_plain
def lookup_ticket(ticket_id: int) -> str:
    return f"{DEFAULT_TENANT}:{ticket_id}:{DEFAULT_PRIORITY}"

@agent.tool_plain
def escalation_policy(ticket_id: int) -> str:
    return (
        f"{DEFAULT_TENANT}:{DEFAULT_PRIORITY}:"
        f"{DEFAULT_POLICY}:{DEFAULT_REGION}"
    )

tool_plain is for tools that do not need the run context. These tools did. The tests expected acme and globex requests to route differently, with fallbacks that still kept the active tenant and region.

The fix

Both tools needed @agent.tool and a typed RunContext:

from pydantic_ai import Agent, RunContext

@agent.tool
def lookup_ticket(ctx: RunContext[SupportDeps], ticket_id: int) -> str:
    priority = ctx.deps.priorities.get(ticket_id, DEFAULT_PRIORITY)
    return f"{ctx.deps.tenant}:{ticket_id}:{priority}"

@agent.tool
def escalation_policy(ctx: RunContext[SupportDeps], ticket_id: int) -> str:
    priority = ctx.deps.priorities.get(ticket_id, DEFAULT_PRIORITY)
    policy = ctx.deps.escalation_policies.get(priority, DEFAULT_POLICY)
    return f"{ctx.deps.tenant}:{priority}:{policy}:{ctx.deps.region}"

The change is small: use the context-aware decorator and read the active dependency object through ctx.deps. Fixing only one tool would still leave routing inconsistent.

What GitHits changed

Both runs produced the same correct patch. Here, GitHits improved efficiency: its run finished 90 seconds sooner, used 508,192 fewer processed tokens, and needed seven fewer tool calls.

GitHits found the relevant Pydantic AI docs for version 2.0.0b7, where @agent.tool and ctx.deps are shown together. A source read then confirmed the Agent.tool API before the edit.

Without GitHits, Codex had to locate the installed package, inspect its exports, read the agent implementation, and trace the run-context types to recover the same contract. GitHits removed that package discovery and source navigation; it did not change the final fix.