How to Migrate from Direct Provider SDK Calls to aisuite's Unified API
Migrate from vendor-specific SDK calls to aisuite's unified API by replacing the provider client with aisuite.Client(), prefixing your model name with the provider identifier (e.g., openai:gpt-4o), and using the same OpenAI-style request parameters across all providers.
The aisuite library from Andrew Ng's team eliminates the friction of juggling multiple LLM SDKs. Instead of importing openai, anthropic, google.generativeai, and others, you install one package and call any supported model through a single, consistent interface. This article walks through the architectural concepts, concrete migration steps, and code patterns you need to transition your existing codebase.
Why aisuite Exists: The Provider Adapter Architecture
Under the hood, aisuite implements a provider adapter pattern that bridges the gap between vendor-specific APIs and a universal Chat Completions interface.
Each supported LLM provider has a lightweight adapter class that follows strict naming conventions:
- File naming:
<provider>_provider.py(e.g.,openai_provider.py,anthropic_provider.py) - Class naming:
<Provider>Provider(e.g.,OpenaiProvider,AnthropicProvider)
These adapters are discovered automatically by the framework's dynamic loading system. As seen in the legacy interface at [aisuite/framework/provider_interface.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/framework/provider_interface.py), every provider implements a standard contract for chat completion requests.
The core abstraction lives in [aisuite/provider.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py), which defines the base Provider class that all concrete adapters extend. This architecture means aisuite can normalize request parameters, response formats, and even tool-calling behavior across dozens of LLM vendors.
Step-by-Step SDK Migration Process
Step 1: Install aisuite with Your Required Provider Dependencies
aisuite uses an optional dependency model—you only install the SDKs you actually need.
# OpenAI only
pip install "aisuite[openai]"
# Multiple providers
pip install "aisuite[openai,anthropic,google]"
# All supported providers
pip install "aisuite[all]"
The client imports provider SDKs lazily, so you won't pay the import cost for providers you don't use.
Step 2: Replace the SDK Client with aisuite.Client()
This is the core migration change. Instead of instantiating a provider-specific client, you create a single aisuite.Client() instance.
Before: Direct OpenAI SDK
import openai
client = openai.OpenAI(api_key="sk-...")
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in one sentence."},
]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
temperature=0.7,
max_tokens=100,
)
print(response.choices[0].message.content)
After: aisuite Unified API
import aisuite as ai
client = ai.Client() # Reads API keys from environment variables
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in one sentence."},
]
response = client.chat.completions.create(
model="openai:gpt-4o", # Provider prefix + model name
messages=messages,
temperature=0.7,
max_tokens=100,
)
print(response.choices[0].message.content)
The key differences:
- Remove provider-specific imports and client initialization
- Prefix the model identifier with
<provider>:(e.g.,openai:,anthropic:,google:) - The
aisuite.Client()automatically reads API keys from standard environment variables (OPENAI_API_KEY,ANTHROPIC_API_KEY, etc.)
Step 3: Leverage Automatic Parameter Normalization
One of aisuite's most valuable features is parameter mapping across providers. Different LLM APIs use different parameter names and value ranges. The [aisuite/framework/parameter_mapper.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/framework/parameter_mapper.py) module handles these translations automatically.
| OpenAI Parameter | Anthropic Equivalent | Google Equivalent |
|---|---|---|
temperature |
temperature |
temperature |
max_tokens |
max_tokens |
max_output_tokens |
top_p |
top_p |
top_p |
stop |
stop_sequences |
stop_sequences |
With aisuite, you always use the OpenAI-style parameter names, and the adapter translates them to provider-native equivalents. This means the same code works across providers without modification:
# Identical request shape for any provider
params = {
"messages": messages,
"temperature": 0.7,
"max_tokens": 500,
"top_p": 0.9,
}
# Works with OpenAI
openai_resp = client.chat.completions.create(model="openai:gpt-4o", **params)
# Works with Anthropic—same parameters, no translation needed
anthropic_resp = client.chat.completions.create(
model="anthropic:claude-3-5-sonnet-20240620",
**params
)
# Works with Google Gemini
google_resp = client.chat.completions.create(
model="google:gemini-1.5-pro",
**params
)
Migrating Tool Calling and Function Execution
Direct SDK implementations require manual parsing of tool_calls from responses, separate function execution, and result injection back into the conversation. aisuite's Agents API automates this entire flow.
Before: Manual Tool Calling with OpenAI SDK
import openai
import json
client = openai.OpenAI()
def get_weather(city: str) -> str:
"""Get weather for a city."""
# Implementation omitted
return f"Sunny and 72°F in {city}"
messages = [
{"role": "user", "content": "What's the weather in San Francisco?"}
]
# Define tool schema manually
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}]
# First API call
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
)
message = response.choices[0].message
# Manual tool call handling
if message.tool_calls:
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
# Execute function
result = get_weather(**function_args)
# Add tool result to messages
messages.append(message.model_dump())
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
# Second API call with results
final_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
print(final_response.choices[0].message.content)
After: aisuite Automatic Tool Execution
import aisuite as ai
client = ai.Client()
def get_weather(city: str) -> str:
"""Get weather for a city."""
return f"Sunny and 72°F in {city}"
messages = [
{"role": "user", "content": "What's the weather in San Francisco?"}
]
# Pass Python function directly—aisuite auto-generates schema
response = client.chat.completions.create(
model="openai:gpt-4o",
messages=messages,
tools=[get_weather], # List of Python functions
max_turns=2, # Let aisuite handle the full tool loop
)
print(response.choices[0].message.content)
The conversion from Python callable to OpenAI-style tool specification happens in [aisuite/utils/tools.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/utils/tools.py), which uses docstring parsing and type hints to generate accurate JSON schemas.
Handling Streaming Responses
Streaming migration follows the same pattern—replace the provider client and model identifier:
import aisuite as ai
client = ai.Client()
stream = client.chat.completions.create(
model="anthropic:claude-3-5-sonnet-20240620",
messages=[{"role": "user", "content": "Count to 10"}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
The streaming interface returns the same ChatCompletionChunk objects regardless of provider, so post-processing code requires no changes when switching between models.
Environment Configuration and API Keys
aisuite follows the convention-over-configuration principle for authentication:
| Provider | Environment Variable | Automatic Detection |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
Yes |
| Anthropic | ANTHROPIC_API_KEY |
Yes |
GOOGLE_API_KEY |
Yes | |
| Azure OpenAI | AZURE_OPENAI_KEY, AZURE_OPENAI_ENDPOINT |
Yes |
| AWS Bedrock | AWS credential chain | Yes |
For explicit control, you can pass configuration via the Client constructor:
client = ai.Client({
"openai": {"api_key": "sk-...", "timeout": 60},
"anthropic": {"api_key": "sk-ant-...", "max_retries": 3},
})
Summary
- Install aisuite with provider-specific extras to control dependencies
- Replace
openai.Client()withai.Client()and prefix models with<provider>: - Use OpenAI-style parameters everywhere—aisuite's
ParameterMapperhandles translation - Pass Python functions directly to
tools=for automatic schema generation and execution - Leverage
max_turnsto let aisuite manage multi-turn tool conversations
The provider adapter architecture in [aisuite/provider.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) and parameter normalization in [aisuite/framework/parameter_mapper.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/framework/parameter_mapper.py) make this unified interface possible across all supported LLM vendors.
Frequently Asked Questions
How do I know which providers and models are supported?
Check the [README.md](https://github.com/andrewyng/aisuite/blob/main/README.md) for the current list of supported providers. The provider discovery system dynamically loads any adapter following the <provider>_provider.py naming convention, so new providers can be added without core library changes.
Can I use aisuite with my existing fine-tuned or deployed models?
Yes. Any model accessible through a provider's standard API works with the unified interface. For custom deployments (e.g., Azure OpenAI with your own endpoint), configure the Client with explicit provider settings including the base URL and deployment name.
What happens if a provider doesn't support a specific parameter?
The ParameterMapper silently drops or transforms unsupported parameters rather than raising errors. For example, if you pass tools to a model without tool support, aisuite returns a standard completion without tool-related fields. Check provider-specific documentation for capability matrix details.
Is there performance overhead from the abstraction layer?
Minimal. The adapter layer adds approximately 1-2ms of Python overhead per request—negligible compared to network latency. The lazy SDK loading in [aisuite/provider.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) ensures you only pay the import cost for providers you actually use.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →