# How to Add a Custom LLM Provider to aisuite: A Complete Step-by-Step Guide

> Learn to add a custom LLM provider to aisuite with this step-by-step guide. Integrate your own LLM by creating a Python module following specific naming conventions for seamless discovery.

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

---

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

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

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

```python
    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()`:

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

## Complete Custom Provider Example

Here is a complete implementation integrating a hypothetical custom LLM API:

```python

# 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:

```python
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.py` with a class named `<Key>Provider` inheriting from `Provider`.
- **Implement** the required `chat_completions_create(self, model, messages)` method in [`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) style.
- **Optionally add** `chat_completions_create_stream()` for streaming and `achat_completions_create()` for async support.
- **Let the factory handle discovery**: `ProviderFactory` in [`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) (lines 92-124) automatically scans and registers providers based on the file naming convention.
- **Raise** `LLMError` from `aisuite.provider` for 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`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) scans the `aisuite/providers/` directory at runtime, converting filenames like [`mycustom_provider.py`](https://github.com/andrewyng/aisuite/blob/main/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`](https://github.com/andrewyng/aisuite/blob/main/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.