Skip to content

Pydantic AI: using the VIDAI Control Plane as the backend

Pydantic AI's OpenAIModel takes an explicit OpenAIProvider. Set the provider's base_url to the control plane and every agent using that model routes through it.

TL;DR

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

model = OpenAIModel(
    "gpt-4o-mini",
    provider=OpenAIProvider(
        base_url="https://your-vidai-server.example.com/v1",
        api_key="your-vidai-key",
    ),
)

agent = Agent(model)
result = agent.run_sync("reply with: ok")
print(result.output)

Prerequisites

  • pip install pydantic-ai (any recent version with OpenAIProvider — 0.0.14 onward).
  • Control plane base URL and an API key from API Keys.
  • A model registered on the Models page.

Structured output

Pydantic AI's whole point is typed output. It works transparently — the control plane forwards the JSON-mode request unchanged:

from pydantic import BaseModel
from pydantic_ai import Agent

class Ticket(BaseModel):
    priority: str
    reason: str

agent = Agent(model, output_type=Ticket)
result = agent.run_sync("Priority for: printer is on fire")
print(result.output.priority, result.output.reason)

Tools

Tools attached with the @agent.tool decorator are called through the same model. No extra setup:

@agent.tool
def get_stock(ctx, symbol: str) -> float:
    return 137.42

result = agent.run_sync("What's the current price of AAPL?")

Every tool-call round trip is a control-plane request that appears in Request Logs.

Streaming

agent.run_stream() streams tokens back — the control plane forwards the stream unchanged:

async with agent.run_stream("write a haiku about ships") as run:
    async for chunk in run.stream_text():
        print(chunk, end="", flush=True)

Cross-provider routing

Pydantic AI has separate AnthropicModel, GeminiModel, etc., but you don't need them to reach non-OpenAI upstreams — a model registered on the control plane that routes to Anthropic is still callable via OpenAIModel("claude-haiku-4-5", ...). The control plane handles the wire-format translation.

If your code specifically needs AnthropicModel (say, for Anthropic-specific tool-call schemas), point its provider at the control plane the same way:

from pydantic_ai.models.anthropic import AnthropicModel
from pydantic_ai.providers.anthropic import AnthropicProvider

model = AnthropicModel(
    "claude-haiku-4-5",
    provider=AnthropicProvider(
        base_url="https://your-vidai-server.example.com",
        api_key="your-vidai-key",
    ),
)

Note the base URL for AnthropicProvider is the root (no /v1); the SDK adds /v1/messages itself.

Verify it works

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

model = OpenAIModel(
    "gpt-4o-mini",
    provider=OpenAIProvider(
        base_url="https://your-vidai-server.example.com/v1",
        api_key="your-vidai-key",
    ),
)
agent = Agent(model)
print(agent.run_sync("reply with exactly: ok").output)

If something's off

Raise an issue at github.com/vidaiUK/vidai-quickstart/issues with your provider config and the agent snippet that reproduces the issue. We'll get it sorted.

Where to go next