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:

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 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() with ai.Client() and prefix models with <provider>:
  • Use OpenAI-style parameters everywhere—aisuite's ParameterMapper handles translation
  • Pass Python functions directly to tools= for automatic schema generation and execution
  • Leverage max_turns to 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →