How to Add a Custom LLM Provider to aisuite: A Complete Step-by-Step Guide
To add a custom LLM provider to aisuite, create a Python module in aisuite/providers/<key>_provider.py containing a class named <Key>Provider that inherits from the base Provider class and implements the required chat_completions_create() method; the ProviderFactory automatically discovers and registers your provider at runtime based on the file naming convention.
The andrewyng/aisuite library abstracts LLM interactions through a minimal, extensible architecture. Integrating a custom provider—whether for a private API, experimental model, or specialized endpoint—requires implementing a simple interface that the framework's ProviderFactory can automatically discover and instantiate.
Understanding the Provider Architecture
aisuite defines its LLM integration layer in aisuite/provider.py. The base Provider class (lines 24-90) establishes the contract that all providers must fulfill, while the ProviderFactory (lines 92-124) handles dynamic discovery and instantiation.
The framework uses a strict naming convention for automatic registration:
- File naming:
<provider_key>_provider.pyplaced inaisuite/providers/ - Class naming:
<Provider_key>Provider(title-cased, e.g.,openai→OpenaiProvider)
When ProviderFactory.create_provider() is called, it scans the aisuite/providers/ directory, imports the matching module, and instantiates the corresponding class with the provided configuration dictionary.
Step-by-Step Implementation Guide
Create the Provider Module
Create a new file following the naming convention. For a provider with the key mycustom, create aisuite/providers/mycustom_provider.py:
from aisuite.provider import Provider, LLMError
class MycustomProvider(Provider):
"""Custom provider implementation for mycustom API."""
def __init__(self, api_key: str, endpoint: str = "https://api.mycustom.ai/v1", **kwargs):
super().__init__()
self.api_key = api_key
self.endpoint = endpoint
Implement Required Methods
You must implement chat_completions_create(self, model, messages), which receives the model identifier and a list of message dictionaries. This method should return a dictionary matching the OpenAI chat completion format (or raise LLMError on failure):
def chat_completions_create(self, model: str, messages: list):
"""Send chat request to custom LLM endpoint."""
import httpx
payload = {"model": model, "messages": messages}
headers = {"Authorization": f"Bearer {self.api_key}"}
try:
resp = httpx.post(
f"{self.endpoint}/chat/completions",
json=payload,
headers=headers,
timeout=30
)
resp.raise_for_status()
except httpx.HTTPError as exc:
raise LLMError(f"MycustomProvider request failed: {exc}") from exc
return resp.json()
Add Optional Features
To support streaming responses, implement chat_completions_create_stream() yielding partial results:
def chat_completions_create_stream(self, model: str, messages: list):
"""Yield streaming chunks from custom provider."""
import httpx
with httpx.stream(
"POST",
f"{self.endpoint}/chat/completions/stream",
json={"model": model, "messages": messages},
headers={"Authorization": f"Bearer {self.api_key}"}
) as stream:
for line in stream.iter_lines():
if line:
yield {"choices": [{"delta": {"content": line.decode()}}]}
For async support, implement achat_completions_create():
async def achat_completions_create(self, model: str, messages: list, **kwargs):
"""Async version of chat completions."""
import httpx
async with httpx.AsyncClient() as client:
resp = await client.post(
f"{self.endpoint}/chat/completions",
json={"model": model, "messages": messages},
headers={"Authorization": f"Bearer {self.api_key}"},
timeout=30
)
resp.raise_for_status()
return resp.json()
Configure Audio Support (Optional)
If your provider supports audio modalities, expose an Audio subclass by assigning it to self.audio in your provider's __init__ method, following the pattern seen in aisuite/providers/openai_provider.py.
Complete Custom Provider Example
Here is a complete implementation integrating a hypothetical custom LLM API:
# aisuite/providers/mycustom_provider.py
from aisuite.provider import Provider, LLMError
import httpx
class MycustomProvider(Provider):
"""
Example custom provider for https://api.mycustom.ai
Supports sync, async, and streaming completions.
"""
def __init__(self, api_key: str, endpoint: str = "https://api.mycustom.ai/v1", **kwargs):
super().__init__()
self.api_key = api_key
self.endpoint = endpoint.rstrip("/")
def chat_completions_create(self, model: str, messages: list):
"""Standard synchronous chat completion."""
payload = {"model": model, "messages": messages}
headers = {"Authorization": f"Bearer {self.api_key}"}
try:
resp = httpx.post(
f"{self.endpoint}/chat/completions",
json=payload,
headers=headers,
timeout=60
)
resp.raise_for_status()
return resp.json()
except httpx.HTTPError as exc:
raise LLMError(f"Request failed: {exc}") from exc
def chat_completions_create_stream(self, model: str, messages: list):
"""Streaming chat completion."""
payload = {"model": model, "messages": messages, "stream": True}
headers = {"Authorization": f"Bearer {self.api_key}"}
with httpx.stream(
"POST",
f"{self.endpoint}/chat/completions",
json=payload,
headers=headers,
timeout=60
) as stream:
for line in stream.iter_lines():
if line and line.startswith(b"data: "):
data = line[6:].decode()
if data != "[DONE]":
yield {"choices": [{"delta": {"content": data}}]}
Registering and Using Your Provider
Once the file exists in aisuite/providers/, the ProviderFactory automatically registers it. No explicit registration code is required. Instantiate your provider programmatically:
from aisuite.provider import ProviderFactory
config = {
"api_key": "sk-your-custom-key",
"endpoint": "https://api.mycustom.ai/v1"
}
# ProviderFactory.create_provider uses the key to map to mycustom_provider.py
provider = ProviderFactory.create_provider("mycustom", config)
# Standard usage
response = provider.chat_completions_create(
"custom-model-v1",
[{"role": "user", "content": "Hello, world!"}]
)
print(response["choices"][0]["message"]["content"])
Alternatively, reference your custom provider in configuration files (TOML, YAML, or JSON) using the same provider key, and aisuite will instantiate it automatically when loading the configuration.
Summary
- Create a module at
aisuite/providers/<key>_provider.pywith a class named<Key>Providerinheriting fromProvider. - Implement the required
chat_completions_create(self, model, messages)method inaisuite/provider.pystyle. - Optionally add
chat_completions_create_stream()for streaming andachat_completions_create()for async support. - Let the factory handle discovery:
ProviderFactoryinaisuite/provider.py(lines 92-124) automatically scans and registers providers based on the file naming convention. - Raise
LLMErrorfromaisuite.providerfor consistent error handling across the framework.
Frequently Asked Questions
What is the minimum code required to add a custom LLM provider to aisuite?
You need a file named <your_key>_provider.py in aisuite/providers/ containing a class <YourKey>Provider that inherits from Provider and implements chat_completions_create(self, model, messages). This single method must return a dictionary matching the OpenAI chat completion response format or raise LLMError on failure.
How does aisuite discover my custom provider without explicit registration?
The ProviderFactory.get_supported_providers() method in aisuite/provider.py scans the aisuite/providers/ directory at runtime, converting filenames like mycustom_provider.py into provider keys (mycustom). When you call ProviderFactory.create_provider("mycustom", config), the factory imports the module and instantiates the MycustomProvider class automatically.
Can I add streaming and asynchronous support to my custom provider?
Yes. Implement chat_completions_create_stream() to yield partial response chunks, and achat_completions_create() as an async coroutine returning the full response. While only the synchronous chat_completions_create() is strictly required, adding these methods enables full feature parity with built-in providers like OpenaiProvider in aisuite/providers/openai_provider.py.
Where should I store API keys and endpoint URLs for my custom provider?
Pass configuration parameters including api_key and endpoint as keyword arguments to ProviderFactory.create_provider(), which passes them to your provider's __init__ method. For production deployments, store sensitive values in environment variables or secure configuration files (TOML/YAML), loading them into the config dictionary before passing to the factory.
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 →