# How aisuite's Provider Factory Pattern Works for Adding New LLM Providers

> Learn how aisuite's provider factory pattern simplifies adding new LLM providers. Discover centralized instantiation and automatic request routing enabling seamless integration.

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

---

**aisuite implements a centralized factory pattern in the `Client` class that instantiates and stores provider instances based on a configuration object, automatically routing requests to the correct LLM backend by parsing `provider/model` strings.**

The andrewyng/aisuite library abstracts vendor-specific AI implementations behind a unified interface using a lightweight provider factory pattern. This architecture allows developers to add support for new language models by extending a base class and registering the provider in the central factory, without modifying existing client code or request handling logic.

## Configuration-Driven Factory Initialization

The factory logic resides entirely in [`src/client.ts`](https://github.com/andrewyng/aisuite/blob/main/src/client.ts), where the `Client` class constructor accepts a `ProviderConfigs` object containing optional configuration sections for each supported provider.

Inside the `initializeProviders` method, the code checks which configuration sections are present (such as `openai`, `anthropic`, `mistral`, or `groq`), instantiates the corresponding provider class, and stores the instance in internal Maps. This design decouples provider lifecycle management from the request execution logic.

## Provider Registration Maps

The `Client` class maintains two separate Maps to handle different provider capabilities:

- **`chatProviders: Map<string, Provider>`** for chat-completion providers
- **`asrProviders: Map<string, ASRProvider>`** for speech-to-text providers

When `initializeProviders` detects a configuration section, it creates the provider instance and registers it using the provider name as the key. For example, as implemented in [`aisuite-js/src/client.ts`](https://github.com/andrewyng/aisuite/blob/main/aisuite-js/src/client.ts):

```typescript
if (config.openai) {
  const openaiProvider = new OpenAIProvider(config.openai);
  this.chatProviders.set("openai", openaiProvider);
  this.asrProviders.set("openai", openaiProvider);
}
if (config.groq) {
  this.chatProviders.set("groq", new GroqProvider(config.groq));
}

```

Providers that support both functionalities (like OpenAI) are registered in both Maps, while specialized providers may only populate one.

## Model Parsing and Request Routing

When a user calls `client.chat.completions.create`, the model string must follow the format `provider/model-name` (for example, `openai/gpt-4`). The `parseModel` utility in [`src/utils/model-parser.ts`](https://github.com/andrewyng/aisuite/blob/main/src/utils/model-parser.ts) splits this string into its components.

The `Client` then retrieves the appropriate provider instance from the registration Map and forwards the request:

```typescript
const { provider, model } = parseModel(request.model);
const providerInstance = this.chatProviders.get(provider);
return providerInstance.chatCompletion(requestWithParsedModel, options);

```

This routing mechanism ensures that any component calling the unified API automatically gains support for new providers as soon as they are registered in the factory.

## Adding a New LLM Provider

Extending aisuite to support a new LLM provider requires four specific steps:

1. **Implement the provider class** by extending `BaseProvider` from [`src/core/base-provider.ts`](https://github.com/andrewyng/aisuite/blob/main/src/core/base-provider.ts) and implementing the required methods like `chatCompletion` and `streamChatCompletion`.

2. **Export the provider** by creating `src/providers/<new>/index.ts` and adding the export to [`src/providers/index.ts`](https://github.com/andrewyng/aisuite/blob/main/src/providers/index.ts).

3. **Update the configuration types** in [`src/types/providers.ts`](https://github.com/andrewyng/aisuite/blob/main/src/types/providers.ts) by adding a new optional field to the `ProviderConfigs` interface.

4. **Register in the factory** by adding instantiation logic to `Client.initializeProviders` in [`src/client.ts`](https://github.com/andrewyng/aisuite/blob/main/src/client.ts).

## Complete Implementation Example

The following example demonstrates adding a hypothetical `MyLLM` provider to the factory.

First, implement the provider class:

```typescript
// src/providers/myllm/provider.ts
import { BaseProvider } from "../../core/base-provider";

export class MyLLMProvider extends BaseProvider {
  constructor(private readonly cfg: MyLLMConfig) {
    super();
    // initialise SDK/client here
  }

  async chatCompletion(req: MyLLMChatRequest) {
    // call the MyLLM SDK and return a ChatCompletionResponse
  }

  async streamChatCompletion(req: MyLLMChatRequest) {
    // return an async iterable of ChatCompletionChunk
  }
}

```

Export the provider:

```typescript
// src/providers/myllm/index.ts
export { MyLLMProvider } from "./provider";
export type { MyLLMConfig } from "./types";

```

Update the aggregate exports:

```typescript
// src/providers/index.ts
export { OpenAIProvider } from "./openai";
export { AnthropicProvider } from "./anthropic";
export { GroqProvider } from "./groq";
export { MyLLMProvider } from "./myllm";   // ← new export

```

Extend the configuration interface:

```typescript
// src/types/providers.ts
export interface ProviderConfigs {
  openai?: OpenAIConfig;
  anthropic?: AnthropicConfig;
  myllm?: MyLLMConfig;                     // ← new config entry
}

```

Finally, register the provider in the factory:

```typescript
// src/client.ts
if (config.myllm) {
  this.chatProviders.set("myllm", new MyLLMProvider(config.myllm));
}

```

Using the new provider follows the standard pattern:

```typescript
import { Client } from "aisuite-js";

const client = new Client({
  myllm: { apiKey: "YOUR_KEY", baseUrl: "https://api.my-llm.com" },
});

const resp = await client.chat.completions.create({
  model: "myllm/awesome-model",
  messages: [{ role: "user", content: "Hello!" }],
});

```

## Summary

- **Centralized factory**: The `Client` class in [`src/client.ts`](https://github.com/andrewyng/aisuite/blob/main/src/client.ts) acts as the sole factory responsible for provider instantiation and lifecycle management.
- **Map-based storage**: Providers are stored in `chatProviders` and `asrProviders` Maps using the provider name as the key for O(1) lookup during request routing.
- **String-based routing**: The `parseModel` utility splits `provider/model` strings to determine which registered provider instance should handle the request.
- **Minimal integration overhead**: Adding a new provider requires only implementing `BaseProvider`, updating type definitions in [`src/types/providers.ts`](https://github.com/andrewyng/aisuite/blob/main/src/types/providers.ts), and adding a single registration block in `Client.initializeProviders`.

## Frequently Asked Questions

### What base class must new providers extend?

New providers must extend `BaseProvider` from [`src/core/base-provider.ts`](https://github.com/andrewyng/aisuite/blob/main/src/core/base-provider.ts) and implement the required interface methods, including `chatCompletion` for standard requests and optionally `streamChatCompletion` for streaming responses.

### How does the factory handle providers that support both chat and ASR?

Providers that support both chat completions and audio transcriptions (such as OpenAI) are instantiated once and registered in both the `chatProviders` and `asrProviders` Maps using the same provider name key, as shown in the `initializeProviders` implementation.

### Can I use environment variables instead of the configuration object?

While the factory accepts an explicit `ProviderConfigs` object in the constructor, individual provider implementations typically read API keys from environment variables (like `OPENAI_API_KEY`) if the configuration object does not explicitly provide them.

### What is the required format for model strings?

The model string must use the format `provider/model-name` (for example, `anthropic/claude-3-opus` or `groq/llama2-70b`). The `parseModel` function in [`src/utils/model-parser.ts`](https://github.com/andrewyng/aisuite/blob/main/src/utils/model-parser.ts) splits this string on the first forward slash to identify the provider and the specific model identifier.