Files
wehub-resource-sync 97e91a83f3
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:38 +08:00

271 lines
7.0 KiB
Markdown

---
title: "Structured outputs with OpenRouter, a complete guide with instructor"
description: "Learn how to use Instructor with OpenRouter to access multiple LLM providers through a unified API. Get type-safe, structured outputs from various models including Qwen, Gemini, Mistral, and Cohere."
---
# Structured outputs with OpenRouter, a complete guide with instructor
OpenRouter provides a unified API to access multiple LLM providers, allowing you to easily switch between different models. This guide shows you how to use Instructor with OpenRouter for type-safe, validated responses across various LLM providers.
To set Provider specific configuration on the `openai` client, make sure to use the `extra_body` kwarg.
## Quick Start
⚠️ **Important**: Make sure that the model you're using has support for `Tool Calling` and/or `Structured Outputs` in the [OpenRouter models listing](https://openrouter.ai/models)
Instructor works with OpenRouter through the OpenAI client, so you don't need to install anything extra beyond the base package.
## Simple User Example (Sync)
We support simple tool calling with this
```python
from openai import OpenAI
import instructor
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
client = instructor.from_provider(
"openrouter/google/gemini-2.0-flash-lite-001",
base_url="https://openrouter.ai/api/v1",
async_client=False
)
resp = client.create(
messages=[
{
"role": "user",
"content": "Ivan is 28 years old",
},
],
response_model=User,
extra_body={"provider": {"require_parameters": True}},
)
print(resp)
#> name='Ivan' age=20
```
## Simple User Example ( Async )
```python
import instructor
from pydantic import BaseModel
import asyncio
class User(BaseModel):
name: str
age: int
client = instructor.from_provider(
"openrouter/google/gemini-2.0-flash-lite-001",
async_client=True,
)
async def extract_user():
user = await client.create(
messages=[
{"role": "user", "content": "Extract: Jason is 25 years old"},
],
response_model=User,
extra_body={"provider": {"require_parameters": True}},
)
return user
# Run async function
user = asyncio.run(extract_user())
print(user)
```
## Nested Object Example ( Sync )
```python
from pydantic import BaseModel
from openai import OpenAI
import instructor
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
country: str
class User(BaseModel):
name: str
age: int
addresses: list[Address]
# Initialize with API key
# Initialize client with base URL
client = instructor.from_provider(
"openrouter/google/gemini-2.0-flash-lite-001",
base_url="https://openrouter.ai/api/v1",
async_client=False
)
# Create structured output with nested objects
user = client.create(
messages=[
{
"role": "user",
"content": """
Extract: Jason is 25 years old.
He lives at 123 Main St, New York, USA
and has a summer house at 456 Beach Rd, Miami, USA
""",
},
],
extra_body={"provider": {"require_parameters": True}},
response_model=User,
)
print(user)
#> name='Jason' age=25 addresses=[Address(street='123 Main St', city='New York', country='USA'), Address(street='456 Beach Rd', city='Miami', country='USA')]
```
## Structured Outputs (Sync)
⚠️ **Important**: Check that your chosen model supports `Structured Outputs` in the [OpenRouter models listing](https://openrouter.ai/models). Structured Outputs is a subset of Tool Calling that constrains the model's output to match your schema in order to produce valid JSON Schema.
Instructor also supports Structured Outputs with OpenRouter as documented in their API [here](https://openrouter.ai/docs/features/structured-outputs). Note that the following User model will throw an error if we use the OpenAI GPT-4o model like `openai/gpt-4o-2024-11-20` because OpenAI does not support using a regex pattern as part of their structured output schema.
```python
from pydantic import BaseModel, Field
from openai import OpenAI
import instructor
class User(BaseModel):
name: str
age: int
phone_number: str = Field(
pattern=r"^\+?1?\s*\(?(\d{3})\)?[-.\s]*(\d{3})[-.\s]*(\d{4})$"
)
# Initialize with API key
# Initialize client with base URL
client = instructor.from_provider(
"openrouter/google/gemini-2.0-flash-lite-001",
base_url="https://openrouter.ai/api/v1",
async_client=False
)
# Create structured output with nested objects
user = client.create(
messages=[
{
"role": "user",
"content": """
Extract: Jason is 25 years old and his number is 1-212-456-7890
""",
},
],
response_model=User,
extra_body={"provider": {"require_parameters": True}},
)
print(user)
# > name='Jason' age=25 phone_number='+1 (212) 456-7890'
```
## JSON Mode
In the event that your model doesn't support tool calling, you will see the following error when you try to use `mode.TOOLS`
> instructor.exceptions.InstructorRetryException: Error code: 404 - {'error': {'message': 'No endpoints found that support tool use. To learn more about provider routing, visit: https://openrouter.ai/docs/provider-routing', 'code': 404}}
In this case, we recommend using the `JSON` mode instead as seen below.
```python
from pydantic import BaseModel, Field
from openai import OpenAI
import instructor
class User(BaseModel):
name: str
age: int
phone_number: str = Field(
pattern=r"^\+?1?\s*\(?(\d{3})\)?[-.\s]*(\d{3})[-.\s]*(\d{4})$"
)
# Initialize with API key
# Initialize client with base URL
client = instructor.from_provider(
"openrouter/google/gemini-2.0-flash-lite-001",
base_url="https://openrouter.ai/api/v1",
async_client=False
)
# Create structured output with nested objects
user = client.create(
messages=[
{
"role": "user",
"content": """
Extract: Jason is 25 years old and his number is 1-212-456-7890
""",
},
],
response_model=User,
)
print(user)
```
## Streaming
You can also use streaming with as seen below using the `create_partial` method. While we're using JSON mode here, this should work with tool calling and structured outputs too.
```python
from pydantic import BaseModel, Field
from openai import OpenAI
import instructor
class User(BaseModel):
name: str
age: int
# Initialize with API key
# Initialize client with base URL
client = instructor.from_provider(
"openrouter/google/gemini-2.0-flash-lite-001",
base_url="https://openrouter.ai/api/v1",
)
# Create structured output with nested objects
user = client.create_partial(
messages=[
{
"role": "user",
"content": """
Extract: Jason is 25 years old and his number is 1-212-456-7890
""",
},
],
response_model=User,
)
for chunk in user:
print(chunk)
# > name=None age=None
# > name='Jason' age=None
# > name='Jason' age=25
```