# How to Add a Custom LLM Provider to aisuite

> Learn to add a custom LLM provider to aisuite by subclassing Provider and implementing chat_completions_create. Follow ProviderFactory naming conventions for seamless integration.

- Repository: [Andrew Ng/aisuite](https://github.com/andrewyng/aisuite)
- Tags: how-to-guide
- Published: 2026-08-03

---

**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`](https://github.com/andrewyng/aisuite/blob/main/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`](https://github.com/andrewyng/aisuite/blob/main/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`](https://github.com/andrewyng/aisuite/blob/main/mycustom_provider.py)
- Class name: `MycustomProvider`

### Dynamic Import Process

```python

# 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`](https://github.com/andrewyng/aisuite/blob/main/mycustom_provider.py)**.

### Step 2: Subclass the Provider Base Class

Implement the required methods from the abstract `Provider` class:

```python

# 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`](https://github.com/andrewyng/aisuite/blob/main/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

```python
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

```python
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`](https://github.com/andrewyng/aisuite/blob/main/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`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) provides default implementations that raise `LLMError` for unimplemented optional methods.

---

## Key Source Files for Reference

- **[`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py)** — Abstract `Provider` class, `ProviderFactory` implementation, and exception types
- **[`aisuite/providers/openai_provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/providers/openai_provider.py)** — Full reference implementation showing config handling, sync/async methods, streaming, and audio support
- **[`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py)** — High-level client that validates keys and lazily creates providers via the factory
- **[`aisuite/providers/__init__.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/providers/__init__.py)** — Package marker enabling dynamic imports

Study [`openai_provider.py`](https://github.com/andrewyng/aisuite/blob/main/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`](https://github.com/andrewyng/aisuite/blob/main/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`](https://github.com/andrewyng/aisuite/blob/main/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.