Providers¶
Providers are functions that integrate AI services (OpenAI, Anthropic, Groq, etc.) with agents. They receive context and return chat completions in OpenAI-compatible format.
Overview¶
Providers enable multi-provider failover and vendor flexibility through a standard interface.
Key Features¶
- ✅ Standard OpenAI ChatCompletion format
- ✅ Automatic failover between providers
- ✅ Support for any AI service (OpenAI, Anthropic, Groq, custom)
- ✅ Access to full agent context
- ✅ Tool call support
Provider Function Signature¶
type Provider = {
(ctx: Context, opts?: { signal?: AbortSignal }): ChatCompletionResponse | Promise<ChatCompletionResponse>;
(ctx: Context, opts: { stream: true; signal?: AbortSignal }): AsyncIterable<ProviderChunk>;
};
Parameters: - ctx: Context instance exposing messages, tools, rules, metadata - opts.signal: Optional AbortSignal - opts.stream: When true, the provider must return an AsyncIterable<ProviderChunk> instead of a response object
Returns (non-stream): OpenAI-compatible ChatCompletionResponse Returns (stream): AsyncIterable<ProviderChunk>
ProviderChunk (stream mode)¶
type ProviderChunk =
| { type: "text_delta"; content: string }
| { type: "thinking_delta"; content: string }
| { type: "signature_delta"; content: string }
| { type: "tool_call_delta"; index: number; id?: string; name?: string; arguments_delta?: string }
| { type: "finish"; finish_reason: string };
text_delta— incremental text token from the model.thinking_delta— incremental reasoning/thinking token (Claude extended thinking, DeepSeek R1reasoning_content, etc.).signature_delta— cryptographic signature of the thinking block (Anthropic-specific). Accumulated and stored asthinking_signatureon the persistedassistantmessage so the API can accept the block when reinjected in subsequent turns.tool_call_delta— partial tool call accumulation (index, id, name, arguments fragment).finish— signals end of generation with the finish reason.
The thinking_delta / signature_delta chunks are emitted as StreamChunk { role: "thinking" } to the consumer. The Agent accumulates both to persist thinking and thinking_signature on the stored assistant message.
ResponseMessage (non-stream)¶
ResponseMessage now includes an optional reasoning_content?: string field that providers may populate from the model's native reasoning output.
ChatCompletion Format¶
interface ChatCompletion {
id: string;
object: "chat.completion";
created: number;
model: string;
choices: Array<{
index: number;
message: {
role: "assistant";
content: string;
tool_calls?: Array<{
id: string;
type: "function";
function: {
name: string;
arguments: string; // JSON string
};
}>;
};
finish_reason: "stop" | "tool_calls" | "length" | null;
}>;
}
Built-in Provider Classes¶
The library exports five ready-to-use callable provider classes: OpenAI, Groq, DeepSeek, Claude, Ollama. Each extends Function and is directly usable as a Provider.
OpenAI¶
import { OpenAI } from '@arcaelas/agent';
const openai = new OpenAI({
api_key: process.env.OPENAI_API_KEY!,
model: "gpt-4o",
// base_url?: string // default: "https://api.openai.com/v1"
// temperature?: number
// max_tokens?: number
// headers?: Record<string, string>
// extra_body?: Record<string, unknown> // pass-through to request body
});
const agent = new Agent({ providers: [openai] });
Use extra_body for provider-specific parameters (e.g. reasoning_effort):
const o3 = new OpenAI({
api_key: process.env.OPENAI_API_KEY!,
model: "o3",
extra_body: { reasoning_effort: "high" },
});
Groq¶
import { Groq } from '@arcaelas/agent';
const groq = new Groq({
api_key: process.env.GROQ_API_KEY!,
model: "llama-3.3-70b-versatile",
});
Groq is an OpenAI-compatible provider — it accepts the same options and uses extra_body for Groq-specific parameters.
DeepSeek¶
import { DeepSeek } from '@arcaelas/agent';
const deepseek = new DeepSeek({
api_key: process.env.DEEPSEEK_API_KEY!,
model: "deepseek-reasoner",
});
DeepSeek is an OpenAI-compatible provider. Streaming mode reads delta.reasoning_content and emits it as thinking_delta chunks.
Claude¶
Claude uses the Anthropic Messages API (not OpenAI-compatible). Key differences: - Uses body (not extra_body) for Anthropic-specific request fields. - api_key is optional — omit it when authentication is handled entirely via headers. - base_url is the full endpoint URL (default: "https://api.anthropic.com/v1/messages").
import { Claude } from '@arcaelas/agent';
// Classic API key
const claude = new Claude({
api_key: process.env.ANTHROPIC_API_KEY!,
model: "claude-sonnet-4-5",
// base_url?: string // full URL, default: "https://api.anthropic.com/v1/messages"
// temperature?: number
// max_tokens?: number // default: 1024
// headers?: Record<string, string>
// body?: Record<string, unknown> // merged into the Anthropic request body
});
// Extended thinking (via body)
const thinker = new Claude({
api_key: process.env.ANTHROPIC_API_KEY!,
model: "claude-sonnet-4-5",
body: { thinking: { type: "enabled", budget_tokens: 8000 } },
});
// Claude Code OAuth flow — full endpoint URL with ?beta=true + beta headers
const oauth_claude = new Claude({
base_url: "https://api.anthropic.com/v1/messages?beta=true",
api_key: oauth_access_token,
headers: { "anthropic-beta": "oauth-2025-04-20,claude-code-20250219" },
model: "claude-sonnet-4-5",
});
// Third-party gateway / proxy (authentication via custom headers, no api_key)
const proxy_claude = new Claude({
base_url: "https://gateway.example.com/anthropic/messages",
headers: { "x-gateway-key": "..." },
model: "claude-sonnet-4-5",
});
Extended thinking: Anthropic only emits
thinkingblocks when the request explicitly enables it viabody: { thinking: { type: "enabled", budget_tokens: <n> } }. Without that flag the model returns no thinking content.
Ollama¶
Ollama runs open-source models locally via Ollama's OpenAI-compatible API. It does not require an api_key and exposes think and num_ctx as first-class options.
import { Ollama } from '@arcaelas/agent';
// Basic usage — default base_url: "http://localhost:11434/v1"
const ollama = new Ollama({ model: "qwen3:8b" });
// Fast classifier (disable thinking)
const classifier = new Ollama({ model: "qwen3:8b", think: false });
// Deep reasoner (enable thinking)
const reasoner = new Ollama({ model: "qwen3:8b", think: true });
// Extended context window
const explorer = new Ollama({ model: "qwen2.5-coder:7b", num_ctx: 16384 });
// Remote Ollama instance
const remote = new Ollama({
model: "llama3.2:3b",
base_url: "http://192.168.1.10:11434/v1",
});
OllamaProviderOptions:
| Option | Type | Default | Description |
|---|---|---|---|
model | string | — | Model identifier (required) |
base_url | string | "http://localhost:11434/v1" | Ollama API base URL |
think | boolean \| undefined | undefined | Force thinking on/off; undefined lets the model decide |
num_ctx | number | model default | Context window size in tokens |
temperature | number | — | Sampling temperature |
max_tokens | number | — | Maximum tokens to generate |
headers | Record<string, string> | — | Extra HTTP headers |
All built-in providers parse model thinking natively: - OpenAI / Groq / DeepSeek / Ollama: read delta.reasoning_content or delta.reasoning (stream) and message.reasoning_content (non-stream). Use extra_body for provider-specific parameters. - Claude: extracts content_block.type === "thinking" and signature_delta from the Anthropic SSE stream.
Thinking content is emitted as StreamChunk { role: "thinking" } to the consumer. The Agent persists thinking and thinking_signature on the stored assistant message for providers that support it (Claude).
Multi-Provider Setup¶
Configure automatic failover:
import { Agent } from '@arcaelas/agent';
const agent = new Agent({
description: "High-availability AI assistant",
providers: [
openai_provider, // Primary (fastest)
groq_provider, // Backup (fast alternative)
anthropic_provider // Fallback (reliable)
]
});
// Agent automatically tries providers on failure
const [messages, success] = await agent.call("Hello");
Best Practices¶
1. Handle Errors Gracefully¶
const safe_provider: Provider = async (ctx) => {
try {
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
return await openai.chat.completions.create({ /* ... */ });
} catch (error) {
console.error("Provider failed:", error);
throw error; // Let agent try next provider
}
};
2. Use Environment Variables¶
const secure_provider: Provider = async (ctx) => {
if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY not configured");
}
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
return await openai.chat.completions.create({ /* ... */ });
};
3. Log Provider Usage¶
const logged_provider: Provider = async (ctx) => {
const start = Date.now();
try {
const response = await openai_provider(ctx);
console.log({ provider: "openai", duration_ms: Date.now() - start, tokens: response.usage?.total_tokens });
return response;
} catch (error) {
console.error({ provider: "openai", duration_ms: Date.now() - start, error: (error as Error).message });
throw error;
}
};
Related¶
- Agent - Uses providers for completions
- Message - Provider input/output format
- Tool - Providers handle tool calls
- Providers Guide - Detailed provider setup
Next: Learn about Built-in Tools →