Skip to main content

Provider Pattern

SmarterAvatar uses a provider adapter pattern to abstract AI services behind consistent interfaces.

Why Adapters?

Different AI providers have different APIs, authentication methods, and capabilities. The adapter pattern provides:

  • Consistent interface - Same code works with any provider
  • Easy switching - Change provider via environment variable
  • Future-proof - Add new providers without modifying core code
  • Testing - Mock providers for development and testing

Provider Types

LLM Providers

Handle text generation and reasoning:

interface LLMProvider {
chat(messages: Message[], options?: ChatOptions): Promise<Response>;
stream(messages: Message[], options?: ChatOptions): AsyncIterable<Chunk>;
getCapabilities(): ProviderCapabilities;
}

Available: Gemini, OpenAI, Anthropic

STT Providers

Handle speech-to-text conversion:

interface STTProvider {
transcribe(audio: Buffer, options?: TranscribeOptions): Promise<Transcript>;
getCapabilities(): ProviderCapabilities;
}

Available: Whisper (OpenAI), Deepgram

Avatar Providers

Handle video avatar synthesis:

interface AvatarProvider {
createSession(config: SessionConfig): Promise<Session>;
speak(sessionId: string, text: string): Promise<void>;
endSession(sessionId: string): Promise<void>;
}

Available: HeyGen LiveAvatar

Configuration

Providers are selected via environment variables:

# LLM Provider
LLM_PROVIDER=gemini # gemini | openai | anthropic

# STT Provider
STT_PROVIDER=whisper # whisper | deepgram

# Avatar (auto-detected from API key)
LIVEAVATAR_API_KEY=... # HeyGen

Provider Selection

At runtime, factory functions return the configured provider:

// Get the configured LLM provider
const llm = getLLMProvider();
const response = await llm.chat(messages);

// Get the configured STT provider
const stt = getSTTProvider();
const transcript = await stt.transcribe(audioBuffer);

Capabilities

Each provider reports its capabilities:

const capabilities = llm.getCapabilities();
// {
// streaming: true,
// functionCalling: true,
// vision: true,
// maxTokens: 1000000,
// contextWindow: 1000000
// }

This allows the application to adapt behavior based on provider features.

Adding New Providers

To add a new LLM provider:

  1. Create an adapter implementing the LLMProvider interface
  2. Register it in the provider factory
  3. Add configuration to .env.example
  4. Document in the providers section

The modular design means new providers don't require changes to the rest of the application.

Error Handling

All providers use consistent error types:

try {
const response = await llm.chat(messages);
} catch (error) {
if (error instanceof ProviderAuthError) {
// Invalid API key
} else if (error instanceof ProviderRateLimitError) {
// Rate limited
} else if (error instanceof ProviderUnavailableError) {
// Service down
}
}