Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [Unreleased]

### Added
- **Requesty**: Add [Requesty](https://requesty.ai) as a provider (`from_requesty` / `from_provider("requesty/...")`), an OpenAI-compatible LLM router, mirroring the existing OpenRouter provider.

### Fixed
- **v2 message handling**: Preserve caller-owned message lists and nested content across request preparation and retries for OpenAI-compatible, Cohere, Mistral, OpenRouter, Writer, and xAI handlers. ([#2417](https://github.com/567-labs/instructor/issues/2417), [#2428](https://github.com/567-labs/instructor/issues/2428))
- **v2 JSON extraction**: Prefer the final complete top-level JSON value in text responses and retain every JSON object when multiple objects arrive in one streaming chunk.
Expand Down
132 changes: 132 additions & 0 deletions docs/integrations/requesty.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: "Structured outputs with Requesty, a complete guide with instructor"
description: "Learn how to use Instructor with Requesty to access multiple LLM providers through a unified, OpenAI-compatible API. Get type-safe, structured outputs from various models."
---

# Structured outputs with Requesty, a complete guide with instructor

[Requesty](https://requesty.ai) provides a unified, OpenAI-compatible API to access 400+ LLM models through a single endpoint, with smart routing, fallbacks, caching, and cost tracking. This guide shows you how to use Instructor with Requesty for type-safe, validated responses across various LLM providers.

## Quick Start

Instructor works with Requesty through the OpenAI client, so you don't need to install anything extra beyond the base package.

```bash
pip install instructor
```

You'll need a Requesty API key, which you can create at [app.requesty.ai/api-keys](https://app.requesty.ai/api-keys). Set it as the `REQUESTY_API_KEY` environment variable.

```bash
export REQUESTY_API_KEY="your-api-key"
```

Requesty uses `provider/model` naming (e.g. `openai/gpt-4o-mini`, `anthropic/claude-sonnet-4-5`, `google/gemini-2.5-flash`).

## Simple User Example (Sync)

```python
import instructor
from pydantic import BaseModel


class User(BaseModel):
name: str
age: int


client = instructor.from_provider("requesty/openai/gpt-4o-mini")

resp = client.chat.completions.create(
messages=[
{
"role": "user",
"content": "Ivan is 28 years old",
},
],
response_model=User,
)

print(resp)
#> name='Ivan' age=28
```

## Simple User Example (Async)

```python
import asyncio
import instructor
from pydantic import BaseModel


class User(BaseModel):
name: str
age: int


client = instructor.from_provider("requesty/openai/gpt-4o-mini", async_client=True)


async def extract_user():
return await client.chat.completions.create(
messages=[
{
"role": "user",
"content": "Ivan is 28 years old",
},
],
response_model=User,
)


user = asyncio.run(extract_user())
print(user)
#> name='Ivan' age=28
```

## Using the OpenAI client directly

You can also point the OpenAI client at Requesty explicitly:

```python
from openai import OpenAI
import instructor
from pydantic import BaseModel


class User(BaseModel):
name: str
age: int


client = instructor.from_openai(
OpenAI(
base_url="https://router.requesty.ai/v1",
api_key="<your-requesty-api-key>",
)
)

resp = client.chat.completions.create(
messages=[{"role": "user", "content": "Ivan is 28 years old"}],
response_model=User,
)

print(resp)
#> name='Ivan' age=28
```

## Supported Modes

Requesty supports the standard OpenAI-compatible modes:

- `Mode.TOOLS` — tool/function calling (default)
- `Mode.JSON` — JSON output
- `Mode.MD_JSON` — Markdown-fenced JSON
- `Mode.PARALLEL_TOOLS` — parallel tool calls

## Related Documentation

- [OpenAI](./openai.md): The base OpenAI integration
- [OpenRouter](./openrouter.md): Another unified LLM router

The [Requesty models listing](https://app.requesty.ai/router/list) shows the full catalogue. Keep in mind that some models do not support `json_schema` response formats.
42 changes: 42 additions & 0 deletions examples/open_source_examples/requesty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import instructor
from pydantic import BaseModel, Field
from typing import Optional
from instructor import Maybe

# Requesty (https://requesty.ai) is an OpenAI-compatible LLM router.
# Set your API key: export REQUESTY_API_KEY="your-api-key"
assert os.environ.get("REQUESTY_API_KEY"), (
"REQUESTY_API_KEY is not set in environment variables"
)

# Requesty uses provider/model naming, e.g. openai/gpt-4o-mini
client = instructor.from_provider("requesty/openai/gpt-4o-mini")

data = [
"Brandon is 33 years old. He works as a solution architect.",
"Jason is 25 years old. He is the GOAT.",
"Dominic is 45 years old. He is retired.",
"Jenny is 72. She is a wife and a CEO.",
"Holly is 22. She is an explorer.",
]


class UserDetail(BaseModel):
age: int
name: str
occupation: Optional[str] = Field(
default=None, description="The person's occupation, if mentioned"
)


MaybeUser = Maybe(UserDetail)

for text in data:
user = client.chat.completions.create(
response_model=MaybeUser,
messages=[
{"role": "user", "content": f"Extract the user details: {text}"},
],
)
print(user)
3 changes: 3 additions & 0 deletions instructor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
from_together as from_together,
)
from .v2.providers.openrouter.client import from_openrouter as from_openrouter
from .v2.providers.requesty.client import from_requesty as from_requesty
from .v2.providers.perplexity.client import from_perplexity as from_perplexity
from .v2.providers.vertexai.client import from_vertexai as from_vertexai
from .v2.providers.writer.client import from_writer as from_writer
Expand All @@ -78,6 +79,7 @@
"from_databricks",
"from_deepseek",
"from_openrouter",
"from_requesty",
"from_litellm",
"from_vertexai",
"from_provider",
Expand Down Expand Up @@ -117,6 +119,7 @@
"from_databricks": (".v2.providers.openai.client", "from_databricks"),
"from_deepseek": (".v2.providers.openai.client", "from_deepseek"),
"from_openrouter": (".v2.providers.openrouter.client", "from_openrouter"),
"from_requesty": (".v2.providers.requesty.client", "from_requesty"),
"from_litellm": (".v2.providers.litellm.client", "from_litellm"),
"Mode": (".mode", "Mode"),
"patch": (".core.patch", "patch"),
Expand Down
65 changes: 65 additions & 0 deletions instructor/v2/auto_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,70 @@ def _build_openrouter(
raise


def _build_requesty(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import openai
from instructor.v2.providers.requesty.client import from_requesty
import os

# Get API key from kwargs or environment
api_key = api_key or os.environ.get("REQUESTY_API_KEY")

if not api_key:
from instructor.v2.core.errors import ConfigurationError

raise ConfigurationError(
"REQUESTY_API_KEY is not set. "
"Set it with `export REQUESTY_API_KEY=<your-api-key>` or pass it as kwarg api_key=<your-api-key>"
)

# Requesty uses an OpenAI-compatible API
base_url = kwargs.pop("base_url", "https://router.requesty.ai/v1")

client = (
openai.AsyncOpenAI(api_key=api_key, base_url=base_url)
if async_client
else openai.OpenAI(api_key=api_key, base_url=base_url)
)

result = from_requesty(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError

raise ConfigurationError(
"The openai package is required to use the Requesty provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise


def _build_litellm(
*,
provider: str,
Expand Down Expand Up @@ -1604,5 +1668,6 @@ def _build_litellm(
"deepseek": _build_deepseek,
"xai": _build_xai,
"openrouter": _build_openrouter,
"requesty": _build_requesty,
"litellm": _build_litellm,
}
20 changes: 20 additions & 0 deletions instructor/v2/core/provider_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,26 @@ def _openai_compat_spec(
client_module="instructor.v2.providers.openrouter.client",
sdk_module="openai",
),
Provider.REQUESTY: _spec(
Provider.REQUESTY,
aliases=("requesty",),
handler_module="instructor.v2.providers.requesty.handlers",
supported_modes=(
Mode.TOOLS,
Mode.JSON_SCHEMA,
Mode.MD_JSON,
Mode.PARALLEL_TOOLS,
),
unsupported_modes=(Mode.RESPONSES_TOOLS,),
legacy_modes={
Mode.FUNCTIONS: Mode.TOOLS,
Mode.TOOLS_STRICT: Mode.TOOLS,
Mode.JSON_O1: Mode.JSON_SCHEMA,
},
from_function="from_requesty",
client_module="instructor.v2.providers.requesty.client",
sdk_module="openai",
),
Provider.ANTHROPIC: _spec(
Provider.ANTHROPIC,
aliases=("anthropic",),
Expand Down
2 changes: 2 additions & 0 deletions instructor/v2/core/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Provider(Enum):
BEDROCK = "bedrock"
PERPLEXITY = "perplexity"
OPENROUTER = "openrouter"
REQUESTY = "requesty"


def provider_from_mode(mode: Mode, default: Provider = Provider.OPENAI) -> Provider:
Expand Down Expand Up @@ -117,6 +118,7 @@ def get_provider(base_url: str) -> Provider:
("localhost:11434", Provider.OLLAMA),
("litellm", Provider.LITELLM),
("openrouter", Provider.OPENROUTER),
("requesty", Provider.REQUESTY),
("x.ai", Provider.XAI),
("xai", Provider.XAI),
)
Expand Down
2 changes: 2 additions & 0 deletions instructor/v2/providers/openai/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
Provider.DATABRICKS,
Provider.DEEPSEEK,
Provider.OPENROUTER,
Provider.REQUESTY,
Provider.GROQ,
Provider.FIREWORKS,
Provider.CEREBRAS,
Expand All @@ -72,6 +73,7 @@
Provider.DATABRICKS,
Provider.DEEPSEEK,
Provider.OPENROUTER,
Provider.REQUESTY,
Provider.CEREBRAS,
]

Expand Down
6 changes: 6 additions & 0 deletions instructor/v2/providers/requesty/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Requesty v2 provider handlers and client."""

from .client import from_requesty
from .handlers import RequestyJSONSchemaHandler

__all__ = ["RequestyJSONSchemaHandler", "from_requesty"]
Loading