Chapter 3 · Providers and Model Catalog
Chapter goals: Master the full landscape of built-in providers,
getModelprecise lookup, model metadata fields, and the difference between static catalogs and dynamic providers.
3.1 Built-in Provider Landscape
pi-ai only includes models that support tool calling (this is a hard requirement for the Agent workflow). Major vendors:
| Provider | ID | Environment Variable |
|---|---|---|
| OpenAI | openai | OPENAI_API_KEY |
| Anthropic | anthropic | ANTHROPIC_API_KEY or ANTHROPIC_OAUTH_TOKEN |
google | GEMINI_API_KEY | |
| DeepSeek | deepseek | DEEPSEEK_API_KEY |
| xAI | xai | XAI_API_KEY |
| Groq | groq | GROQ_API_KEY |
| Cerebras | cerebras | CEREBRAS_API_KEY |
| Mistral | mistral | MISTRAL_API_KEY |
| OpenRouter | openrouter | OPENROUTER_API_KEY |
| Amazon Bedrock | amazon-bedrock | AWS credential chain |
| Vertex AI | google-vertex | ADC / GOOGLE_CLOUD_API_KEY |
| GitHub Copilot | github-copilot | OAuth sign-in |
| Moonshot | moonshot | MOONSHOT_API_KEY |
Additionally there are Azure OpenAI, NVIDIA NIM, Together, Fireworks, MiniMax, Qwen, and others, plus any OpenAI-compatible endpoint (Ollama/vLLM/LM Studio) — Chapter 14 covers how to customize.
3.2 getModel: Synchronous Precise Lookup
Reads on the collection are synchronous, returning the last-known catalog:
// Three commonly used query methods
const models = builtinModels();
// 1. Look up a single model: provider id + model id
const model = models.getModel("anthropic", "claude-sonnet-4-6");
if (!model) throw new Error("Model not found"); // returns undefined if not found
// 2. List all models from a single provider
const anthropicModels = models.getModels("anthropic");
// 3. List all registered providers
const providers = models.getProviders();3.3 Reading Model Metadata
Each Model object carries the key information needed for decision-making:
for (const m of models.getModels("openai").slice(0, 5)) {
console.log(`${m.id}: ${m.name}`);
console.log(` API protocol: ${m.api}`); // openai-responses, etc.
console.log(` Context window: ${m.contextWindow}`); // token limit
console.log(` Vision input: ${m.input.includes("image")}`); // can read images
console.log(` Supports reasoning: ${m.reasoning}`); // has thinking capability
}These fields can be used directly for runtime selection logic:
// Example: automatically pick a vision-capable model with the largest context window
const visionModel = models
.getModels("google")
.filter((m) => m.input.includes("image"))
.sort((a, b) => b.contextWindow - a.contextWindow)[0];
console.log(visionModel?.name);3.4 hasApi Type Guard
Dynamically queried models have a broad type; you need hasApi() to narrow them before getting complete type hints for API-specific options:
import { hasApi } from "@earendil-works/pi-ai";
const m = models.getModel("anthropic", "claude-sonnet-4-6")!;
if (hasApi(m, "anthropic-messages")) {
// Here m is narrowed to Model<'anthropic-messages'>,
// and stream options have complete Anthropic-native parameters and autocompletion
models.stream(m, context, {
thinkingEnabled: true,
thinkingBudgetTokens: 2048,
});
}3.5 Static Catalog vs. Dynamic Refresh
import {
getBuiltinModel, // Static single lookup (complete literal types, IDs auto-complete)
getBuiltinProviders,
} from "@earendil-works/pi-ai/providers/all";
// Static read: does not depend on any collection instance, suitable for toolchains/config validation
const m2 = getBuiltinModel("openai", "gpt-4o-mini");
// Dynamic providers (e.g. local llama.cpp service, OpenRouter live list)
// Catalog requires explicit async refresh:
await models.refresh({ providers: ["openrouter"] }); // refresh only one vendor
await models.refresh(); // refresh all in parallel (best-effort)
const fresh = models.getModel("llamacpp", "qwen3-30b");Static built-in providers are a no-op for refresh(); only dynamic providers will actually make a network request to fetch the latest list. Reads are always synchronous — refresh first, then read.
3.6 Chapter Summary
- Built-in providers cover major vendors plus any OpenAI-compatible endpoint; only models supporting tool calling are included;
getModel(provider, id)performs a synchronous lookup, returningundefinedon miss;- Model metadata includes
api/contextWindow/input/reasoning/cost, usable for runtime selection; hasApi()narrows a broad model to a specific API type to unlock complete option hints;- Dynamic provider catalogs need an explicit
refresh()before synchronous reads.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Why does pi-ai only include models that support tool calling (function calling)?
2. What happens when you call `models.getModel("anthropic", "nonexistent")`?
3. Regarding hasApi(m, "anthropic-messages"), which statement is correct?
4. What happens when you call models.refresh() on a static built-in provider?
🛠️ Hands-on Practice
- Print all models under
builtinModels()for both Anthropic and OpenAI; find the one with the largest context window and the cheapest one for each. - Write a function
pick(modelsWithReasoning): given any provider name, return the first reasoning-capable model, and safely enable thinking usinghasApi. - Use
getBuiltinProviders()to list all built-in vendor ID counts and compare with the table in this chapter.
Once you are familiar with the catalog, move on to Chapter 4 to officially create your first Agent.