llm-provider-registry
v1.0.0
Published
Map any LLM model name (glm-4.6, deepseek-chat, kimi-k2, grok-4, claude-…, gpt-…) to its API base URL + key env var. Curated for 16 providers — incl. the Chinese ones the big frameworks don't ship. Zero deps.
Maintainers
Readme
llm-provider-registry
Give it a model name, get back where to send the request and which key to use.
"glm-4.6" → https://open.bigmodel.cn/api/paas/v4 · ZHIPU_API_KEY · OpenAI-compatible
"deepseek-chat" → https://api.deepseek.com/v1 · DEEPSEEK_API_KEY · OpenAI-compatible
"kimi-k2" → https://api.moonshot.cn/v1 · MOONSHOT_API_KEY · OpenAI-compatible
"claude-sonnet-4-6" → https://api.anthropic.com · ANTHROPIC_API_KEY· Anthropic SDK
"gpt-5" → https://api.openai.com/v1 · OPENAI_API_KEY · OpenAI SDKA tiny, zero-dependency lookup table + resolver. It knows 16 providers — including the Chinese ones (Zhipu/GLM, DeepSeek, Moonshot/Kimi, Qwen, Doubao, MiniMax, Baichuan, Yi, Stepfun, Hunyuan) that the big frameworks make you wire up by hand.
The problem it solves
You want to let users pick any model — gpt-5, deepseek-chat, glm-4.6, qwen3-max, claude-… — and Just Work. Every one of those is reachable through the OpenAI chat-completions API, so in principle you only need one SDK. The catch is the plumbing: each provider has its own base URL and its own API-key env var, and nobody ships a maintained table of them — the OpenAI SDK obviously doesn't, and the big frameworks skip most of the Chinese providers.
So everyone ends up hand-copying base URLs off docs pages into a switch statement. This is that switch statement, curated and tested, as a 2 kB import.
import OpenAI from "openai";
import { resolveModel } from "llm-provider-registry";
function clientFor(model) {
const p = resolveModel(model);
if (!p) throw new Error(`Unknown model: ${model}`);
if (!p.openaiCompatible) throw new Error(`${p.provider} needs its own SDK, not OpenAI's`);
return new OpenAI({ baseURL: p.baseURL, apiKey: p.apiKey });
}
// One line, any provider:
const res = await clientFor("glm-4.6").chat.completions.create({
model: "glm-4.6",
messages: [{ role: "user", content: "hi" }],
});Install
npm install llm-provider-registryNode ≥ 18. ESM. Ships its own TypeScript types.
API
resolveModel(model, env?) → ResolvedModel | null
Returns null if the model isn't recognized, otherwise:
{
provider: string; // "Zhipu (GLM)", "DeepSeek", "Anthropic", …
kind: "anthropic" | "openai" | "gemini" | "openai-compatible";
baseURL: string; // point your client here
apiKeyEnv: string; // e.g. "ZHIPU_API_KEY"
apiKey: string | undefined;// that var's value, read from `env` (default process.env)
openaiCompatible: boolean; // true → use the OpenAI SDK against baseURL
}env defaults to process.env; pass your own object to resolve keys from somewhere else (tests, a config store, per-request overrides).
openaiCompatible is the branch that matters. It's true for everything except Anthropic (claude-*), which speaks a different wire protocol — send those to @anthropic-ai/sdk. OpenAI, Gemini (via its OpenAI-compat endpoint), and all 13 presets are true.
hasCredentials(model, env?) → boolean
true only when the model is recognized and its API-key env var is set. Handy for filtering a model menu down to the ones the user can actually call:
const usable = ALL_MODELS.filter((m) => hasCredentials(m));PRESETS
The raw table of OpenAI-compatible third-party providers, if you'd rather iterate it yourself:
{ name: string; modelPrefixes: string[]; baseURL: string; apiKeyEnv: string }[]Providers covered
| Provider | Example model prefixes | Base URL | API-key env |
|---|---|---|---|
| Anthropic | claude- | api.anthropic.com | ANTHROPIC_API_KEY |
| OpenAI | gpt-, o1, o3, o4, chatgpt- | api.openai.com/v1 | OPENAI_API_KEY |
| Google Gemini | gemini- | …/v1beta/openai/ | GEMINI_API_KEY |
| DeepSeek | deepseek- | api.deepseek.com/v1 | DEEPSEEK_API_KEY |
| Mistral AI | mistral-, codestral-, magistral-, ministral-, pixtral- | api.mistral.ai/v1 | MISTRAL_API_KEY |
| Perplexity (Sonar) | sonar, sonar- | api.perplexity.ai | PERPLEXITY_API_KEY |
| xAI Grok | grok- | api.x.ai/v1 | XAI_API_KEY |
| Volcengine Ark (Doubao) | doubao-, ep- | ark.cn-beijing.volces.com/api/v3 | ARK_API_KEY |
| Aliyun DashScope (Qwen) | qwen-, qwen2, qwen3 | dashscope.aliyuncs.com/compatible-mode/v1 | DASHSCOPE_API_KEY |
| Moonshot (Kimi) | moonshot-, kimi- | api.moonshot.cn/v1 | MOONSHOT_API_KEY |
| Zhipu (GLM) | glm-, chatglm- | open.bigmodel.cn/api/paas/v4 | ZHIPU_API_KEY |
| Stepfun (Step) | step- | api.stepfun.com/v1 | STEPFUN_API_KEY |
| 01.AI (Yi) | yi- | api.lingyiwanwu.com/v1 | LINGYI_API_KEY |
| Baichuan | baichuan-, baichuan2/3/4 | api.baichuan-ai.com/v1 | BAICHUAN_API_KEY |
| MiniMax | abab, minimax- | api.minimax.io/v1 | MINIMAX_API_KEY |
| Tencent Hunyuan | hunyuan- | api.hunyuan.cloud.tencent.com/v1 | HUNYUAN_API_KEY |
Matching is by case-insensitive prefix, so glm-4.6, glm-4-flash, and GLM-4.6 all resolve to Zhipu — new model releases usually keep the family prefix and Just Work without a library bump.
What it deliberately isn't
- Not an API client. It hands you a
baseURL+ key; you bring your own SDK. That's the point — it stays 2 kB and never breaks when a provider tweaks a response field. - Not a param translator. It doesn't reshape requests between the Anthropic and OpenAI schemas. It only tells you which family a model belongs to (
kind) so you know which SDK to reach for. - Not exhaustive on models. It matches families by prefix, not an ever-changing list of exact model IDs.
License
MIT © 2026 oratis
