How to Add a Custom LLM Provider to aisuite

Adding a custom LLM provider to aisuite requires creating a new module in aisuite/providers/ that subclasses the abstract Provider class, implements the required chat_completions_create method, and follows the naming convention that ProviderFactory expects.

The aisuite library from Andrew Ng's team provides a unified interface for multiple LLM providers through a clean abstraction layer. All provider-specific logic is isolated behind an abstract Provider class defined in aisuite/provider.py, while the ProviderFactory handles dynamic instantiation based on simple string keys. This architecture makes it trivial to extend aisuite with custom providers without modifying any core library code.


Understanding the ProviderFactory Pattern

The ProviderFactory in aisuite/provider.py implements a convention-based discovery system that transforms provider names into fully instantiated objects. The factory operates on three principles:

Naming Convention

A provider key like "mycustom" maps automatically to:

Dynamic Import Process


# Simplified from ProviderFactory.create_provider()

module = importlib.import_module(f"aisuite.providers.{key}_provider")
provider_class = getattr(module, f"{key.title()}Provider")
return provider_class(**config)

Runtime Discovery

Supported providers are discovered by scanning the aisuite/providers directory for files matching *_provider.py. This means new providers are picked up automatically—no registration step required.


Step-by-Step: Creating a Custom Provider

Follow these four steps to add your own LLM provider to aisuite.

Step 1: Create the Provider Module

Create a new file in aisuite/providers/ following the naming convention. For a provider key "mycustom", the file must be named mycustom_provider.py.

Step 2: Subclass the Provider Base Class

Implement the required methods from the abstract Provider class:


# aisuite/providers/mycustom_provider.py

from aisuite.provider import Provider, LLMError
from typing import Union, BinaryIO, AsyncGenerator


class MycustomProvider(Provider):
    """Custom LLM provider implementing the aisuite Provider interface."""

    def __init__(self, **config):
        """
        Accept flexible **config so the factory can forward any settings.
        Common keys: api_key, base_url, timeout, model, etc.
        """
        super().__init__()  # Initializes optional audio container

        self.api_key = config.get("api_key")
        self.base_url = config.get("base_url", "https://api.mycustom.com/v1")
        
        # Initialize your SDK client here

        # self.client = MyCustomSDK(api_key=self.api_key, base_url=self.base_url)

    def chat_completions_create(self, model, messages, **kwargs):
        """
        Required: Synchronous chat completion.
        Must return an OpenAI-compatible response object with a `choices` attribute.
        """
        try:
            # Transform aisuite message format to provider format

            # provider_messages = self._transform_messages(messages)

            # response = self.client.chat.completions.create(

            #     model=model, messages=provider_messages, **kwargs

            # )

            
            # Mock response for illustration

            class MockResponse:
                def __init__(self, content):
                    self.choices = [{"message": {"role": "assistant", "content": content}}]
            
            return MockResponse(content="Response from custom provider")
            
        except Exception as exc:
            raise LLMError(f"Mycustom provider error: {exc}") from exc

    async def achat_completions_create(self, model, messages, **kwargs):
        """Optional: Native async implementation."""
        # Delegate to sync version or use async SDK

        return self.chat_completions_create(model, messages, **kwargs)

    # Optional: streaming support

    # def chat_completions_create_stream(self, model, messages, **kwargs):

    #     yield chunk

Step 3: Handle Configuration Flexibly

The **config parameter is critical. The ProviderFactory forwards all provider-specific settings from the user's configuration dictionary. Accepting **config ensures compatibility with any future options without code changes.

Step 4: Manage Dependencies

Add any required third-party packages to your environment. aisuite itself requires no modifications—no edits to requirements.txt or source files are needed.


Using Your Custom Provider

Once the module exists, instantiate your provider through either the factory directly or the high-level Client.

Direct Factory Usage

from aisuite.provider import ProviderFactory

config = {
    "api_key": "sk-my-custom-key",
    "base_url": "https://api.mycustom.com/v1"
}

provider = ProviderFactory.create_provider("mycustom", config)

response = provider.chat_completions_create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Explain quantum computing"}]
)

print(response.choices[0]["message"]["content"])

Via the Client Interface

from aisuite import Client

client = Client(provider_configs={
    "mycustom": {
        "api_key": "sk-my-custom-key",
        "base_url": "https://api.mycustom.com/v1"
    }
})

# Model string format: "provider_key:model_name"

result = client.chat.completions.create(
    model="mycustom:gpt-4",
    messages=[{"role": "user", "content": "Write a Python function"}]
)

print(result.choices[0].message.content)

The Client validates provider keys against ProviderFactory.get_supported_providers() before instantiation. Invalid keys raise a clear ValueError as implemented in aisuite/client.py lines 80-87.


Required and Optional Methods

Method Status Description
__init__(self, **config) Required Initialize with flexible config dictionary
chat_completions_create(self, model, messages, **kwargs) Required Synchronous chat completion
achat_completions_create(self, model, messages, **kwargs) Optional Native async implementation
chat_completions_create_stream(self, model, messages, **kwargs) Optional Streaming response generator
Audio methods Optional Speech-to-text or text-to-speech capabilities

The base Provider class in aisuite/provider.py provides default implementations that raise LLMError for unimplemented optional methods.


Key Source Files for Reference

Study openai_provider.py for a production-ready example of handling API keys, base URLs, timeouts, and response transformation.


Summary

  • aisuite's ProviderFactory uses naming conventions and dynamic imports to instantiate providers from string keys
  • Create a module named {key}_provider.py with a class {Key}Provider subclassing Provider
  • Implement chat_completions_create with OpenAI-compatible return format; accept **config for flexibility
  • Optional async and streaming methods enhance performance where supported by the underlying service
  • No core library changes needed—drop-in modules are discovered automatically at runtime

Frequently Asked Questions

How does ProviderFactory convert a string key to a provider instance?

ProviderFactory.create_provider() in aisuite/provider.py uses importlib.import_module() to load aisuite.providers.{key}_provider, then retrieves the class via getattr(module, f"{key.title()}Provider") and instantiates it with the supplied config dictionary. This convention-based approach eliminates manual registration.

What happens if my provider key doesn't match any module?

The factory raises a ValueError with a clear message. In aisuite/client.py lines 80-87, the Client class validates keys against ProviderFactory.get_supported_providers() before attempting instantiation, surfacing the error early in the configuration process.

Can I implement only synchronous methods and skip async support?

Yes. The base Provider class provides default implementations for achat_completions_create and streaming methods that raise LLMError. Your provider functions correctly for synchronous use; asynchronous calls will fail gracefully with a descriptive error message.

Do I need to modify aisuite's requirements.txt for my provider's dependencies?

No. aisuite deliberately does not import provider-specific packages at the top level. Install your dependencies in your environment, and import them only within your provider module's methods. This keeps aisuite lightweight while allowing full flexibility for custom implementations.

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 →