Skip to main content

Overview

The Pi AI toolkit includes a comprehensive model registry with automatic model discovery for all supported providers. Models are strongly typed and include metadata about capabilities, pricing, and API configuration.

getModel()

Retrieve a specific model from the registry.
function getModel<
  TProvider extends KnownProvider,
  TModelId extends keyof (typeof MODELS)[TProvider]
>(
  provider: TProvider,
  modelId: TModelId
): Model<ModelApi<TProvider, TModelId>>
provider
KnownProvider
required
The provider name. Fully typed with autocomplete support.Supported providers: 'openai', 'anthropic', 'google', 'google-vertex', 'google-gemini-cli', 'amazon-bedrock', 'azure-openai-responses', 'openai-codex', 'github-copilot', 'xai', 'groq', 'cerebras', 'mistral', 'openrouter', 'vercel-ai-gateway', 'zai', 'minimax', 'minimax-cn', 'huggingface', 'kimi-coding', and more.
modelId
string
required
The model identifier. Autocomplete suggests valid models for the provider.Examples:
  • OpenAI: 'gpt-4o', 'gpt-4o-mini', 'o3-mini'
  • Anthropic: 'claude-sonnet-4-20250514', 'claude-3-5-haiku-20241022'
  • Google: 'gemini-2.5-flash', 'gemini-2.0-flash-exp'
Model
Model<TApi>
A fully typed model object containing metadata and configuration.

Example

import { getModel } from '@mariozechner/pi-ai';

// TypeScript provides autocomplete for both provider and model
const model = getModel('openai', 'gpt-4o-mini');

console.log(model.name);              // "GPT-4o mini"
console.log(model.api);               // "openai-responses"
console.log(model.contextWindow);     // 128000
console.log(model.maxTokens);         // 16384
console.log(model.reasoning);         // false
console.log(model.input);             // ["text", "image"]
console.log(model.cost.input);        // 0.15 ($/million tokens)
console.log(model.cost.output);       // 0.6 ($/million tokens)

getProviders()

Get all available providers.
function getProviders(): KnownProvider[]
providers
KnownProvider[]
Array of all registered provider names.

Example

import { getProviders } from '@mariozechner/pi-ai';

const providers = getProviders();
console.log(providers);
// ["openai", "anthropic", "google", "xai", "groq", ...]

getModels()

Get all models from a specific provider.
function getModels<TProvider extends KnownProvider>(
  provider: TProvider
): Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[]
provider
KnownProvider
required
The provider name.
models
Model[]
Array of all models from the provider.

Example

import { getModels } from '@mariozechner/pi-ai';

const anthropicModels = getModels('anthropic');

for (const model of anthropicModels) {
  console.log(`${model.id}: ${model.name}`);
  console.log(`  Context: ${model.contextWindow.toLocaleString()} tokens`);
  console.log(`  Vision: ${model.input.includes('image')}`);
  console.log(`  Reasoning: ${model.reasoning}`);
  console.log(`  Cost: $${model.cost.input}/$${model.cost.output} per million tokens`);
}

Model Type

The Model interface contains all metadata for a model.
interface Model<TApi extends Api> {
  id: string;
  name: string;
  api: TApi;
  provider: Provider;
  baseUrl: string;
  reasoning: boolean;
  input: ("text" | "image")[];
  cost: {
    input: number;      // $/million tokens
    output: number;     // $/million tokens
    cacheRead: number;  // $/million tokens
    cacheWrite: number; // $/million tokens
  };
  contextWindow: number;
  maxTokens: number;
  headers?: Record<string, string>;
  compat?: OpenAICompletionsCompat | OpenAIResponsesCompat;
}
id
string
Model identifier used with the provider’s API.
name
string
Human-readable model name.
api
Api
The API protocol this model uses. Examples: "openai-completions", "anthropic-messages", "google-generative-ai".
provider
Provider
Provider name (e.g., "openai", "anthropic").
baseUrl
string
Base URL for API requests.
reasoning
boolean
Whether the model supports thinking/reasoning capabilities.
input
('text' | 'image')[]
Supported input types. Check model.input.includes('image') for vision support.
cost
object
Pricing in dollars per million tokens.
  • input: Input token cost
  • output: Output token cost
  • cacheRead: Cache read cost (for prompt caching)
  • cacheWrite: Cache write cost (for prompt caching)
contextWindow
number
Maximum context length in tokens.
maxTokens
number
Maximum output tokens per request.
headers
Record<string, string>
Optional custom headers for all requests to this model.
compat
OpenAICompletionsCompat | OpenAIResponsesCompat
Compatibility settings for OpenAI-compatible APIs. See Custom Models below.

Custom Models

Create custom model configurations for local servers or custom endpoints:
import { Model, stream } from '@mariozechner/pi-ai';

// Example: Ollama using OpenAI-compatible API
const ollamaModel: Model<'openai-completions'> = {
  id: 'llama-3.1-8b',
  name: 'Llama 3.1 8B (Ollama)',
  api: 'openai-completions',
  provider: 'ollama',
  baseUrl: 'http://localhost:11434/v1',
  reasoning: false,
  input: ['text'],
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
  contextWindow: 128000,
  maxTokens: 32000
};

