Skip to content

Commit 05fbaaa

Browse files
ajac-zeroDouweM
andauthored
Add dedicated OpenRouterModel with support for thinking and more (#3089)
Co-authored-by: Douwe Maan <[email protected]>
1 parent 5f4e078 commit 05fbaaa

24 files changed

+3252
-158
lines changed

docs/models/openai.md

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ agent = Agent(model)
233233
```
234234

235235
Various providers also have their own provider classes so that you don't need to specify the base URL yourself and you can use the standard `<PROVIDER>_API_KEY` environment variable to set the API key.
236-
When a provider has its own provider class, you can use the `Agent("<provider>:<model>")` shorthand, e.g. `Agent("deepseek:deepseek-chat")` or `Agent("openrouter:google/gemini-2.5-pro-preview")`, instead of building the `OpenAIChatModel` explicitly. Similarly, you can pass the provider name as a string to the `provider` argument on `OpenAIChatModel` instead of building instantiating the provider class explicitly.
236+
When a provider has its own provider class, you can use the `Agent("<provider>:<model>")` shorthand, e.g. `Agent("deepseek:deepseek-chat")` or `Agent("moonshotai:kimi-k2-0711-preview")`, instead of building the `OpenAIChatModel` explicitly. Similarly, you can pass the provider name as a string to the `provider` argument on `OpenAIChatModel` instead of building instantiating the provider class explicitly.
237237

238238
#### Model Profile
239239

@@ -385,34 +385,6 @@ agent = Agent(model)
385385
...
386386
```
387387

388-
### OpenRouter
389-
390-
To use [OpenRouter](https://openrouter.ai), first create an API key at [openrouter.ai/keys](https://openrouter.ai/keys).
391-
392-
You can set the `OPENROUTER_API_KEY` environment variable and use [`OpenRouterProvider`][pydantic_ai.providers.openrouter.OpenRouterProvider] by name:
393-
394-
```python
395-
from pydantic_ai import Agent
396-
397-
agent = Agent('openrouter:anthropic/claude-3.5-sonnet')
398-
...
399-
```
400-
401-
Or initialise the model and provider directly:
402-
403-
```python
404-
from pydantic_ai import Agent
405-
from pydantic_ai.models.openai import OpenAIChatModel
406-
from pydantic_ai.providers.openrouter import OpenRouterProvider
407-
408-
model = OpenAIChatModel(
409-
'anthropic/claude-3.5-sonnet',
410-
provider=OpenRouterProvider(api_key='your-openrouter-api-key'),
411-
)
412-
agent = Agent(model)
413-
...
414-
```
415-
416388
### Vercel AI Gateway
417389

418390
To use [Vercel's AI Gateway](https://vercel.com/docs/ai-gateway), first follow the [documentation](https://vercel.com/docs/ai-gateway) instructions on obtaining an API key or OIDC token.

docs/models/openrouter.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# OpenRouter
2+
3+
## Install
4+
5+
To use `OpenRouterModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `openrouter` optional group:
6+
7+
```bash
8+
pip/uv-add "pydantic-ai-slim[openrouter]"
9+
```
10+
11+
## Configuration
12+
13+
To use [OpenRouter](https://openrouter.ai), first create an API key at [openrouter.ai/keys](https://openrouter.ai/keys).
14+
15+
You can set the `OPENROUTER_API_KEY` environment variable and use [`OpenRouterProvider`][pydantic_ai.providers.openrouter.OpenRouterProvider] by name:
16+
17+
```python
18+
from pydantic_ai import Agent
19+
20+
agent = Agent('openrouter:anthropic/claude-3.5-sonnet')
21+
...
22+
```
23+
24+
Or initialise the model and provider directly:
25+
26+
```python
27+
from pydantic_ai import Agent
28+
from pydantic_ai.models.openrouter import OpenRouterModel
29+
from pydantic_ai.providers.openrouter import OpenRouterProvider
30+
31+
model = OpenRouterModel(
32+
'anthropic/claude-3.5-sonnet',
33+
provider=OpenRouterProvider(api_key='your-openrouter-api-key'),
34+
)
35+
agent = Agent(model)
36+
...
37+
```
38+
39+
## App Attribution
40+
41+
OpenRouter has an [app attribution](https://openrouter.ai/docs/app-attribution) feature to track your application in their public ranking and analytics.
42+
43+
You can pass in an `app_url` and `app_title` when initializing the provider to enable app attribution.
44+
45+
```python
46+
from pydantic_ai.providers.openrouter import OpenRouterProvider
47+
48+
provider=OpenRouterProvider(
49+
api_key='your-openrouter-api-key',
50+
app_url='https://your-app.com',
51+
app_title='Your App',
52+
),
53+
...
54+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ nav:
3131
- models/google.md
3232
- models/bedrock.md
3333
- models/cohere.md
34+
- models/openrouter.md
3435
- models/groq.md
3536
- models/mistral.md
3637
- models/huggingface.md

pydantic_ai_slim/pydantic_ai/messages.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,12 @@ class ThinkingPart:
10631063
part_kind: Literal['thinking'] = 'thinking'
10641064
"""Part type identifier, this is available on all parts as a discriminator."""
10651065

1066+
provider_details: dict[str, Any] | None = None
1067+
"""Additional provider-specific details in a serializable format.
1068+
1069+
This allows storing selected vendor-specific data that isn't mapped to standard ThinkingPart fields.
1070+
"""
1071+
10661072
def has_content(self) -> bool:
10671073
"""Return `True` if the thinking content is non-empty."""
10681074
return bool(self.content)

pydantic_ai_slim/pydantic_ai/models/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,6 @@ def infer_model( # noqa: C901
807807
'heroku',
808808
'moonshotai',
809809
'ollama',
810-
'openrouter',
811810
'together',
812811
'vercel',
813812
'litellm',
@@ -838,6 +837,10 @@ def infer_model( # noqa: C901
838837
from .cohere import CohereModel
839838

840839
return CohereModel(model_name, provider=provider)
840+
elif model_kind == 'openrouter':
841+
from .openrouter import OpenRouterModel
842+
843+
return OpenRouterModel(model_name, provider=provider)
841844
elif model_kind == 'mistral':
842845
from .mistral import MistralModel
843846

0 commit comments

Comments
 (0)