Skip to content

Commit 98ca212

Browse files
authored
Add unified provider interface with string-based initialization (#1490)
This allows users to be able to initialise instructor using strings
1 parent 865074a commit 98ca212

File tree

22 files changed

+1253
-108
lines changed

22 files changed

+1253
-108
lines changed

.github/workflows/test.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
if: matrix.python-version != '3.11'
2828
run: uv run pytest tests/ -k 'not llm and not openai and not gemini and not anthropic and not cohere and not vertexai and not mistral'
2929
env:
30+
INSTRUCTOR_ENV: CI
3031
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
3132
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
3233
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
@@ -37,12 +38,22 @@ jobs:
3738
env:
3839
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
3940

41+
- name: Run Auto Client
42+
if: matrix.python-version != '3.11'
43+
run: uv run pytest tests/test_auto_client.py
44+
env:
45+
INSTRUCTOR_ENV: CI
46+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
47+
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
48+
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
49+
4050
- name: Generate coverage report
4151
if: matrix.python-version == '3.11'
4252
run: |
4353
uv run coverage run -m pytest tests/ -k "not docs and not anthropic and not gemini and not cohere and not vertexai and not fireworks and not perplexity and not mistral"
4454
uv run coverage report
4555
uv run coverage html
4656
env:
57+
INSTRUCTOR_ENV: CI
4758
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
4859
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ print(user_info.age)
5757
#> 30
5858
```
5959

60+
### Provider Initialization
61+
62+
Instructor provides a simple way to work with different providers using a consistent interface:
63+
64+
```python
65+
import instructor
66+
from pydantic import BaseModel
67+
68+
class UserInfo(BaseModel):
69+
name: str
70+
age: int
71+
72+
# Initialize client for any supported provider
73+
client = instructor.from_provider("openai/gpt-4") # OpenAI
74+
client = instructor.from_provider("anthropic/claude-3-sonnet") # Anthropic
75+
client = instructor.from_provider("google/gemini-pro") # Google
76+
client = instructor.from_provider("mistral/mistral-large") # Mistral
77+
# ... and many more providers
78+
79+
# Use the same interface across all providers
80+
user_info = client.chat.completions.create(
81+
response_model=UserInfo,
82+
messages=[{"role": "user", "content": "John Doe is 30 years old."}],
83+
)
84+
```
85+
86+
The `from_provider` function supports both synchronous and asynchronous usage with `async_client=True`, and works with all supported providers including OpenAI, Anthropic, Google, Mistral, Cohere, Perplexity, Groq, Writer, AWS Bedrock, Cerebras, Fireworks, Vertex AI, and more.
87+
6088
### Using Hooks
6189

6290
Instructor provides a powerful hooks system that allows you to intercept and log various stages of the LLM interaction process. Here's a simple example demonstrating how to use hooks:

docs/blog/index.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ If you want to get updates on new features and tips on how to use Instructor, yo
2121

2222
## Advanced Topics
2323

24-
1. [Instructor Implements llms.txt](posts/llms-txt-adoption.md)
25-
2. [Query Understanding: Beyond Embeddings](posts/rag-and-beyond.md)
26-
3. [Achieving GPT-4 Level Summaries with GPT-3.5-turbo](posts/chain-of-density.md)
27-
4. [Basics of Guardrails and Validation in AI Models](posts/validation-part1.md)
28-
5. [Validating Citations in AI-Generated Content](posts/citations.md)
29-
6. [Fine-tuning and Distillation in AI Models](posts/distilation-part1.md)
30-
7. [Enhancing OpenAI Client Observability with LangSmith](posts/langsmith.md)
31-
8. [Logfire Integration with Pydantic](posts/logfire.md)
24+
1. [Unified Provider Interface in Instructor](posts/unified-provider.md)
25+
2. [Instructor Implements llms.txt](posts/llms-txt-adoption.md)
26+
3. [Query Understanding: Beyond Embeddings](posts/rag-and-beyond.md)
27+
4. [Achieving GPT-4 Level Summaries with GPT-3.5-turbo](posts/chain-of-density.md)
28+
5. [Basics of Guardrails and Validation in AI Models](posts/validation-part1.md)
29+
6. [Validating Citations in AI-Generated Content](posts/citations.md)
30+
7. [Fine-tuning and Distillation in AI Models](posts/distilation-part1.md)
31+
8. [Enhancing OpenAI Client Observability with LangSmith](posts/langsmith.md)
32+
9. [Logfire Integration with Pydantic](posts/logfire.md)
3233

3334
## AI Development and Optimization
3435

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
---
2+
draft: false
3+
date: 2024-04-20
4+
authors:
5+
- jxnl
6+
categories:
7+
- Tutorial
8+
---
9+
10+
# Unified Provider Interface with String-Based Initialization
11+
12+
Instructor now offers a simplified way to initialize any supported LLM provider with a single consistent interface. This approach makes it easier than ever to switch between different LLM providers while maintaining the same structured output functionality you rely on.
13+
14+
## The Problem
15+
16+
As the number of LLM providers grows, so does the complexity of initializing and working with different client libraries. Each provider has its own initialization patterns, API structures, and quirks. This leads to code that isn't portable between providers and requires significant refactoring when you want to try a new model.
17+
18+
## The Solution: String-Based Initialization
19+
20+
We've introduced a new unified interface that allows you to initialize any supported provider with a simple string format:
21+
22+
```python
23+
import instructor
24+
from pydantic import BaseModel
25+
26+
class UserInfo(BaseModel):
27+
name: str
28+
age: int
29+
30+
# Initialize any provider with a single consistent interface
31+
client = instructor.from_provider("openai/gpt-4")
32+
client = instructor.from_provider("anthropic/claude-3-sonnet")
33+
client = instructor.from_provider("google/gemini-pro")
34+
client = instructor.from_provider("mistral/mistral-large")
35+
```
36+
37+
The `from_provider` function takes a string in the format `"provider/model-name"` and handles all the details of setting up the appropriate client with the right model. This provides several key benefits:
38+
39+
- **Simplified Initialization**: No need to manually create provider-specific clients
40+
- **Consistent Interface**: Same syntax works across all providers
41+
- **Reduced Dependency Exposure**: You don't need to import specific provider libraries in your application code
42+
- **Easy Experimentation**: Switch between providers with a single line change
43+
44+
## Supported Providers
45+
46+
The string-based initialization currently supports all major providers in the ecosystem:
47+
48+
- OpenAI: `"openai/gpt-4"`, `"openai/gpt-4o"`, `"openai/gpt-3.5-turbo"`
49+
- Anthropic: `"anthropic/claude-3-opus-20240229"`, `"anthropic/claude-3-sonnet-20240229"`, `"anthropic/claude-3-haiku-20240307"`
50+
- Google Gemini: `"google/gemini-pro"`, `"google/gemini-pro-vision"`
51+
- Mistral: `"mistral/mistral-small-latest"`, `"mistral/mistral-medium-latest"`, `"mistral/mistral-large-latest"`
52+
- Cohere: `"cohere/command"`, `"cohere/command-r"`, `"cohere/command-light"`
53+
- Perplexity: `"perplexity/sonar-small-online"`, `"perplexity/sonar-medium-online"`
54+
- Groq: `"groq/llama2-70b-4096"`, `"groq/mixtral-8x7b-32768"`, `"groq/gemma-7b-it"`
55+
- Writer: `"writer/palmyra-instruct"`, `"writer/palmyra-instruct-v2"`
56+
- AWS Bedrock: `"bedrock/anthropic.claude-v2"`, `"bedrock/amazon.titan-text-express-v1"`
57+
- Cerebras: `"cerebras/cerebras-gpt"`, `"cerebras/cerebras-gpt-2.7b"`
58+
- Fireworks: `"fireworks/llama-v2-70b"`, `"fireworks/firellama-13b"`
59+
- Vertex AI: `"vertexai/gemini-pro"`, `"vertexai/text-bison"`
60+
- Google GenAI: `"genai/gemini-pro"`, `"genai/gemini-pro-vision"`
61+
62+
Each provider will be initialized with sensible defaults, but you can also pass additional keyword arguments to customize the configuration. For model-specific details, consult each provider's documentation.
63+
64+
## Async Support
65+
66+
The unified interface fully supports both synchronous and asynchronous clients:
67+
68+
```python
69+
# Synchronous client (default)
70+
client = instructor.from_provider("openai/gpt-4")
71+
72+
# Asynchronous client
73+
async_client = instructor.from_provider("anthropic/claude-3-sonnet", async_client=True)
74+
75+
# Use like any other async client
76+
response = await async_client.chat.completions.create(
77+
response_model=UserInfo,
78+
messages=[{"role": "user", "content": "Extract information about John who is 30 years old"}]
79+
)
80+
```
81+
82+
## Mode Selection
83+
84+
You can also specify which structured output mode to use with the provider:
85+
86+
```python
87+
import instructor
88+
from instructor import Mode
89+
90+
# Override the default mode for a provider
91+
client = instructor.from_provider(
92+
"anthropic/claude-3-sonnet",
93+
mode=Mode.ANTHROPIC_TOOLS
94+
)
95+
96+
# Use JSON mode instead of the default tools mode
97+
client = instructor.from_provider(
98+
"mistral/mistral-large",
99+
mode=Mode.MISTRAL_STRUCTURED_OUTPUTS
100+
)
101+
102+
# Use reasoning tools instead of regular tools for Anthropic
103+
client = instructor.from_provider(
104+
"anthropic/claude-3-opus",
105+
mode=Mode.ANTHROPIC_REASONING_TOOLS
106+
)
107+
```
108+
109+
If not specified, each provider will use its recommended default mode:
110+
111+
- OpenAI: `Mode.OPENAI_FUNCTIONS`
112+
- Anthropic: `Mode.ANTHROPIC_TOOLS`
113+
- Google Gemini: `Mode.GEMINI_JSON`
114+
- Mistral: `Mode.MISTRAL_TOOLS`
115+
- Cohere: `Mode.COHERE_TOOLS`
116+
- Perplexity: `Mode.JSON`
117+
- Groq: `Mode.GROQ_TOOLS`
118+
- Writer: `Mode.WRITER_JSON`
119+
- Bedrock: `Mode.ANTHROPIC_TOOLS` (for Claude on Bedrock)
120+
- Vertex AI: `Mode.VERTEXAI_TOOLS`
121+
122+
You can always customize this based on your specific needs and model capabilities.
123+
124+
## Error Handling
125+
126+
The `from_provider` function includes robust error handling to help you quickly identify and fix issues:
127+
128+
```python
129+
# Missing dependency
130+
try:
131+
client = instructor.from_provider("anthropic/claude-3-sonnet")
132+
except ImportError as e:
133+
print("Error: Install the anthropic package first")
134+
# pip install anthropic
135+
136+
# Invalid provider format
137+
try:
138+
client = instructor.from_provider("invalid-format")
139+
except ValueError as e:
140+
print(e) # Model string must be in format "provider/model-name"
141+
142+
# Unsupported provider
143+
try:
144+
client = instructor.from_provider("unknown/model")
145+
except ValueError as e:
146+
print(e) # Unsupported provider: unknown. Supported providers are: ...
147+
```
148+
149+
The function validates the provider string format, checks if the provider is supported, and ensures the necessary packages are installed.
150+
151+
## Environment Variables
152+
153+
Like the native client libraries, `from_provider` respects environment variables set for each provider:
154+
155+
```python
156+
# Set environment variables
157+
import os
158+
os.environ["OPENAI_API_KEY"] = "your-openai-key"
159+
os.environ["ANTHROPIC_API_KEY"] = "your-anthropic-key"
160+
os.environ["MISTRAL_API_KEY"] = "your-mistral-key"
161+
162+
# No need to pass API keys directly
163+
client = instructor.from_provider("openai/gpt-4")
164+
```
165+
166+
## Troubleshooting
167+
168+
Here are some common issues and solutions when using the unified provider interface:
169+
170+
### Model Not Found Errors
171+
172+
If you receive a 404 error, check that you're using the correct model name format:
173+
174+
```
175+
Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-3-haiku'}}
176+
```
177+
178+
For Anthropic models, always include the version date:
179+
- ✅ Correct: `anthropic/claude-3-haiku-20240307`
180+
- ❌ Incorrect: `anthropic/claude-3-haiku`
181+
182+
### Provider-Specific Parameters
183+
184+
Some providers require specific parameters for API calls:
185+
186+
```python
187+
# Anthropic requires max_tokens
188+
anthropic_client = instructor.from_provider(
189+
"anthropic/claude-3-haiku-20240307",
190+
max_tokens=400 # Required for Anthropic
191+
)
192+
193+
# Use models with vision capabilities for multimodal content
194+
gemini_client = instructor.from_provider(
195+
"google/gemini-pro-vision" # Required for image processing
196+
)
197+
```
198+
199+
### Working Example
200+
201+
Here's a complete example that demonstrates the automodel functionality with multiple providers:
202+
203+
```python
204+
import os
205+
import asyncio
206+
import instructor
207+
from pydantic import BaseModel, Field
208+
209+
class UserInfo(BaseModel):
210+
"""User information extraction model."""
211+
name: str = Field(description="The user's full name")
212+
age: int = Field(description="The user's age in years")
213+
occupation: str = Field(description="The user's job or profession")
214+
215+
async def main():
216+
# Test OpenAI
217+
openai_client = instructor.from_provider("openai/gpt-3.5-turbo")
218+
openai_result = openai_client.chat.completions.create(
219+
response_model=UserInfo,
220+
messages=[{"role": "user", "content": "Jane Doe is a 28-year-old data scientist."}]
221+
)
222+
print(f"OpenAI result: {openai_result.model_dump()}")
223+
224+
# Test Anthropic with async client
225+
if os.environ.get("ANTHROPIC_API_KEY"):
226+
anthropic_client = instructor.from_provider(
227+
model="anthropic/claude-3-haiku-20240307",
228+
async_client=True,
229+
max_tokens=400 # Required for Anthropic
230+
)
231+
anthropic_result = await anthropic_client.chat.completions.create(
232+
response_model=UserInfo,
233+
messages=[{"role": "user", "content": "John Smith is a 35-year-old software engineer."}]
234+
)
235+
print(f"Anthropic result: {anthropic_result.model_dump()}")
236+
237+
if __name__ == "__main__":
238+
asyncio.run(main())
239+
```
240+
241+
## Conclusion
242+
243+
String-based initialization is a significant step toward making Instructor even more user-friendly and flexible. It reduces the learning curve for working with multiple providers and makes it easier than ever to experiment with different models.
244+
245+
Benefits include:
246+
- Simplified initialization with a consistent interface
247+
- Automatic selection of appropriate default modes
248+
- Support for both synchronous and asynchronous clients
249+
- Clear error messages to quickly identify issues
250+
- Respect for provider-specific environment variables
251+
- Comprehensive model selection across the entire LLM ecosystem
252+
253+
Whether you're building a new application or migrating an existing one, the unified provider interface offers a cleaner, more maintainable way to work with structured outputs across the LLM ecosystem.
254+
255+
Try it today with `instructor.from_provider()` and check out the [complete example code](https://github.com/instructor-ai/instructor/tree/main/examples/automodel) in our repository!

docs/integrations/index.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,50 @@ See the [Modes Comparison](../modes-comparison.md) guide for details.
8484

8585
## Getting Started
8686

87-
To use a provider with Instructor:
87+
There are two ways to use providers with Instructor:
88+
89+
### 1. Using Provider Initialization (Recommended)
90+
91+
The simplest way to get started is using the provider initialization:
92+
93+
```python
94+
import instructor
95+
from pydantic import BaseModel
96+
97+
class UserInfo(BaseModel):
98+
name: str
99+
age: int
100+
101+
# Initialize any provider with a simple string
102+
client = instructor.from_provider("openai/gpt-4")
103+
# Or use async client
104+
async_client = instructor.from_provider("anthropic/claude-3-sonnet", async_client=True)
105+
106+
# Use the same interface for all providers
107+
response = client.chat.completions.create(
108+
response_model=UserInfo,
109+
messages=[{"role": "user", "content": "Your prompt"}]
110+
)
111+
```
112+
113+
Supported provider strings:
114+
- `openai/model-name`: OpenAI models
115+
- `anthropic/model-name`: Anthropic models
116+
- `google/model-name`: Google models
117+
- `mistral/model-name`: Mistral models
118+
- `cohere/model-name`: Cohere models
119+
- `perplexity/model-name`: Perplexity models
120+
- `groq/model-name`: Groq models
121+
- `writer/model-name`: Writer models
122+
- `bedrock/model-name`: AWS Bedrock models
123+
- `cerebras/model-name`: Cerebras models
124+
- `fireworks/model-name`: Fireworks models
125+
- `vertexai/model-name`: Vertex AI models
126+
- `genai/model-name`: Google GenAI models
127+
128+
### 2. Manual Client Setup
129+
130+
Alternatively, you can manually set up the client:
88131

89132
1. Install the required dependencies:
90133
```bash

0 commit comments

Comments
 (0)