Use Xantly with PydanticAI

PydanticAI's OpenAIModel accepts a base_url via OpenAIProvider. Point it at Xantly for typed agents with smart routing, cache, and memory.

PydanticAI is Pydantic's agent framework, strongly-typed agents with pluggable providers. Its OpenAIModel takes a base_url kwarg, so pointing it at Xantly unlocks smart routing, semantic cache, and memory under every agent run.

Prerequisites

Setup

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

model = OpenAIModel(
    "xantly/auto-quality",
    provider=OpenAIProvider(
        base_url="https://api.xantly.com/v1",
        api_key="xantly_sk_...",
    ),
)

agent = Agent(model)
result = agent.run_sync("What is PydanticAI?")
print(result.output)

Typed output

PydanticAI's headline feature, enforce a Pydantic return type:

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

class CityInfo(BaseModel):
    name: str
    country: str
    population: int

model = OpenAIModel(
    "openai/gpt-5.4",
    provider=OpenAIProvider(
        base_url="https://api.xantly.com/v1",
        api_key="xantly_sk_...",
    ),
)

agent = Agent(model, output_type=CityInfo)
result = agent.run_sync("Tell me about Tokyo.")
assert isinstance(result.output, CityInfo)
print(result.output.name, result.output.population)

Tools

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

model = OpenAIModel(
    "anthropic/claude-sonnet-4.6",
    provider=OpenAIProvider(
        base_url="https://api.xantly.com/v1",
        api_key="xantly_sk_...",
    ),
)

agent = Agent(model)

@agent.tool
def multiply(ctx: RunContext[None], a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b

print(agent.run_sync("What is 17 * 23?").output)

Streaming

import asyncio

async def main():
    async with agent.run_stream("Stream a haiku.") as result:
        async for chunk in result.stream_text(delta=True):
            print(chunk, end="", flush=True)

asyncio.run(main())

System prompt + dependencies

from dataclasses import dataclass
from pydantic_ai import Agent, RunContext

@dataclass
class Deps:
    user_id: str

agent = Agent(
    model,
    deps_type=Deps,
    system_prompt="You are a concise assistant for user {user_id}.",
)

@agent.system_prompt
def add_context(ctx: RunContext[Deps]) -> str:
    return f"The user's ID is {ctx.deps.user_id}."

result = agent.run_sync("Hi!", deps=Deps(user_id="u_123"))
print(result.output)

Model choice

Model IDWhen
xantly/auto-qualityTyped agents, tool use, default.
anthropic/claude-sonnet-4.6Strong tool-calling for complex agents.
openai/gpt-5.4Tight typed output adherence.
xantly/auto-valueHigh-volume typed classification workloads.

Verify

agent = Agent(
    OpenAIModel(
        "xantly/auto-speed",
        provider=OpenAIProvider(
            base_url="https://api.xantly.com/v1",
            api_key="xantly_sk_...",
        ),
    ),
)
print(agent.run_sync("say pong").output)

Check your Xantly dashboard, the run logs with routed-to model + cost.

What you get

Gotchas

Use OpenAIProvider with base_url. The older base_url= kwarg directly on OpenAIModel was deprecated in recent versions. Wrap it via OpenAIProvider.

/v1 suffix required.

PydanticAI validates tool outputs against their schema. If a provider returns malformed JSON, PydanticAI will reprompt. Xantly's waterfall will swap to a more reliable provider automatically on repeated schema failures.

Async-only for streaming. stream_text is async. Use asyncio.run(...) or inside an event loop.

Next steps