Documentation Index Fetch the complete documentation index at: https://mintlify.com/pt-act/pi-mono/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The @mariozechner/pi-ai package provides a unified interface to multiple LLM providers with automatic model discovery, token counting, cost tracking, and cross-provider context handoffs.
Unified API Single interface for OpenAI, Anthropic, Google, and 12+ other providers
Type Safety Full TypeScript support with auto-complete for providers and models
Tool Calling TypeBox schemas with automatic validation and partial JSON streaming
Cross-Provider Handoff Switch models mid-conversation with automatic context transformation
Installation
npm install @mariozechner/pi-ai
Quick Start
import { getModel , complete , stream } from "@mariozechner/pi-ai" ;
// Get a model with full auto-complete support
const model = getModel ( "anthropic" , "claude-sonnet-4-20250514" );
// Simple completion
const response = await complete ( model , {
systemPrompt: "You are a helpful assistant." ,
messages: [
{ role: "user" , content: "Hello!" }
]
});
console . log ( response . content [ 0 ]. text );
console . log ( `Tokens: ${ response . usage . input } / ${ response . usage . output } ` );
console . log ( `Cost: $ ${ response . usage . cost . total } ` );
Key Features
Supported Providers
All providers support tool calling (function calling) for agentic workflows:
API Key Providers:
OpenAI (GPT-4o, GPT-5, o1, o3)
Anthropic (Claude Sonnet, Opus, Haiku)
Google Gemini
Amazon Bedrock
Mistral AI
Groq
Cerebras
xAI (Grok)
OpenRouter
Vercel AI Gateway
zAI
MiniMax
Kimi For Coding
Hugging Face
OAuth Providers:
OpenAI Codex (ChatGPT Plus/Pro, GPT-5.x Codex models)
GitHub Copilot
Google Gemini CLI (Cloud Code Assist)
Google Antigravity (free Gemini, Claude, GPT-OSS)
Cloud Providers:
Azure OpenAI (Responses API)
Google Vertex AI (with ADC)
Custom:
Any OpenAI-compatible API (Ollama, vLLM, LM Studio, etc.)
Define tools with TypeBox schemas for type-safe validation:
import { Type , Tool , complete } from "@mariozechner/pi-ai" ;
const tools : Tool [] = [{
name: "get_weather" ,
description: "Get current weather for a location" ,
parameters: Type . Object ({
location: Type . String ({ description: "City name" }),
units: Type . Optional ( Type . Union ([
Type . Literal ( "celsius" ),
Type . Literal ( "fahrenheit" )
]))
})
}];
const response = await complete ( model , {
messages: [{ role: "user" , content: "What's the weather in London?" }],
tools
});
// Handle tool calls
for ( const block of response . content ) {
if ( block . type === "toolCall" ) {
console . log ( `Tool: ${ block . name } ` );
console . log ( `Args: ${ JSON . stringify ( block . arguments ) } ` );
}
}
Streaming with Events
Stream responses with granular event types:
import { stream } from "@mariozechner/pi-ai" ;
const s = stream ( model , context );
for await ( const event of s ) {
switch ( event . type ) {
case "text_delta" :
process . stdout . write ( event . delta );
break ;
case "toolcall_end" :
console . log ( ` \n Tool: ${ event . toolCall . name } ` );
break ;
case "thinking_delta" :
// For reasoning models
process . stdout . write ( event . delta );
break ;
case "done" :
console . log ( ` \n Finished: ${ event . reason } ` );
break ;
}
}
const finalMessage = await s . result ();
Event Types:
start - Stream begins
text_start, text_delta, text_end - Text generation
thinking_start, thinking_delta, thinking_end - Reasoning/thinking
toolcall_start, toolcall_delta, toolcall_end - Tool calls
done - Completion
error - Error or abort
During streaming, tool arguments are progressively parsed:
for await ( const event of s ) {
if ( event . type === "toolcall_delta" ) {
const toolCall = event . partial . content [ event . contentIndex ];
if ( toolCall . type === "toolCall" && toolCall . arguments ) {
// Arguments may be incomplete - check before using
if ( toolCall . arguments . filename ) {
console . log ( `Writing to: ${ toolCall . arguments . filename } ` );
}
}
}
}
Reasoning/Thinking Models
Many models support extended thinking capabilities:
import { streamSimple , completeSimple } from "@mariozechner/pi-ai" ;
const model = getModel ( "anthropic" , "claude-sonnet-4-20250514" );
// Also: openai/gpt-5-mini, google/gemini-2.5-flash, xai/grok-code-fast-1
const response = await completeSimple ( model , {
messages: [{ role: "user" , content: "Solve: 2x + 5 = 13" }]
}, {
reasoning: "medium" // minimal | low | medium | high | xhigh
});
for ( const block of response . content ) {
if ( block . type === "thinking" ) {
console . log ( "Thinking:" , block . thinking );
} else if ( block . type === "text" ) {
console . log ( "Response:" , block . text );
}
}
Cross-Provider Handoffs
Switch models mid-conversation - the library handles context transformation automatically:
import { getModel , complete , Context } from "@mariozechner/pi-ai" ;
const context : Context = {
messages: []
};
// Start with Claude
const claude = getModel ( "anthropic" , "claude-sonnet-4-20250514" );
context . messages . push ({ role: "user" , content: "What is 25 * 18?" });
const claudeResponse = await complete ( claude , context , { thinkingEnabled: true });
context . messages . push ( claudeResponse );
// Switch to GPT-5 - Claude's thinking becomes <thinking> tagged text
const gpt5 = getModel ( "openai" , "gpt-5-mini" );
context . messages . push ({ role: "user" , content: "Is that correct?" });
const gptResponse = await complete ( gpt5 , context );
context . messages . push ( gptResponse );
// Switch to Gemini
const gemini = getModel ( "google" , "gemini-2.5-flash" );
context . messages . push ({ role: "user" , content: "What was the original question?" });
const geminiResponse = await complete ( gemini , context );
Image Support
Vision-capable models can process images:
import { readFileSync } from "fs" ;
import { getModel , complete } from "@mariozechner/pi-ai" ;
const model = getModel ( "openai" , "gpt-4o" );
if ( model . input . includes ( "image" )) {
const imageBuffer = readFileSync ( "screenshot.png" );
const base64Image = imageBuffer . toString ( "base64" );
const response = await complete ( model , {
messages: [{
role: "user" ,
content: [
{ type: "text" , text: "What's in this image?" },
{ type: "image" , data: base64Image , mimeType: "image/png" }
]
}]
});
}
Token Counting and Costs
Every response includes detailed usage information:
const response = await complete ( model , context );
console . log ( response . usage );
// {
// input: 150,
// output: 75,
// cacheRead: 0,
// cacheWrite: 100,
// cost: {
// input: 0.00045,
// output: 0.001125,
// cacheRead: 0,
// cacheWrite: 0.000375,
// total: 0.00195
// }
// }
Custom Models
Define custom models for local servers or proxies:
import { Model , stream } from "@mariozechner/pi-ai" ;
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
};
await stream ( ollamaModel , context , { apiKey: "dummy" });
API Reference
Core Functions
// Get models
function getProviders () : string [];
function getModels ( provider : string ) : Model [];
function getModel < P extends KnownProvider >( provider : P , modelId : string ) : Model ;
// Completion (non-streaming)
function complete < A extends KnownApi >(
model : Model < A >,
context : Context ,
options ?: StreamOptions
) : Promise < AssistantMessage >;
function completeSimple < A extends KnownApi >(
model : Model < A >,
context : SimpleContext ,
options ?: SimpleStreamOptions
) : Promise < AssistantMessage >;
// Streaming
function stream < A extends KnownApi >(
model : Model < A >,
context : Context ,
options ?: StreamOptions
) : AssistantMessageEventStream ;
function streamSimple < A extends KnownApi >(
model : Model < A >,
context : SimpleContext ,
options ?: SimpleStreamOptions
) : AssistantMessageEventStream ;
// Tool validation
function validateToolCall (
tools : Tool [],
toolCall : { name : string ; arguments : any }
) : any ;
Types
interface Context {
systemPrompt ?: string ;
messages : Message [];
tools ?: Tool [];
}
interface Message {
role : "user" | "assistant" | "toolResult" ;
content : ContentBlock [] | string ;
// ... additional fields
}
interface Tool {
name : string ;
description : string ;
parameters : TSchema ; // TypeBox schema
}
interface AssistantMessage {
role : "assistant" ;
content : ContentBlock [];
stopReason : "stop" | "length" | "toolUse" | "error" | "aborted" ;
usage : Usage ;
timestamp : number ;
}
For complete type definitions, see Stream and Complete API Reference .
OAuth Authentication
For providers requiring OAuth (Codex, Copilot, Gemini CLI, Antigravity):
# CLI login
npx @mariozechner/pi-ai login anthropic
npx @mariozechner/pi-ai login github-copilot
// Programmatic OAuth
import { loginGitHubCopilot , getOAuthApiKey } from "@mariozechner/pi-ai" ;
const credentials = await loginGitHubCopilot ({
onAuth : ( url ) => console . log ( `Open: ${ url } ` ),
onProgress : ( msg ) => console . log ( msg )
});
// Store credentials securely
const auth = { "github-copilot" : { type: "oauth" , ... credentials } };
// Use in requests
const result = await getOAuthApiKey ( "github-copilot" , auth );
if ( result ) {
await complete ( model , context , { apiKey: result . apiKey });
}
Environment Variables
Provider API keys can be set via environment variables:
export ANTHROPIC_API_KEY = sk-ant- ...
export OPENAI_API_KEY = sk- ...
export GEMINI_API_KEY = ...
export GROQ_API_KEY = ...
export XAI_API_KEY = ...
# ... and more
See Installation Guide for the complete list.
Links