Skip to content

Configuration

UPP provides flexible configuration options for provider infrastructure.

All providers share a common configuration interface:

interface ProviderConfig {
/**
* API key - string, async function, or key strategy
* @example 'sk-xxx'
* @example () => fetchKeyFromVault()
* @example roundRobinKeys(['sk-1', 'sk-2'])
*/
apiKey?: string | (() => string | Promise<string>) | KeyStrategy;
/** Override the base API URL (for proxies, local models) */
baseUrl?: string;
/** Request timeout in milliseconds */
timeout?: number;
/** Custom fetch implementation (for logging, caching, custom TLS) */
fetch?: typeof fetch;
/** API version override */
apiVersion?: string;
/** Retry strategy factory for handling failures and rate limits */
retryStrategy?: RetryStrategyFactory;
/** Custom HTTP headers to include in API requests */
headers?: Record<string, string>;
}

Some providers accept additional options for vendor-specific operational choices. These are passed to the provider factory function, not to ProviderConfig:

interface OpenAIProviderOptions {
/**
* Which API to use:
* - 'responses': Modern Responses API (default, recommended)
* - 'completions': Legacy Chat Completions API
*/
api?: 'responses' | 'completions';
}
// Use the modern Responses API (default)
const gpt = llm({
model: openai('gpt-4o'),
});
// Explicitly use the legacy Completions API
const gptLegacy = llm({
model: openai('gpt-4o', { api: 'completions' }),
});
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
apiKey: 'sk-ant-xxx',
},
});

UPP provides built-in key strategies for API key management:

Cycles through a list of API keys:

import { roundRobinKeys } from '@providerprotocol/ai';
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
apiKey: roundRobinKeys([
process.env.ANTHROPIC_KEY_1!,
process.env.ANTHROPIC_KEY_2!,
process.env.ANTHROPIC_KEY_3!,
]),
},
});

Selects keys based on weight (useful for tiered pricing or rate limits):

import { weightedKeys } from '@providerprotocol/ai';
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
apiKey: weightedKeys([
{ key: process.env.PRIMARY_KEY!, weight: 0.7 },
{ key: process.env.SECONDARY_KEY!, weight: 0.3 },
]),
},
});

Custom key selection logic:

import { dynamicKey } from '@providerprotocol/ai';
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
apiKey: dynamicKey(async () => {
// Custom logic to select key
const keys = await fetchActiveKeys();
return selectLeastUsedKey(keys);
}),
},
});

UPP provides pluggable retry strategies:

interface RetryStrategy {
/**
* Called when a request fails with a retryable error.
* @returns Delay in ms before retrying, or null to stop retrying
*/
onRetry(error: UPPError, attempt: number): number | null | Promise<number | null>;
/**
* Called before each request. Can be used for pre-emptive delays.
*/
beforeRequest?(): number | Promise<number>;
/**
* Reset the strategy state (e.g., after a successful request)
*/
reset?(): void;
readonly maxAttempts?: number;
setRetryAfter?(seconds: number): void;
}

Provider configs accept a RetryStrategyFactory — a function that returns a fresh RetryStrategy per request, ensuring complete isolation between concurrent requests.

import {
exponentialBackoff,
linearBackoff,
noRetry,
retryAfterStrategy,
} from '@providerprotocol/ai';
// Exponential backoff (recommended)
const strategy1 = exponentialBackoff({
maxAttempts: 3,
baseDelay: 1000,
maxDelay: 30000,
});
// Linear backoff
const strategy2 = linearBackoff({
maxAttempts: 5,
delay: 2000,
});
// No retries
const strategy3 = noRetry();
const myCustomStrategy: RetryStrategyFactory = () => ({
onRetry(error: UPPError, attempt: number): number | null {
if (attempt >= 5) return null;
if (error.code === 'RATE_LIMITED') return 5000;
return 1000;
}
});
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
retryStrategy: myCustomStrategy,
},
});

Override the API endpoint for proxies or local models:

const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
baseUrl: 'https://my-proxy.example.com',
},
});

Provide a custom fetch implementation for logging, caching, or custom TLS:

const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
fetch: async (url, init) => {
console.log('Request:', url);
const response = await fetch(url, init);
console.log('Response:', response.status);
return response;
},
},
});

Set request timeout in milliseconds:

const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
timeout: 30000, // 30 seconds
},
});

Add custom HTTP headers to API requests:

const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
headers: {
'anthropic-beta': 'extended-cache-ttl-2025-04-11',
},
},
});

Custom headers allow you to:

  • Enable provider beta features (e.g., Anthropic’s anthropic-beta header)
  • Pass organization/project identifiers (e.g., OpenAI’s OpenAI-Organization)
  • Provide attribution metadata (e.g., OpenRouter’s HTTP-Referer and X-Title)
  • Configure proxy authentication (e.g., Cloudflare Access headers for Ollama)
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
apiKey: process.env.ANTHROPIC_API_KEY,
headers: {
'anthropic-beta': 'extended-cache-ttl-2025-04-11',
},
},
});
import { llm } from '@providerprotocol/ai';
import { anthropic } from '@providerprotocol/ai/anthropic';
import type { AnthropicLLMParams } from '@providerprotocol/ai/anthropic';
import { roundRobinKeys, exponentialBackoff } from '@providerprotocol/ai';
const claude = llm({
model: anthropic('claude-sonnet-4-20250514'),
config: {
apiKey: roundRobinKeys([
process.env.ANTHROPIC_KEY_1!,
process.env.ANTHROPIC_KEY_2!,
]),
baseUrl: 'https://my-proxy.example.com',
timeout: 30000,
retryStrategy: exponentialBackoff({ maxAttempts: 3 }),
},
params: {
max_tokens: 4096,
temperature: 0.7,
} as AnthropicLLMParams,
system: 'You are a helpful assistant.',
});