# How aisuite Implements Provider Abstraction and the ProviderFactory Pattern

> Discover how aisuite uses provider abstraction and the ProviderFactory pattern for flexible backend integration. Learn about its abstract base class and dynamic instantiation.

- Repository: [Andrew Ng/aisuite](https://github.com/andrewyng/aisuite)
- Tags: internals
- Published: 2026-08-04

---

**aisuite implements provider abstraction through a lightweight abstract base class `Provider` in [`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) and instantiates concrete implementations via the dynamic `ProviderFactory` class using naming conventions and lazy imports.**

aisuite is an open-source Python library that provides a unified interface for multiple large language model (LLM) and audio providers. The project, hosted at `andrewyng/aisuite`, achieves its goal of vendor-agnostic AI integration through a clean **aisuite provider abstraction** layer and a dynamic **ProviderFactory pattern** that eliminates hard dependencies on specific SDKs until runtime.

## The Provider Abstract Base Class

### Core Interface Definition

The foundation of aisuite's provider abstraction lives in **[`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py)**. The `Provider` abstract base class defines the contract that every concrete provider must implement:

- **`chat_completions_create`** – Synchronous chat completion request.
- **`achat_completions_create`** – Async version that runs the sync method in a thread pool by default.
- **`chat_completions_create_stream`** / **`achat_completions_create_stream`** – Optional streaming interfaces enabled by overriding.
- **`audio`** – An optional attribute holding an `Audio` implementation for transcription services.

```python

# aisuite/provider.py (excerpt)

class Provider(ABC):
    def __init__(self):
        self.audio: Optional[Audio] = None

    @abstractmethod
    def chat_completions_create(self, model, messages):
        ...

    async def achat_completions_create(self, model, messages, **kwargs):
        return await asyncio.to_thread(
            lambda: self.chat_completions_create(model, messages, **kwargs)
        )

```

### Synchronous and Asynchronous Support

The base class provides a default asynchronous implementation using **`asyncio.to_thread`**, which wraps the synchronous `chat_completions_create` method. This design allows providers to support async operations immediately without implementing custom async logic, while still permitting overrides for native async SDKs.

## The ProviderFactory Pattern Implementation

### Dynamic Provider Discovery

The **`ProviderFactory`** class in [`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) implements the classic Factory pattern with filesystem discovery. The **`get_supported_providers`** method scans the `aisuite/providers` directory and caches results using **`@functools.cache`**:

```python

# aisuite/provider.py (excerpt)

class ProviderFactory:
    PROVIDERS_DIR = Path(__file__).parent / "providers"

    @classmethod
    @functools.cache
    def get_supported_providers(cls):
        provider_files = Path(cls.PROVIDERS_DIR).glob("*_provider.py")
        return {file.stem.replace("_provider", "") for file in provider_files}

```

This approach returns a set of available provider keys (e.g., `{"openai", "anthropic", "google"}`) without importing any provider modules, keeping startup time minimal.

### Lazy Loading and Instantiation

The **`create_provider`** method translates provider keys into module and class names using a strict naming convention, then uses **`importlib.import_module`** for lazy loading:

1. **Naming convention** – A key like `"openai"` becomes module `openai_provider` and class `OpenaiProvider`.
2. **Dynamic import** – The module is loaded only when requested, avoiding hard dependencies on unused SDKs.
3. **Configuration forwarding** – The factory passes the user-supplied configuration dictionary directly to the provider's constructor.

```python

# aisuite/provider.py (excerpt)

class ProviderFactory:
    @classmethod
    def create_provider(cls, provider_key, config):
        provider_class_name = f"{provider_key.capitalize()}Provider"
        provider_module_name = f"{provider_key}_provider"
        module_path = f"aisuite.providers.{provider_module_name}"
        module = importlib.import_module(module_path)
        provider_class = getattr(module, provider_class_name)
        return provider_class(**config)

```

## Architecture Workflow: From Discovery to Execution

The **aisuite provider abstraction** and **ProviderFactory pattern** work together through five distinct phases:

1. **Discovery** – `ProviderFactory.get_supported_providers()` reads the filesystem to build a list of available keys.
2. **Selection** – Client code picks a provider key based on user configuration or command-line flags.
3. **Instantiation** – `ProviderFactory.create_provider(key, cfg)` imports the matching module (e.g., `aisuite.providers.openai_provider`) and constructs the concrete class (e.g., `OpenaiProvider`).
4. **Uniform Interface** – The caller interacts with the returned instance through the `Provider` abstract API, remaining oblivious to the underlying SDK (OpenAI, Anthropic, Azure, etc.).
5. **Audio Support** – If the provider supports transcription, its constructor sets `self.audio` to an `Audio` subclass (e.g., `OpenAIAudio`), which follows the same abstract pattern (`Audio.Transcription`).

Because the factory relies only on naming conventions and dynamic imports, adding a new provider requires only creating a file `<name>_provider.py` defining a `<Name>Provider` class inheriting from `Provider`. No changes to the core factory logic are required.

## Practical Implementation Examples

### Creating a Provider from Configuration

```python
from aisuite.provider import ProviderFactory

config = {"api_key": "sk-…", "base_url": "https://api.openai.com/v1"}
provider = ProviderFactory.create_provider("openai", config)

# Use the uniform API

response = provider.chat_completions_create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

```

### Listing All Available Providers

```python
from aisuite.provider import ProviderFactory

available = ProviderFactory.get_supported_providers()
print("Supported providers:", sorted(available))

# → Supported providers: ['anthropic', 'aws', 'azure', 'cohere', …]

```

### Using Async Helpers

```python
import asyncio
from aisuite.provider import ProviderFactory

async def demo():
    prov = ProviderFactory.create_provider("anthropic", {"api_key": "…"})
    resp = await prov.achat_completions_create(
        model="claude-3-5-sonnet-20240620",
        messages=[{"role": "user", "content": "Explain quantum entanglement."}],
    )
    print(resp.content[0].text)

asyncio.run(demo())

```

### Accessing Audio Transcription

```python
provider = ProviderFactory.create_provider("openai", {"api_key": "…"})
transcript = provider.audio.transcriptions.create(
    model="whisper-1",
    file="speech.wav",
)
print(transcript.text)

```

## Key Source Files

- **[`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py)** – Defines the `Provider` abstract base class, `ProviderFactory`, and `Audio` interfaces. This is the core of the provider abstraction and factory implementation.
- **[`aisuite/providers/openai_provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/providers/openai_provider.py)** – Concrete implementation demonstrating how a provider plugs into the abstraction, including audio transcription support.
- **`aisuite/providers/`** – Directory containing all concrete provider implementations (e.g., [`anthropic_provider.py`](https://github.com/andrewyng/aisuite/blob/main/anthropic_provider.py), [`google_provider.py`](https://github.com/andrewyng/aisuite/blob/main/google_provider.py)) following the established naming convention.
- **[`aisuite/framework/provider_interface.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/framework/provider_interface.py)** – Higher-level helper that wraps a `Provider` instance for integration with the broader framework.

## Summary

- **aisuite** achieves vendor neutrality through an abstract `Provider` base class in [`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) that standardizes chat completion and audio interfaces.
- The **`ProviderFactory`** uses filesystem scanning and `importlib` to discover and instantiate providers dynamically, avoiding hard dependencies on unused SDKs.
- **Naming conventions** (`<key>_provider.py` / `<Key>Provider`) enable automatic plugin discovery without registry modifications.
- **Lazy loading** ensures that provider SDKs are imported only when explicitly requested, optimizing startup performance.
- The default async implementation using `asyncio.to_thread` allows immediate async support for synchronous-only SDKs.

## Frequently Asked Questions

### How does aisuite handle async operations for providers that only offer synchronous SDKs?

The `Provider` base class in [`aisuite/provider.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/provider.py) provides a default `achat_completions_create` method that uses `asyncio.to_thread` to execute the synchronous `chat_completions_create` method in a separate thread. This allows all providers to support async operations immediately, while providers with native async SDKs can override the method for optimized performance.

### What naming convention must I follow to add a new provider to aisuite?

You must create a file named `<provider_key>_provider.py` in the `aisuite/providers/` directory containing a class named `<Provider_key>Provider` (capitalized first letter) that inherits from `Provider`. For example, a provider with key `"cohere"` requires [`cohere_provider.py`](https://github.com/andrewyng/aisuite/blob/main/cohere_provider.py) containing class `CohereProvider`. The `ProviderFactory` relies on this convention to locate and instantiate the class dynamically.

### How does ProviderFactory avoid importing all provider SDKs at startup?

The `ProviderFactory` uses lazy loading via `importlib.import_module` inside the `create_provider` method. Modules are imported only when a specific provider is requested. Additionally, `get_supported_providers` scans the filesystem for filenames ending in [`_provider.py`](https://github.com/andrewyng/aisuite/blob/main/_provider.py) without importing the modules, ensuring zero SDK overhead during discovery.

### Can I use the provider abstraction without the ProviderFactory pattern?

Yes, you can instantiate concrete provider classes directly by importing them from their respective modules (e.g., `from aisuite.providers.openai_provider import OpenaiProvider`). However, using the `ProviderFactory` is recommended as it handles configuration validation, enforces the naming convention, and maintains the loose coupling that makes the architecture extensible.