// Use the custom model
const response = await stream(ollamaModel, context, {
  apiKey: 'dummy'  // Ollama doesn't need a real key
});

Custom Headers Example

// Custom endpoint with authentication headers
const proxyModel: Model<'anthropic-messages'> = {
  id: 'claude-sonnet-4',
  name: 'Claude Sonnet 4 (Proxied)',
  api: 'anthropic-messages',
  provider: 'custom-proxy',
  baseUrl: 'https://proxy.example.com/v1',
  reasoning: true,
  input: ['text', 'image'],
  cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
  contextWindow: 200000,
  maxTokens: 8192,
  headers: {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
    'X-Custom-Auth': 'bearer-token-here'
  }
};

OpenAI Compatibility Settings

For OpenAI-compatible APIs with non-standard behavior, use the compat field:
const customModel: Model<'openai-completions'> = {
  id: 'custom-model',
  name: 'Custom Model',
  api: 'openai-completions',
  provider: 'custom',
  baseUrl: 'https://api.custom.com/v1',
  reasoning: false,
  input: ['text', 'image'],
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
  contextWindow: 128000,
  maxTokens: 16384,
  compat: {
    supportsStore: false,                    // Provider doesn't support 'store' field
    supportsDeveloperRole: false,            // Use 'system' instead of 'developer'
    maxTokensField: 'max_tokens',            // Use 'max_tokens' instead of 'max_completion_tokens'
    requiresToolResultName: true,            // Tool results require 'name' field
    supportsUsageInStreaming: false,         // No token usage in streaming
    requiresThinkingAsText: true,            // Convert thinking to <thinking> tags
    requiresMistralToolIds: true,            // Normalize tool IDs to 9 chars
    thinkingFormat: 'zai'                    // Use zai-style thinking format
  }
};
compat.supportsStore
boolean
default:"true"
Whether the provider supports the store field.
compat.supportsDeveloperRole
boolean
default:"true"
Whether the provider supports developer role (vs system).
compat.supportsReasoningEffort
boolean
default:"true"
Whether the provider supports reasoning_effort parameter.
compat.supportsUsageInStreaming
boolean
default:"true"
Whether streaming responses include token usage via stream_options.
compat.supportsStrictMode
boolean
default:"true"
Whether the provider supports strict in tool definitions.
compat.maxTokensField
'max_completion_tokens' | 'max_tokens'
default:"'max_completion_tokens'"
Which field name to use for max tokens.
compat.requiresToolResultName
boolean
default:"false"
Whether tool results require the name field.
compat.requiresAssistantAfterToolResult
boolean
default:"false"
Whether a user message after tool results requires an assistant message in between.
compat.requiresThinkingAsText
boolean
default:"false"
Whether thinking blocks must be converted to text with <thinking> delimiters.
compat.requiresMistralToolIds
boolean
default:"false"
Whether tool call IDs must be normalized to Mistral format (exactly 9 alphanumeric chars).
compat.thinkingFormat
'openai' | 'zai' | 'qwen'
default:"'openai'"
Format for reasoning parameter:
  • "openai": Uses reasoning_effort
  • "zai": Uses thinking: { type: "enabled" }
  • "qwen": Uses enable_thinking: boolean

Provider APIs

Each model uses a specific API protocol. Common APIs:
openai-completions
Api
OpenAI Chat Completions API. Used by OpenAI, Mistral, xAI, Groq, Cerebras, and OpenAI-compatible providers.
openai-responses
Api
OpenAI Responses API. Used by OpenAI’s latest models including GPT-5.x and o3.
anthropic-messages
Api
Anthropic Messages API. Used by Claude models.
google-generative-ai
Api
Google Generative AI API. Used by Gemini models via Google AI Studio.
google-vertex
Api
Google Vertex AI API. Used by Gemini models via Google Cloud Vertex AI.
bedrock-converse-stream
Api
Amazon Bedrock Converse Stream API. Used by models on AWS Bedrock.
azure-openai-responses
Api
Azure OpenAI Responses API. Used by models on Azure OpenAI.

Utility Functions

calculateCost()

Calculate costs for a given usage.
function calculateCost<TApi extends Api>(
  model: Model<TApi>,
  usage: Usage
): Usage["cost"]

Example

import { getModel, calculateCost } from '@mariozechner/pi-ai';

const model = getModel('openai', 'gpt-4o-mini');
const usage = {
  input: 1000,
  output: 500,
  cacheRead: 0,
  cacheWrite: 0,
  totalTokens: 1500,
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
};

const cost = calculateCost(model, usage);
console.log(`Total cost: $${cost.total.toFixed(4)}`);

supportsXhigh()

Check if a model supports the xhigh thinking level.
function supportsXhigh<TApi extends Api>(model: Model<TApi>): boolean
Currently supported by:
  • GPT-5.2 and GPT-5.3 model families
  • Anthropic Opus 4.6 models (xhigh maps to adaptive effort “max”)

modelsAreEqual()

Compare two models for equality.
function modelsAreEqual<TApi extends Api>(
  a: Model<TApi> | null | undefined,
  b: Model<TApi> | null | undefined
): boolean
Compares both id and provider fields. Returns false if either model is null/undefined.

Build docs developers (and LLMs) love