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:
- Module file:
mycustom_provider.py - Class name:
MycustomProvider
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
aisuite/provider.py— AbstractProviderclass,ProviderFactoryimplementation, and exception typesaisuite/providers/openai_provider.py— Full reference implementation showing config handling, sync/async methods, streaming, and audio supportaisuite/client.py— High-level client that validates keys and lazily creates providers via the factoryaisuite/providers/__init__.py— Package marker enabling dynamic imports
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.pywith a class{Key}ProvidersubclassingProvider - Implement
chat_completions_createwith OpenAI-compatible return format; accept**configfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →