# How ProviderFactory Dynamically Loads LLM Providers in AISuite

> Learn how ProviderFactory dynamically loads LLM providers in AISuite using a registry and dynamic imports. Instantiate implementations only when needed for efficient runtime.

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

---

**The ProviderFactory uses a registry-based pattern with dynamic imports to instantiate LLM providers at runtime, mapping provider names to descriptor objects that load concrete implementations only when requested.**

AISuite, developed by Andrew Ng's team at andrewyng/aisuite, implements a sophisticated factory architecture that enables seamless switching between LLM vendors without code changes. The system relies on a central registry in [[`platform/coworker/providers/registry.py`](https://github.com/andrewyng/aisuite/blob/main/platform/coworker/providers/registry.py)](https://github.com/andrewyng/aisuite/blob/main/platform/coworker/providers/registry.py) that defer-loads provider modules until explicitly requested, minimizing bundle size and maximizing extensibility.

## The Registry-Based Factory Architecture

The dynamic loading mechanism centers on three core components working in concert: provider descriptors, a central registry, and the factory build method. This pattern abstracts vendor-specific implementations behind a unified interface while maintaining plug-and-play extensibility.

### Provider Descriptors and Builder Functions

Each LLM provider ships with a **descriptor object** that defines the provider's configuration fields and a builder function. These descriptors reside in individual provider modules such as [`aisuite-js/src/providers/openai/provider.ts`](https://github.com/andrewyng/aisuite/blob/main/aisuite-js/src/providers/openai/provider.ts), [`aisuite-js/src/providers/anthropic/provider.ts`](https://github.com/andrewyng/aisuite/blob/main/aisuite-js/src/providers/anthropic/provider.ts), and [`aisuite-js/src/providers/mistral/provider.ts`](https://github.com/andrewyng/aisuite/blob/main/aisuite-js/src/providers/mistral/provider.ts).

The builder function performs a **dynamic `import()`** of the provider's implementation module. This ensures provider code loads only when the user explicitly selects that vendor, keeping the initial bundle size minimal.

### The Central Registry

All provider descriptors are collected in a single registry defined in [`platform/coworker/providers/registry.py`](https://github.com/andrewyng/aisuite/blob/main/platform/coworker/providers/registry.py). The registry maintains a dictionary that maps provider names (e.g., `"openai"`, `"anthropic"`, `"groq"`) to their respective descriptor objects.

When the AISuite client initializes, it consults this registry to resolve the requested provider name to its concrete implementation.

## How the Factory Build Method Works

The registry exposes a `build(profile, secrets)` function that acts as the primary factory method. This function accepts a configuration profile and secret credentials, then executes the following sequence:

1. **Descriptor Lookup**: Retrieves the provider descriptor for the requested name from the registry dictionary.
2. **Dynamic Module Loading**: Invokes the descriptor's builder function, which uses dynamic `import()` to load the specific provider module.
3. **Instance Creation**: Constructs a fully initialized provider instance with the supplied credentials.
4. **Client Attachment**: Returns the concrete provider client to the calling code.

This process occurs without hardcoding vendor-specific logic, allowing the factory to support new providers through registry updates alone.

## Client Integration and Provider Wiring

The top-level client in [`aisuite-js/src/client.ts`](https://github.com/andrewyng/aisuite/blob/main/aisuite-js/src/client.ts) leverages the factory to obtain provider instances. Upon instantiation with a configuration object containing the provider name and API keys, the client delegates to the factory's `build()` method.

All subsequent LLM operations—such as `chat()` and `completion()`—delegate to this stored provider instance, completely abstracting concrete vendor details from the application layer.

## Practical Implementation Examples

### Example 1: Creating a Client with the OpenAI Provider

```typescript
import { AISuiteClient } from "./client";

const client = new AISuiteClient({
  llm: {
    provider: "openai",                 // ← name used by the registry
    apiKey: "sk-********************"   // ← passed as a secret to the builder
  }
});

// The factory looks up the "openai" descriptor, dynamically imports
// `src/providers/openai/provider.ts`, builds an OpenAIProvider instance
// and attaches it to `client.provider`.
await client.chat({ messages: [{ role: "user", content: "Hello!" }] });

```

### Example 2: Switching to the Anthropic Provider

```typescript
client.updateConfig({
  llm: {
    provider: "anthropic",
    apiKey: "anthropic-****************"
  }
});

// The next call triggers the factory again; it loads the Anthropic module
// and replaces the underlying provider instance.
await client.chat({ messages: [{ role: "assistant", content: "How can I help?" }] });

```

### Example 3: Adding a Custom Provider

```typescript
// In `my-provider/provider.ts`
export class MyProvider implements Provider { /* … */ }

// In `my-provider/descriptor.ts`
export const myProviderDescriptor = {
  name: "myprovider",
  fields: [{ name: "apiKey", type: "string" }],
  builder: (profile, secrets) =>
    import("./provider").then(m => new m.MyProvider(secrets.apiKey))
};

// Register it (e.g. in the central registry)
import { registry } from "platform/coworker/providers/registry";
registry["myprovider"] = myProviderDescriptor;

```

Now the client can be configured with `"myprovider"` and the factory will load it automatically.

## Summary

- **AISuite** uses a registry-based factory pattern in [`platform/coworker/providers/registry.py`](https://github.com/andrewyng/aisuite/blob/main/platform/coworker/providers/registry.py) to manage LLM provider instantiation.
- **Dynamic imports** ensure provider code loads only when requested, optimizing performance and bundle size.
- **Provider descriptors** encapsulate vendor-specific builder functions that return concrete implementations.
- **Zero code changes** are required to switch between providers or add new ones—simply update the configuration and register the descriptor.
- The top-level client in [`aisuite-js/src/client.ts`](https://github.com/andrewyng/aisuite/blob/main/aisuite-js/src/client.ts) delegates all LLM operations to the factory-provided provider instance.

## Frequently Asked Questions

### How does AISuite avoid loading unused provider code?

AISuite avoids loading unused provider code by using **dynamic imports** within descriptor builder functions. The factory only executes the import statement when a user explicitly requests that provider, keeping the initial bundle minimal and memory footprint low.

### What is the role of the provider descriptor?

The provider descriptor is an object that maps a provider name to its implementation details, containing UI field definitions and a **builder function** that performs the dynamic import and instantiation. Descriptors live in individual provider modules and are referenced by the central registry.

### Can I add custom providers without modifying core AISuite code?

Yes. Adding a custom provider requires only creating a module that implements the `Provider` interface, exporting a descriptor with a builder function, and registering it in the central registry. No changes to the core AISuite client or factory logic are necessary, achieving true plug-and-play extensibility.

### How does the factory handle provider configuration and secrets?

The factory's `build(profile, secrets)` method receives configuration profiles and API credentials as arguments. It passes these secrets to the provider's builder function, which initializes the concrete class with authentication tokens, ensuring secure credential handling without exposing keys to the registry itself.