#!/usr/bin/env node
const fs = require("node:fs");
const path = require("node:path");
const repoRoot = path.resolve(__dirname, "..", "..");
const registryPath = path.join(
repoRoot,
"packages/core/src/registries/model-provider-registry.generated.ts",
);
const modelsPath = path.join(
repoRoot,
"packages/core/src/registries/model-provider-types.generated.ts",
);
const docsRoot = path.join(repoRoot, "website/models-docs");
const providersDir = path.join(docsRoot, "providers");
const readFile = (filePath) => fs.readFileSync(filePath, "utf8");
const parseRegistry = (source) => {
const marker = "MODEL_PROVIDER_REGISTRY";
const start = source.indexOf(marker);
if (start === -1) {
throw new Error("MODEL_PROVIDER_REGISTRY not found.");
}
const braceStart = source.indexOf("{", start);
const braceEnd = source.lastIndexOf("};");
if (braceStart === -1 || braceEnd === -1) {
throw new Error("Unable to parse provider registry object.");
}
const objectLiteral = source.slice(braceStart, braceEnd + 1);
return new Function(`return ${objectLiteral};`)();
};
const parseProviderModels = (source) => {
const providerRegex = /readonly\s+'([^']+)':\s+readonly\s+\[\n([\s\S]*?)\n\s*\];/g;
const modelRegex = /'([^']+)'/g;
const map = {};
for (const match of source.matchAll(providerRegex)) {
const providerId = match[1];
const block = match[2];
const models = [];
for (const modelMatch of block.matchAll(modelRegex)) {
models.push(modelMatch[1]);
}
map[providerId] = models;
}
return map;
};
const formatEnvList = (env) => {
if (!env || env.length === 0) {
return "None listed.";
}
return env.map((name) => `- \`${name}\``).join("\n");
};
const formatTableEnv = (env) => {
if (!env || env.length === 0) {
return "-";
}
return env.map((name) => `\`${name}\``).join("
");
};
const formatModelList = (models) => {
if (!models || models.length === 0) {
return "No models listed in the registry snapshot.";
}
const lines = models.map((model) => `- ${model}`).join("\n");
return [
"",
` Show models (${models.length})
`,
"",
lines,
"",
" ",
].join("\n");
};
const autoGeneratedNotice =
"";
const buildOverview = (providerCount, modelCount) => `---
title: Models
slug: /
sidebar_position: 1
description: Explore ${providerCount}+ providers and ${modelCount}+ models supported by VoltAgent's model registry.
---
${autoGeneratedNotice}
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Models
Explore ${providerCount}+ providers and ${modelCount}+ models using VoltAgent's built-in model registry. Use \`provider/model\` strings for fast routing, or pass an ai-sdk \`LanguageModel\` when you need provider-specific control.
The registry is generated from [models.dev](https://models.dev) and bundled with VoltAgent. At runtime, VoltAgent checks required environment variables and reports the exact one that's missing.
## Highlights
- **Zero-import model strings** - Use \`provider/model\` IDs without adding provider packages.
- **Registry-backed env mapping** - VoltAgent knows which env vars each provider expects.
- **Type-aware model IDs** - \`ModelRouterModelId\` adds autocomplete and validation.
- **Runtime routing** - Pick models dynamically per request or tenant.
- **Bring your own LanguageModel** - Drop in ai-sdk providers for advanced options.
## Quick start (model strings)
\`\`\`ts
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "openai-summary",
instructions: "Summarize the update in 2 bullets.",
model: "openai/gpt-4.1-mini",
});
\`\`\`
\`\`\`ts
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "claude-notes",
instructions: "Turn notes into action items.",
model: "anthropic/claude-3-5-haiku",
});
\`\`\`
\`\`\`ts
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "gemini-translator",
instructions: "Translate to Turkish and keep tone friendly.",
model: "google/gemini-2.0-flash",
});
\`\`\`
\`\`\`ts
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "grok-ideas",
instructions: "Brainstorm five product names.",
model: "xai/grok-3-mini",
});
\`\`\`
\`\`\`ts
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "openrouter-agent",
instructions: "Answer in short paragraphs.",
model: "openrouter/anthropic/claude-3.5-haiku",
});
\`\`\`
## Provider directory
Browse provider pages in the left navigation or visit the [Providers directory](/models-docs/providers/overview) for the full list.
For full model inventories per provider, see each provider page or explore [models.dev](https://models.dev).
## Type-safe model IDs
Use \`ModelRouterModelId\` to get IDE autocomplete for model strings:
\`\`\`ts
import type { ModelRouterModelId } from "@voltagent/core";
const modelId: ModelRouterModelId = "openai/gpt-4.1-mini";
\`\`\`
## Split workloads across models
Assign cheaper models to throughput-heavy steps and stronger models to critical analysis:
\`\`\`ts
import { Agent } from "@voltagent/core";
const ingestAgent = new Agent({
name: "ingest-agent",
instructions: "Extract entities and key facts from raw notes.",
model: "google/gemini-2.0-flash",
});
const reviewAgent = new Agent({
name: "review-agent",
instructions: "Review the summary and flag risks or gaps.",
model: "anthropic/claude-3-5-sonnet",
});
\`\`\`
## Runtime model selection
Pick a model based on request context:
\`\`\`ts
const agent = new Agent({
name: "runtime-router",
model: ({ context }) => {
const tier = (context.get("tier") as string) || "fast";
return tier === "fast"
? "openai/gpt-4.1-mini"
: "anthropic/claude-3-5-sonnet";
},
});
\`\`\`
## Provider options
Pass provider-specific options per request when you need them:
\`\`\`ts
const analyst = new Agent({
name: "analyst",
instructions: "Explain tradeoffs clearly and concisely.",
model: "openai/o3-mini",
});
const response = await analyst.generateText("Compare JWTs vs cookies for auth.", {
providerOptions: {
openai: { reasoningEffort: "high" },
},
});
\`\`\`
## Custom headers
If you need custom headers, pass an ai-sdk \`LanguageModel\` directly:
\`\`\`ts
import { Agent } from "@voltagent/core";
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
const customProvider = createOpenAICompatible({
name: "openai",
baseURL: "https://api.openai.com/v1",
apiKey: process.env.OPENAI_API_KEY,
headers: {
"X-Client-Source": "voltagent-docs",
},
});
const agent = new Agent({
name: "custom-agent",
model: customProvider("gpt-4o-mini"),
});
\`\`\`
:::note
VoltAgent does not include fallback chains yet. Implement retries or failover in your app if needed.
:::
## Use ai-sdk directly
You can use ai-sdk provider modules anywhere VoltAgent expects a \`LanguageModel\`:
\`\`\`ts
import { mistral } from "@ai-sdk/mistral";
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "mistral-agent",
model: mistral("mistral-small-latest"),
});
\`\`\`
`;
const buildProvidersOverview = (providerIds, registry, _providerModels) => {
const rows = providerIds
.map((providerId) => {
const entry = registry[providerId];
const name = entry?.name ?? providerId;
const env = formatTableEnv(entry?.env);
const doc = entry?.doc ? `[Docs](${entry.doc})` : "-";
return `| ${name} | \`${providerId}\` | ${env} | ${doc} |`;
})
.join("\n");
return `---
title: Providers
description: Browse all providers supported by VoltAgent's model router.
sidebar_label: Overview
sidebar_position: 1
---
${autoGeneratedNotice}
# Providers
Each provider page includes:
- A usage example with \`provider/model\`
- Required environment variables
- Links to provider documentation
- Model list from the registry snapshot
## Provider directory
| Provider | Model prefix | Environment variables | Docs |
| --- | --- | --- | --- |
${rows}
`;
};
const preferredModelByProvider = {
anthropic: "claude-3-5-sonnet",
evroc: "Qwen/Qwen3.8-27B",
google: "gemini-2.0-flash",
"google-vertex": "gemini-3.5-flash",
nearai: "Qwen/Qwen3.8-27B",
openai: "gpt-4o-mini",
openrouter: "anthropic/claude-haiku-4-5",
xai: "grok-4",
};
const buildProviderDoc = (providerId, entry, models) => {
const name = entry?.name ?? providerId;
const docLink = entry?.doc ? `- ${entry.doc}` : "- Not available.";
const envBlock = formatEnvList(entry?.env);
const baseUrlEnv = `${providerId.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
const baseUrl = entry?.api && entry.api.trim().length > 0 ? `\`${entry.api}\`` : "Not listed.";
const usesOpenAICompatible = entry?.npm === "@ai-sdk/openai-compatible";
const providerPackage = entry?.npm ? `\`${entry.npm}\`` : "Not listed.";
const fallbackModel = models?.[0];
const preferredModel = preferredModelByProvider[providerId];
const exampleModel =
preferredModel && models?.includes(preferredModel)
? preferredModel
: fallbackModel || "model-id";
const exampleModelId = `${providerId}/${exampleModel}`;
return `---
title: ${name}
---
${autoGeneratedNotice}
# ${name}
Use \`${providerId}/\` with VoltAgent's model router.
## Quick start
\`\`\`ts
import { Agent } from "@voltagent/core";
const agent = new Agent({
name: "${providerId}-agent",
instructions: "You are a helpful assistant",
model: "${exampleModelId}",
});
\`\`\`
## Environment variables
${envBlock}
## Provider package
${providerPackage}
${usesOpenAICompatible ? "This provider uses the OpenAI-compatible adapter." : ""}
## Default base URL
${baseUrl}
You can override the base URL by setting \`${baseUrlEnv}\`.
## Provider docs
${docLink}
## Models
${formatModelList(models)}
`;
};
const main = () => {
const registrySource = readFile(registryPath);
const modelsSource = readFile(modelsPath);
const registry = parseRegistry(registrySource);
const providerModels = parseProviderModels(modelsSource);
const providerIds = Object.keys(registry).sort((a, b) => a.localeCompare(b));
const providerCount = providerIds.length;
const modelCount = providerIds.reduce(
(total, providerId) => total + (providerModels[providerId]?.length ?? 0),
0,
);
fs.mkdirSync(providersDir, { recursive: true });
fs.writeFileSync(
path.join(docsRoot, "overview.md"),
buildOverview(providerCount, modelCount),
"utf8",
);
fs.writeFileSync(
path.join(providersDir, "overview.md"),
buildProvidersOverview(providerIds, registry, providerModels),
"utf8",
);
for (const providerId of providerIds) {
const entry = registry[providerId];
const models = providerModels[providerId] ?? [];
const content = buildProviderDoc(providerId, entry, models);
fs.writeFileSync(path.join(providersDir, `${providerId}.md`), content, "utf8");
}
// eslint-disable-next-line no-console
console.log(`Generated Models docs for ${providerCount} providers (${modelCount} models).`);
};
main();