# Use Xantly with PydanticAI

[PydanticAI](https://ai.pydantic.dev) 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

- Python 3.10+
- `pip install pydantic-ai`
- A Xantly API key, [create one](/dashboard/api-keys)

## Setup

```python
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:

```python
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

```python
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

```python
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

```python
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 ID | When |
|---|---|
| `xantly/auto-quality` | Typed agents, tool use, default. |
| `anthropic/claude-sonnet-4.6` | Strong tool-calling for complex agents. |
| `openai/gpt-5.4` | Tight typed output adherence. |
| `xantly/auto-value` | High-volume typed classification workloads. |

## Verify

```python
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

- **Every PydanticAI feature proxied.** Typed output, tools, dependencies, streaming, multi-agent handoffs.
- **Tool-call translation** across providers, pick `xantly/auto-quality` and BaRP may swap between Claude/GPT/Groq while your typed agent keeps working.
- **Semantic cache.** Repeat queries with the same output schema hit cache.
- **Memory.** Set `extra_headers` via `OpenAIProvider`'s client config to opt into Xantly's L3 memory.
- **Cost per run.** Each `agent.run_sync(...)` call shows up with model + cost.

## 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

- [Instructor](/docs/use-with-instructor), alternative structured-output library.
- [OpenAI SDK (Python)](/docs/use-with-openai-sdk-python), lower-level control.
- [LangChain (Python)](/docs/use-with-langchain-python), if you want a bigger agent framework.
- [Multi-Agent Orchestration](/docs/multi-agent-orchestration), Xantly-native agent chaining.
