Multi-provider abstraction with Anthropic, Google, Mistral, and custom providers
AI Providers
The @codmir/ai provider system abstracts multiple AI services behind a unified interface. Each provider implements the BaseProvider abstract class, exposing complete(), stream(), and createEmbedding().
Installation
import {
BaseProvider,
AnthropicProvider,
createAnthropicProvider,
createProviders,
getProvider,
getBestProvider,
} from "@codmir/ai";Provider Registry
createProviders(): ProviderRegistry
Creates a registry of all available providers. Each provider is instantiated from environment variables.
import { createProviders, getBestProvider } from "@codmir/ai";
const providers = createProviders();
const best = getBestProvider(providers, "anthropic");
const response = await best.complete(
[{ role: "user", content: "Hello" }],
{ model: "claude-sonnet-4-20250514" },
);getProvider(registry, name): BaseProvider
Returns a specific provider from the registry. Throws if not found.
getBestProvider(registry, preference?): BaseProvider
Returns the best available provider, optionally preferring a specific one. Falls back to other available providers if the preferred one is not configured.
AnthropicProvider
The Anthropic provider uses Claude models via the @ai-sdk/anthropic package.
Setup
import { createAnthropicProvider } from "@codmir/ai";
// From environment (reads ANTHROPIC_API_KEY)
const provider = createAnthropicProvider();
// Manual configuration
const provider = createAnthropicProvider({
apiKey: "sk-ant-...",
baseUrl: "https://api.anthropic.com",
defaultModel: "claude-opus-4-20250514",
});Capabilities
| Capability | Supported |
|---|---|
| Chat | Yes |
| Streaming | Yes |
| Vision | Yes |
| Function Calling | Yes |
| Embeddings | No |
| Code Generation | Yes |
| Long Context | Yes (200k tokens) |
Default Model
claude-opus-4-20250514
Environment Variables
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY | Anthropic API key |
BaseProvider Interface
All providers implement this abstract class. Use it to create custom providers.
import { BaseProvider, BaseProviderOptions } from "@codmir/ai";
import type {
AIProvider,
ProviderCapabilities,
Message,
ToolDefinition,
CompletionResponse,
StreamChunk,
} from "@codmir/ai";BaseProviderOptions
interface BaseProviderOptions {
apiKey?: string;
baseUrl?: string;
defaultModel?: string;
}Abstract Methods
complete(messages, options?): Promise<CompletionResponse>
Generate a single completion.
abstract complete(
messages: Message[],
options?: {
model?: string;
temperature?: number;
maxTokens?: number;
tools?: ToolDefinition[];
},
): Promise<CompletionResponse>;stream(messages, options?): AsyncIterable<StreamChunk>
Stream a completion response.
abstract stream(
messages: Message[],
options?: {
model?: string;
temperature?: number;
maxTokens?: number;
tools?: ToolDefinition[];
},
): AsyncIterable<StreamChunk>;createEmbedding(text, options?): Promise<number[][]>
Create embeddings for text. Throws if the provider does not support embeddings.
abstract createEmbedding(
text: string | string[],
options?: { model?: string },
): Promise<number[][]>;isConfigured(): boolean
Returns whether the provider has the required credentials to operate.
getDefaultModel(): string
Returns the provider's default model identifier.
getStatus(): ProviderStatus
Returns the provider's current status (implemented by BaseProvider, not abstract).
const status = provider.getStatus();
// {
// provider: "anthropic",
// available: true,
// model: "claude-opus-4-20250514",
// capabilities: { chat: true, streaming: true, vision: true, ... }
// }Creating a Custom Provider
Extend BaseProvider to add support for a new AI service.
import { BaseProvider, BaseProviderOptions } from "@codmir/ai";
import type {
AIProvider,
ProviderCapabilities,
Message,
CompletionResponse,
StreamChunk,
} from "@codmir/ai";
export class MyProvider extends BaseProvider {
readonly name: AIProvider = "local";
readonly capabilities: ProviderCapabilities = {
chat: true,
streaming: true,
vision: false,
functionCalling: false,
embeddings: true,
codeGeneration: true,
longContext: false,
maxContextLength: 8192,
};
constructor(options: BaseProviderOptions = {}) {
super(options);
this.apiKey = options.apiKey || process.env.MY_PROVIDER_KEY;
}
getDefaultModel(): string {
return "my-model-v1";
}
isConfigured(): boolean {
return !!this.apiKey;
}
async complete(messages: Message[], options?: { model?: string; temperature?: number; maxTokens?: number }): Promise<CompletionResponse> {
// Implement your API call here
const response = await fetch("https://my-provider.com/v1/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: options?.model || this.defaultModel,
messages,
temperature: options?.temperature,
max_tokens: options?.maxTokens,
}),
});
const data = await response.json();
return {
content: data.choices[0].message.content,
role: "assistant",
usage: {
promptTokens: data.usage.prompt_tokens,
completionTokens: data.usage.completion_tokens,
totalTokens: data.usage.total_tokens,
},
provider: this.name,
model: options?.model || this.defaultModel,
finishReason: "stop",
};
}
async *stream(messages: Message[], options?: { model?: string }): AsyncIterable<StreamChunk> {
// Implement streaming
yield { content: "...", done: false };
yield { content: "", done: true };
}
async createEmbedding(text: string | string[]): Promise<number[][]> {
// Implement embeddings
const texts = Array.isArray(text) ? text : [text];
return texts.map(() => new Array(768).fill(0));
}
}Types
ProviderCapabilities
interface ProviderCapabilities {
chat: boolean;
streaming: boolean;
vision: boolean;
functionCalling: boolean;
embeddings: boolean;
codeGeneration: boolean;
longContext: boolean;
maxContextLength: number;
}ProviderStatus
interface ProviderStatus {
provider: AIProvider;
available: boolean;
model: string;
capabilities: ProviderCapabilities;
}ProviderConfig
interface ProviderConfig {
provider: AIProvider;
apiKey?: string;
baseUrl?: string;
model?: string;
maxTokens?: number;
temperature?: number;
}