How OpenMAIC Abstracts Different LLM Providers Using the Vercel AI SDK: A Technical Deep Dive
OpenMAIC implements a provider-agnostic adapter layer in lib/orchestration/ai-sdk-adapter.ts that maps provider IDs to specific Vercel AI SDK factories, exposing a unified chat.completions interface through the registry in lib/ai/providers.ts.
The OpenMAIC repository solves the challenge of supporting multiple large language model (LLM) backends without scattering provider-specific logic throughout the codebase. By leveraging the Vercel AI SDK’s modular factory functions, OpenMAIC creates a clean abstraction that allows developers to switch between OpenAI, Anthropic, Azure, and other providers using identical method signatures.
The Adapter Pattern: Mapping Providers to SDK Factories
At the core of the abstraction lies the adapter module located at lib/orchestration/ai-sdk-adapter.ts. This file imports the provider-specific factory functions from the Vercel AI SDK ecosystem and exposes a single entry point for SDK instantiation.
The getProviderSdk function accepts a provider identifier and configuration object, then returns the appropriate SDK instance through a deterministic switch statement:
// lib/orchestration/ai-sdk-adapter.ts
import { createOpenAI } from '@ai-sdk/openai';
import { createAnthropic } from '@ai-sdk/anthropic';
import { createAzure } from '@ai-sdk/azure';
interface ProviderConfig {
apiKey: string;
baseUrl?: string;
}
export function getProviderSdk(providerId: string, cfg: ProviderConfig) {
switch (providerId) {
case 'openai':
return createOpenAI({
apiKey: cfg.apiKey,
baseURL: cfg.baseUrl
});
case 'anthropic':
return createAnthropic({
apiKey: cfg.apiKey
});
case 'azure':
return createAzure({
apiKey: cfg.apiKey,
endpoint: cfg.baseUrl
});
default:
throw new Error(`Unsupported provider: ${providerId}`);
}
}
This approach encapsulates all provider-specific initialization logic, ensuring that downstream modules never directly import from @ai-sdk/* packages.
Provider Registry: Creating a Unified Interface
The lib/ai/providers.ts file implements the provider registry pattern, consuming the adapter to expose a homogeneous interface regardless of the underlying service. It reads runtime configuration from the application store and constructs SDK instances on demand.
The registry exposes a getChatClient method that returns a standardized chat client:
// lib/ai/providers.ts
import { getProviderSdk } from '@/lib/orchestration/ai-sdk-adapter';
export const providerRegistry = {
getChatClient(providerId: string) {
const cfg = store.getState().providersConfig[providerId];
const sdk = getProviderSdk(providerId, cfg);
return sdk.chat; // Unified chat interface
},
};
When components request a client, they receive an object conforming to the Vercel AI SDK's ChatCompletion type, abstracting away differences in endpoint URLs, authentication headers, and request payloads.
Unified Consumption: How Client Code Uses the Abstraction
Application components interact with LLMs through the registry without knowing which provider serves the request. The following example demonstrates identical code paths for Anthropic and OpenAI:
// Usage with Anthropic
import { providerRegistry } from '@/lib/ai/providers';
const anthropicClient = providerRegistry.getChatClient('anthropic');
const claudeResponse = await anthropicClient.completions({
model: 'claude-3-5-sonnet-20240620',
messages: [
{ role: 'user', content: 'Explain quantum tunneling in plain English.' }
],
});
// Usage with OpenAI (identical API surface)
const openaiClient = providerRegistry.getChatClient('openai');
const gptResponse = await openaiClient.completions({
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'Explain quantum tunneling in plain English.' }
],
});
This symmetry eliminates conditional logic in UI components and service layers, reducing maintenance overhead when adding or removing providers.
Advanced Abstraction Features
Model ID Normalization
The adapter layer handles translation between generic model identifiers and provider-specific naming conventions. When the registry receives a request for a standardized model name like "gpt-4o", the adapter maps it to the provider's internal identifier if necessary, ensuring consistent dropdown menus and configuration files across heterogeneous backends.
Automatic Fallback Handling
OpenMAIC respects the allow_fallbacks flag defined in provider configurations. When a primary provider fails, the adapter automatically instantiates a secondary Vercel AI SDK implementation (for example, falling back from Fireworks to Cerebras) without requiring changes to the calling code. This degradation logic remains encapsulated within lib/orchestration/ai-sdk-adapter.ts, preserving the simple interface exposed to the rest of the application.
Summary
- Adapter Module:
lib/orchestration/ai-sdk-adapter.tsisolates provider-specific SDK initialization using Vercel AI SDK factories likecreateOpenAIandcreateAnthropic. - Registry Pattern:
lib/ai/providers.tsexposes a unifiedgetChatClientmethod that returns standardized chat interfaces regardless of the underlying provider. - Code Reuse: Client components call identical
chat.completionsmethods for all providers, eliminating provider-specific branching logic. - Extensibility: Adding new LLM support requires only extending the adapter's switch statement and updating the provider configuration store.
- Resilience: Built-in fallback mechanisms transparently handle service outages without disrupting the user experience.
Frequently Asked Questions
What is the role of ai-sdk-adapter.ts in OpenMAIC?
The ai-sdk-adapter.ts module serves as the single integration point for the Vercel AI SDK. It imports provider-specific factory functions and exports getProviderSdk, which instantiates the correct SDK client based on runtime configuration. This file ensures that provider-specific dependencies remain isolated from business logic.
How does the provider registry handle different authentication requirements?
The registry delegates authentication to the adapter, which passes apiKey and optional baseUrl parameters to the respective Vercel AI SDK factory functions. Each provider receives only the credentials it requires—OpenAI and Anthropic typically need only an API key, while Azure requires both a key and an endpoint URL—without exposing these details to the calling code.
Can I add a custom LLM provider to OpenMAIC?
Yes. You must install the corresponding @ai-sdk/* package, add a new case to the getProviderSdk switch statement in lib/orchestration/ai-sdk-adapter.ts, and register the provider's configuration schema in the store. Once registered, the provider becomes available through the existing providerRegistry.getChatClient() interface without modifying any consumer components.
Does the abstraction layer support streaming responses?
Yes. Because the abstraction returns native Vercel AI SDK client instances, all SDK capabilities—including streaming text generation via streamText or chat.completions with streaming options—remain available to consumers. The adapter does not interfere with response handling; it only standardizes the client initialization process.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →