ai-sdk-provider-env 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/dist/index.cjs +71 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +16 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +71 -12
- package/dist/index.mjs.map +1 -1
- package/llms.txt +2 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -74,6 +74,7 @@ provider.languageModel('my_api/some-model') // same env vars
|
|
|
74
74
|
| `{PREFIX}_PRESET` | No | Built-in preset name (e.g. `openai`) |
|
|
75
75
|
| `{PREFIX}_COMPATIBLE` | No | `openai` · `anthropic` · `gemini` · `openai-compatible` (default) |
|
|
76
76
|
| `{PREFIX}_HEADERS` | No | Custom HTTP headers (JSON) |
|
|
77
|
+
| `{PREFIX}_NATIVE_ROUTING` | No | Enable/disable native model routing (`true`/`false`) |
|
|
77
78
|
|
|
78
79
|
When `_PRESET` is set or [auto-detected](#built-in-presets), `_BASE_URL` and `_COMPATIBLE` fall back to preset defaults.
|
|
79
80
|
|
|
@@ -105,7 +106,7 @@ provider.languageModel('deepseek/deepseek-chat') // just works
|
|
|
105
106
|
| `openai` | `https://api.openai.com/v1` | `openai` |
|
|
106
107
|
| `anthropic` | `https://api.anthropic.com` | `anthropic` |
|
|
107
108
|
| `google` | `https://generativelanguage.googleapis.com/v1beta` | `gemini` |
|
|
108
|
-
| `opencode-zen` | `https://opencode.ai/zen/v1` | `openai-compatible` |
|
|
109
|
+
| `opencode-zen` | `https://opencode.ai/zen/v1` | `openai-compatible` (nativeRouting enabled) |
|
|
109
110
|
| `opencode-go` | `https://opencode.ai/zen/go/v1` | `openai-compatible` |
|
|
110
111
|
| `deepseek` | `https://api.deepseek.com` | `openai-compatible` |
|
|
111
112
|
| `groq` | `https://api.groq.com/openai/v1` | `openai-compatible` |
|
|
@@ -124,6 +125,35 @@ provider.languageModel('deepseek/deepseek-chat') // just works
|
|
|
124
125
|
|
|
125
126
|
To disable auto-detection: `envProvider({ presetAutoDetect: false })`. See [Advanced Usage](./docs/advanced.md#preset-auto-detect) for details.
|
|
126
127
|
|
|
128
|
+
## Native Routing
|
|
129
|
+
|
|
130
|
+
When a config set uses a gateway that exposes multiple AI providers (like `opencode-zen`), `nativeRouting` auto-detects the model family from the model ID prefix and routes it to the appropriate native SDK:
|
|
131
|
+
|
|
132
|
+
- `claude-*` → `@ai-sdk/anthropic`
|
|
133
|
+
- `gemini-*` → `@ai-sdk/google`
|
|
134
|
+
- `gpt-*` → `@ai-sdk/openai`
|
|
135
|
+
- Other models fall back to the config set's default `compatible` mode
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# opencode-zen preset has nativeRouting enabled — just set API key
|
|
139
|
+
OPENCODE_ZEN_API_KEY=zen-xxx
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// Routes to native Anthropic SDK automatically
|
|
144
|
+
provider.languageModel('opencode-zen/claude-sonnet-4-20250514')
|
|
145
|
+
|
|
146
|
+
// Routes to native Google SDK automatically
|
|
147
|
+
provider.languageModel('opencode-zen/gemini-3-flash')
|
|
148
|
+
|
|
149
|
+
// Other models use openai-compatible
|
|
150
|
+
provider.languageModel('opencode-zen/minimax-m2.5')
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
To disable: `OPENCODE_ZEN_NATIVE_ROUTING=false`
|
|
154
|
+
|
|
155
|
+
> **Known limitation**: `o1-*`, `o3-*`, `chatgpt-*` models are not automatically routed. Use `{PREFIX}_COMPATIBLE=openai` explicitly for these.
|
|
156
|
+
|
|
127
157
|
## Documentation
|
|
128
158
|
|
|
129
159
|
- **[API Reference](./docs/api-reference.md)** — `envProvider()` options, types, model ID format
|
package/dist/index.cjs
CHANGED
|
@@ -177,7 +177,8 @@ const builtinPresets = {
|
|
|
177
177
|
},
|
|
178
178
|
"opencode-zen": {
|
|
179
179
|
baseURL: "https://opencode.ai/zen/v1",
|
|
180
|
-
compatible: "openai-compatible"
|
|
180
|
+
compatible: "openai-compatible",
|
|
181
|
+
nativeRouting: true
|
|
181
182
|
},
|
|
182
183
|
"opencode-go": {
|
|
183
184
|
baseURL: "https://opencode.ai/zen/go/v1",
|
|
@@ -207,6 +208,20 @@ const defaultFactories = {
|
|
|
207
208
|
createOpenAICompatible: createOpenAICompatibleProvider
|
|
208
209
|
};
|
|
209
210
|
/**
|
|
211
|
+
* Detect the native compatible mode for a model based on its ID prefix.
|
|
212
|
+
*
|
|
213
|
+
* Used by nativeRouting to auto-route model families to their native provider SDKs.
|
|
214
|
+
* Only `claude-*`, `gemini-*`, and `gpt-*` prefixes are matched.
|
|
215
|
+
* Known limitation: `o1-*`, `o3-*`, `chatgpt-*` are NOT matched (use explicit compatible mode).
|
|
216
|
+
*
|
|
217
|
+
* @returns The detected compatible mode, or `undefined` if no match.
|
|
218
|
+
*/
|
|
219
|
+
function detectNativeCompatible(model) {
|
|
220
|
+
if (model.startsWith("claude-")) return "anthropic";
|
|
221
|
+
if (model.startsWith("gemini-")) return "gemini";
|
|
222
|
+
if (model.startsWith("gpt-")) return "openai";
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
210
225
|
* Testable core implementation that accepts injected provider factories.
|
|
211
226
|
*
|
|
212
227
|
* In tests, call this function directly with fake factories
|
|
@@ -228,7 +243,8 @@ function createEnvProvider(factories, options = {}) {
|
|
|
228
243
|
}
|
|
229
244
|
return {
|
|
230
245
|
baseURL: preset.baseURL,
|
|
231
|
-
compatible: preset.compatible ?? "openai-compatible"
|
|
246
|
+
compatible: preset.compatible ?? "openai-compatible",
|
|
247
|
+
nativeRouting: preset.nativeRouting
|
|
232
248
|
};
|
|
233
249
|
}
|
|
234
250
|
/**
|
|
@@ -243,6 +259,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
243
259
|
baseURL: config.baseURL ?? preset.baseURL,
|
|
244
260
|
apiKey: config.apiKey,
|
|
245
261
|
compatible: config.compatible ?? preset.compatible,
|
|
262
|
+
nativeRouting: config.nativeRouting ?? preset.nativeRouting,
|
|
246
263
|
...config.headers && { headers: config.headers }
|
|
247
264
|
};
|
|
248
265
|
}
|
|
@@ -251,6 +268,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
251
268
|
baseURL: config.baseURL,
|
|
252
269
|
apiKey: config.apiKey,
|
|
253
270
|
compatible: config.compatible ?? "openai-compatible",
|
|
271
|
+
nativeRouting: config.nativeRouting,
|
|
254
272
|
...config.headers && { headers: config.headers }
|
|
255
273
|
};
|
|
256
274
|
}
|
|
@@ -267,6 +285,14 @@ function createEnvProvider(factories, options = {}) {
|
|
|
267
285
|
} catch {
|
|
268
286
|
throw new Error(`[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`);
|
|
269
287
|
}
|
|
288
|
+
const nativeRoutingRaw = env("NATIVE_ROUTING");
|
|
289
|
+
let nativeRoutingFromEnv;
|
|
290
|
+
if (nativeRoutingRaw !== void 0) {
|
|
291
|
+
const lower = nativeRoutingRaw.toLowerCase();
|
|
292
|
+
if (lower === "true") nativeRoutingFromEnv = true;
|
|
293
|
+
else if (lower === "false") nativeRoutingFromEnv = false;
|
|
294
|
+
else throw new Error(`[ai-sdk-provider-env] Invalid value for ${prefix}${separator}NATIVE_ROUTING: "${nativeRoutingRaw}". Expected "true" or "false".`);
|
|
295
|
+
}
|
|
270
296
|
const presetName = env("PRESET");
|
|
271
297
|
if (presetName) {
|
|
272
298
|
const preset = resolvePreset(presetName);
|
|
@@ -274,6 +300,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
274
300
|
baseURL: env("BASE_URL") ?? preset.baseURL,
|
|
275
301
|
apiKey,
|
|
276
302
|
compatible: env("COMPATIBLE") ?? preset.compatible,
|
|
303
|
+
nativeRouting: nativeRoutingFromEnv ?? preset.nativeRouting,
|
|
277
304
|
...headers && { headers }
|
|
278
305
|
};
|
|
279
306
|
}
|
|
@@ -282,6 +309,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
282
309
|
baseURL,
|
|
283
310
|
apiKey,
|
|
284
311
|
compatible: env("COMPATIBLE") ?? "openai-compatible",
|
|
312
|
+
nativeRouting: nativeRoutingFromEnv,
|
|
285
313
|
...headers && { headers }
|
|
286
314
|
};
|
|
287
315
|
if (options.presetAutoDetect !== false) {
|
|
@@ -290,6 +318,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
290
318
|
baseURL: autoPreset.baseURL,
|
|
291
319
|
apiKey,
|
|
292
320
|
compatible: env("COMPATIBLE") ?? autoPreset.compatible ?? "openai-compatible",
|
|
321
|
+
nativeRouting: nativeRoutingFromEnv ?? autoPreset.nativeRouting,
|
|
293
322
|
...headers && { headers }
|
|
294
323
|
};
|
|
295
324
|
}
|
|
@@ -327,8 +356,18 @@ function createEnvProvider(factories, options = {}) {
|
|
|
327
356
|
}
|
|
328
357
|
throw error;
|
|
329
358
|
}
|
|
330
|
-
case "anthropic":
|
|
331
|
-
|
|
359
|
+
case "anthropic": try {
|
|
360
|
+
return factories.createAnthropic(baseOpts);
|
|
361
|
+
} catch (error) {
|
|
362
|
+
if (isModuleNotFoundError(error, "@ai-sdk/anthropic")) throw new Error("[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. Run: npm install @ai-sdk/anthropic");
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
case "gemini": try {
|
|
366
|
+
return factories.createGemini(baseOpts);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (isModuleNotFoundError(error, "@ai-sdk/google")) throw new Error("[ai-sdk-provider-env] Google provider requires @ai-sdk/google. Run: npm install @ai-sdk/google");
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
332
371
|
case "openai-compatible": try {
|
|
333
372
|
return factories.createOpenAICompatible({
|
|
334
373
|
name: configSet,
|
|
@@ -349,8 +388,12 @@ function createEnvProvider(factories, options = {}) {
|
|
|
349
388
|
* Env-var-backed config sets normalize hyphens to underscores, so aliases
|
|
350
389
|
* like `my-api` and `my_api` share one cached provider.
|
|
351
390
|
*/
|
|
352
|
-
function getCacheKey(configSet) {
|
|
353
|
-
if (options.configs?.[configSet])
|
|
391
|
+
function getCacheKey(configSet, config, effectiveCompatible) {
|
|
392
|
+
if (options.configs?.[configSet]) {
|
|
393
|
+
if (config.nativeRouting) return `config:${configSet.toUpperCase()}:${effectiveCompatible}`;
|
|
394
|
+
return `config:${configSet.toUpperCase()}`;
|
|
395
|
+
}
|
|
396
|
+
if (config.nativeRouting) return `env:${configSet.replace(/-/g, "_").toUpperCase()}:${effectiveCompatible}`;
|
|
354
397
|
return `env:${configSet.replace(/-/g, "_").toUpperCase()}`;
|
|
355
398
|
}
|
|
356
399
|
/**
|
|
@@ -360,13 +403,29 @@ function createEnvProvider(factories, options = {}) {
|
|
|
360
403
|
* This is safe because `ProviderV3` and `ProviderV4` have identical
|
|
361
404
|
* method signatures — only `specificationVersion` and model type brands differ.
|
|
362
405
|
*/
|
|
363
|
-
function getProvider(configSet) {
|
|
364
|
-
const
|
|
406
|
+
function getProvider(configSet, model) {
|
|
407
|
+
const config = resolveConfig(configSet);
|
|
408
|
+
const detectedCompatible = config.nativeRouting && model ? detectNativeCompatible(model) : void 0;
|
|
409
|
+
const effectiveCompatible = detectedCompatible ?? config.compatible;
|
|
410
|
+
const wasAutoRouted = detectedCompatible != null && detectedCompatible !== config.compatible;
|
|
411
|
+
const key = getCacheKey(configSet, config, effectiveCompatible);
|
|
365
412
|
const cached = cache.get(key);
|
|
366
413
|
if (cached) return cached;
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
414
|
+
const configForProvider = wasAutoRouted ? {
|
|
415
|
+
...config,
|
|
416
|
+
compatible: effectiveCompatible
|
|
417
|
+
} : config;
|
|
418
|
+
try {
|
|
419
|
+
const provider = createUnderlying(configSet, configForProvider);
|
|
420
|
+
cache.set(key, provider);
|
|
421
|
+
return provider;
|
|
422
|
+
} catch (error) {
|
|
423
|
+
if (wasAutoRouted && error instanceof Error && error.message.includes("[ai-sdk-provider-env]")) {
|
|
424
|
+
const prefix = configSet.replace(/-/g, "_").toUpperCase();
|
|
425
|
+
throw new Error(`${error.message} (nativeRouting auto-detected this model as ${effectiveCompatible}. Disable with ${prefix}${separator}NATIVE_ROUTING=false to use ${config.compatible} instead.)`, { cause: error });
|
|
426
|
+
}
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
370
429
|
}
|
|
371
430
|
/**
|
|
372
431
|
* Parse a model ID. The first `/` separates the config set name from the actual model ID.
|
|
@@ -383,7 +442,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
383
442
|
specificationVersion: "v3",
|
|
384
443
|
languageModel(modelId) {
|
|
385
444
|
const { configSet, model } = parseModelId(modelId);
|
|
386
|
-
return getProvider(configSet).languageModel(model);
|
|
445
|
+
return getProvider(configSet, model).languageModel(model);
|
|
387
446
|
},
|
|
388
447
|
embeddingModel(modelId) {
|
|
389
448
|
const { configSet, model } = parseModelId(modelId);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["process","NoSuchModelError"],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, ProviderV3Compatible } from './types'\n\n/**\n * Check if an error is a \"module not found\" error for a specific package.\n *\n * Handles Node.js (error.code), Bun, and Bun compiled binaries\n * which may throw non-standard error objects.\n */\nexport function isModuleNotFoundError(error: unknown, packageName: string): boolean {\n if (typeof error !== 'object' || error === null)\n return false\n\n const message = 'message' in error && typeof error.message === 'string' ? error.message : ''\n const code = 'code' in error ? (error as Record<string, unknown>).code : undefined\n\n // Only match the module name in the \"Cannot find module/package 'X'\" part,\n // not in the require stack that follows. This prevents false positives when\n // a sub-dependency fails but the parent package appears in the stack trace.\n const isResolutionError = code === 'MODULE_NOT_FOUND'\n || code === 'ERR_MODULE_NOT_FOUND'\n || message.startsWith('Cannot find module')\n || message.startsWith('Cannot find package')\n\n if (!isResolutionError)\n return false\n\n // Extract the quoted module name from \"Cannot find module 'X'\" or \"Cannot find package 'X'\"\n // Handles both single and double quote styles across runtimes.\n const quoted = message.match(/Cannot find (?:module|package) ['\"]([^'\"]+)['\"]/)\n if (quoted)\n return quoted[1] === packageName || quoted[1].startsWith(`${packageName}/`)\n\n // Fallback for non-standard message formats (e.g. some Bun versions):\n // check if the package name appears before any \"Require stack:\" section,\n // with boundary checking to avoid false positives (e.g. @ai-sdk/openai matching @ai-sdk/openai-compatible).\n const requireStackIndex = message.indexOf('\\nRequire stack:')\n const relevantPart = requireStackIndex !== -1 ? message.slice(0, requireStackIndex) : message\n const boundaryPattern = new RegExp(`(?:^|[\\\\s'\"/@])${packageName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?:[\\\\s'\"/@]|$)`)\n return boundaryPattern.test(relevantPart)\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n * When not installed, errors propagate to allow fallback to OpenAI-compatible provider.\n */\nexport function createOpenAIProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n * No fallback is available — Anthropic does not support the OpenAI-compatible protocol.\n */\nexport function createAnthropicProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/anthropic')) {\n throw new Error(\n '[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n throw error\n }\n}\n\n/**\n * Create a Google Generative AI (Gemini) provider.\n *\n * Dynamically requires `@ai-sdk/google`, so it only needs to be installed when actually used.\n * No fallback is available — Google does not support the OpenAI-compatible protocol.\n */\nexport function createGeminiProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createGoogleGenerativeAI } = require('@ai-sdk/google')\n return createGoogleGenerativeAI(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/google')) {\n throw new Error(\n '[ai-sdk-provider-env] Google provider requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n throw error\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * `@ai-sdk/openai-compatible` is a regular dependency and always available.\n */\nexport function createOpenAICompatibleProvider(opts: EnvProviderNamedFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n}\n","import type { PresetConfig } from './types'\n\n/**\n * Built-in preset configurations for common providers.\n *\n * When using a preset, only `{PREFIX}__PRESET` and `{PREFIX}__API_KEY`\n * are required; `BASE_URL` and `COMPATIBLE` are provided by the preset.\n */\nexport const builtinPresets: Record<string, PresetConfig> = {\n // OpenAI\n 'openai': {\n baseURL: 'https://api.openai.com/v1',\n compatible: 'openai',\n },\n\n // Anthropic\n 'anthropic': {\n baseURL: 'https://api.anthropic.com',\n compatible: 'anthropic',\n },\n\n // Google AI Studio (Gemini)\n 'google': {\n baseURL: 'https://generativelanguage.googleapis.com/v1beta',\n compatible: 'gemini',\n },\n\n // DeepSeek\n 'deepseek': {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI (GLM series)\n 'zhipu': {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // Groq\n 'groq': {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai-compatible',\n },\n\n // Together AI\n 'together': {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai-compatible',\n },\n\n // Fireworks AI\n 'fireworks': {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai-compatible',\n },\n\n // Mistral AI\n 'mistral': {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (international)\n 'moonshot': {\n baseURL: 'https://api.moonshot.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (China mainland)\n 'moonshot-china': {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // Perplexity\n 'perplexity': {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai-compatible',\n },\n\n // OpenRouter\n 'openrouter': {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (international)\n 'siliconflow': {\n baseURL: 'https://api.siliconflow.com/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (China mainland)\n 'siliconflow-china': {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // xAI (Grok series)\n 'xai': {\n baseURL: 'https://api.x.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI International (Z.AI — global endpoint for GLM series)\n 'zai': {\n baseURL: 'https://api.z.ai/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // OpenCode Zen (curated multi-model AI gateway)\n 'opencode-zen': {\n baseURL: 'https://opencode.ai/zen/v1',\n compatible: 'openai-compatible',\n },\n\n // OpenCode Go (low-cost subscription for open coding models)\n 'opencode-go': {\n baseURL: 'https://opencode.ai/zen/go/v1',\n compatible: 'openai-compatible',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { EnvProviderFactories, EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, EnvProviderOptions, ProviderV3Compatible } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createGeminiProvider, createOpenAICompatibleProvider, createOpenAIProvider, isModuleNotFoundError } from './factories'\nimport { builtinPresets } from './presets'\n\n/**\n * Thrown when a provider is not available (factory not provided by user).\n * Used internally to trigger fallback to OpenAI-compatible provider.\n */\nclass ProviderNotAvailableError extends Error {\n constructor(provider: string) {\n super(`Provider \"${provider}\" is not available`)\n this.name = 'ProviderNotAvailableError'\n }\n}\n\n/**\n * Interface for provider factory functions, used for dependency injection.\n *\n * In production, `defaultFactories` delegates to the real SDK implementations.\n * In tests, fake factories can be injected to avoid module mocking.\n *\n * Return types use `ProviderV3Compatible` so that both `ProviderV3`\n * (from `@ai-sdk/provider@3.x`) and `ProviderV4` (`@ai-sdk/provider@4.x`)\n * are accepted.\n */\nexport interface ProviderFactories {\n createOpenAI: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createAnthropic: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createGemini: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createOpenAICompatible: (opts: EnvProviderNamedFactoryOptions) => ProviderV3Compatible\n}\n\n/**\n * Default factories that delegate to the real implementations in `factories.ts`.\n */\nconst defaultFactories: ProviderFactories = {\n createOpenAI: createOpenAIProvider,\n createAnthropic: createAnthropicProvider,\n createGemini: createGeminiProvider,\n createOpenAICompatible: createOpenAICompatibleProvider,\n}\n\n/**\n * Internally resolved configuration with all required fields determined.\n */\ninterface ResolvedConfig {\n baseURL: string\n apiKey: string\n compatible: string\n headers?: Record<string, string>\n}\n\n/**\n * Testable core implementation that accepts injected provider factories.\n *\n * In tests, call this function directly with fake factories\n * to avoid module mocking entirely.\n */\nexport function createEnvProvider(\n factories: ProviderFactories,\n options: Omit<EnvProviderOptions, 'factories'> = {},\n): ProviderV3 {\n const separator = options.separator ?? '_'\n const defaultFetch = options.defaults?.fetch\n const defaultHeaders = options.defaults?.headers\n\n // Cache created providers to avoid redundant initialization\n // Stored as ProviderV3 via safe cast from ProviderV3Compatible,\n // since V3 and V4 provider interfaces are structurally identical.\n const cache = new Map<string, ProviderV3>()\n\n /**\n * Resolve baseURL and compatible from a preset name.\n */\n function resolvePreset(presetName: string): { baseURL: string, compatible: string } {\n const preset = builtinPresets[presetName]\n if (!preset) {\n const available = Object.keys(builtinPresets).join(', ')\n throw new Error(\n `[ai-sdk-provider-env] Unknown preset \"${presetName}\". Available presets: ${available}`,\n )\n }\n return {\n baseURL: preset.baseURL,\n compatible: preset.compatible ?? 'openai-compatible',\n }\n }\n\n /**\n * Resolve config set configuration from explicit configs, presets, or environment variables.\n */\n function resolveConfig(configSet: string): ResolvedConfig {\n // Explicit configs take precedence over env vars\n if (options.configs?.[configSet]) {\n const config = options.configs[configSet]\n\n // Code-based configs also support presets\n if (config.preset) {\n const preset = resolvePreset(config.preset)\n return {\n baseURL: config.baseURL ?? preset.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? preset.compatible,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n if (!config.baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing baseURL in config for \"${configSet}\"`\n + ` (or set preset to use a built-in preset)`,\n )\n }\n\n return {\n baseURL: config.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? 'openai-compatible',\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n // --- All checks below apply only to the env-var resolution path ---\n // The configs option (above) bypasses these and accepts arbitrary names/separators.\n\n // Validate separator produces shell-safe env var names (deferred to first env-var use)\n if (!/^\\w+$/.test(separator)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid separator \"${separator}\". `\n + `Separator must only contain ASCII letters, digits, or underscores to produce shell-safe env var names.`,\n )\n }\n\n // Validate config set name (ASCII shell-safe names only)\n if (!/^[A-Z_][\\w-]*$/i.test(configSet)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid config set name \"${configSet}\". `\n + `Names must start with a letter or underscore, followed by letters, digits, underscores, or hyphens. `\n + `For arbitrary names, use the configs option instead.`,\n )\n }\n\n // Normalize hyphens to underscores for POSIX shell-safe env var names.\n // e.g. \"my-api\" → \"MY_API\", so env vars are MY_API_API_KEY, MY_API_BASE_URL, etc.\n const prefix = configSet.replace(/-/g, '_').toUpperCase()\n const env = (key: string): string | undefined => process.env[`${prefix}${separator}${key}`]\n\n const apiKey = env('API_KEY')\n if (!apiKey) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}API_KEY`,\n )\n }\n\n // Parse headers from env var (JSON format)\n const headersRaw = env('HEADERS')\n let headers: Record<string, string> | undefined\n if (headersRaw) {\n try {\n headers = JSON.parse(headersRaw)\n }\n catch {\n throw new Error(\n `[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`,\n )\n }\n }\n\n // Check for preset\n const presetName = env('PRESET')\n if (presetName) {\n const preset = resolvePreset(presetName)\n return {\n baseURL: env('BASE_URL') ?? preset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? preset.compatible,\n ...(headers && { headers }),\n }\n }\n\n // Without a preset, try BASE_URL first\n const baseURL = env('BASE_URL')\n if (baseURL) {\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai-compatible',\n ...(headers && { headers }),\n }\n }\n\n // Auto-detect: if configSet name matches a built-in preset, use it automatically\n if (options.presetAutoDetect !== false) {\n const autoPreset = builtinPresets[configSet.toLowerCase()]\n if (autoPreset) {\n return {\n baseURL: autoPreset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? autoPreset.compatible ?? 'openai-compatible',\n ...(headers && { headers }),\n }\n }\n }\n\n // Error: neither BASE_URL nor a matching preset found\n const available = Object.keys(builtinPresets).join(', ')\n const presetHint = builtinPresets[configSet.toLowerCase()]\n ? ` (Note: \"${configSet}\" matches a built-in preset, but presetAutoDetect is disabled.)`\n : ''\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL`\n + ` (or set ${prefix}${separator}PRESET to use a preset.`\n + ` Available presets: ${available})${presetHint}`,\n )\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(configSet: string, config: ResolvedConfig): ProviderV3Compatible {\n const { baseURL, apiKey, compatible, headers } = config\n\n // Merge headers: defaults.headers as base, config-set headers override matching keys\n const mergedHeaders = (defaultHeaders || headers)\n ? { ...defaultHeaders, ...headers }\n : undefined\n\n const baseOpts = {\n baseURL,\n apiKey,\n ...(mergedHeaders && { headers: mergedHeaders }),\n ...(defaultFetch && { fetch: defaultFetch }),\n }\n\n switch (compatible) {\n case 'openai':\n try {\n return factories.createOpenAI(baseOpts)\n }\n catch (error) {\n // Fallback to OpenAI-compatible when:\n // - @ai-sdk/openai is not installed (module not found)\n // - User-provided factories don't include openai (ProviderNotAvailableError)\n if (isModuleNotFoundError(error, '@ai-sdk/openai') || error instanceof ProviderNotAvailableError) {\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (fallbackError) {\n // Only swallow module-not-found errors from the fallback itself\n if (isModuleNotFoundError(fallbackError, '@ai-sdk/openai-compatible') || fallbackError instanceof ProviderNotAvailableError) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai or its openai-compatible fallback. '\n + 'Install @ai-sdk/openai for full OpenAI features, or if using a bundler, '\n + 'provide factories: { openai: createOpenAI }',\n )\n }\n // Non-module-not-found errors from openai-compatible (e.g. config issues) should propagate\n throw fallbackError\n }\n }\n throw error\n }\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n case 'gemini':\n return factories.createGemini(baseOpts)\n case 'openai-compatible':\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/openai-compatible')) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai-compatible. '\n + 'If using a bundler, provide factories: { openaiCompatible: createOpenAICompatible }',\n )\n }\n throw error\n }\n default:\n throw new Error(\n `[ai-sdk-provider-env] Unknown compatible mode \"${compatible}\".`\n + ` Supported values: \"openai\", \"anthropic\", \"gemini\", \"openai-compatible\".`\n + ` Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`,\n )\n }\n }\n\n /**\n * Compute a cache key for the given config set.\n *\n * Explicit configs use the raw (uppercased) name, so `foo-bar` and `foo_bar`\n * remain distinct when both are defined in `configs`.\n * Env-var-backed config sets normalize hyphens to underscores, so aliases\n * like `my-api` and `my_api` share one cached provider.\n */\n function getCacheKey(configSet: string): string {\n if (options.configs?.[configSet]) {\n return `config:${configSet.toUpperCase()}`\n }\n return `env:${configSet.replace(/-/g, '_').toUpperCase()}`\n }\n\n /**\n * Get or create a cached provider for the given config set.\n *\n * Returns `ProviderV3` via a safe cast from `ProviderV3Compatible`.\n * This is safe because `ProviderV3` and `ProviderV4` have identical\n * method signatures — only `specificationVersion` and model type brands differ.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = getCacheKey(configSet)\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n // Safe cast: V3 and V4 providers are structurally identical.\n // The underlying provider may be ProviderV3 or ProviderV4 depending\n // on which SDK version the user has installed.\n const provider = createUnderlying(configSet, config) as unknown as ProviderV3\n cache.set(key, provider)\n return provider\n }\n\n /**\n * Parse a model ID. The first `/` separates the config set name from the actual model ID.\n */\n function parseModelId(modelId: string): { configSet: string, model: string } {\n const slashIndex = modelId.indexOf('/')\n if (slashIndex === -1) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid model ID \"${modelId}\". `\n + `Expected format: \"{configSet}/{modelId}\", e.g. \"zhipu/glm-4\"`,\n )\n }\n return {\n configSet: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n }\n\n return {\n specificationVersion: 'v3' as const,\n\n languageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).languageModel(model)\n },\n\n embeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).embeddingModel(model)\n },\n\n imageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).imageModel(model)\n },\n\n textEmbeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n // Prefer textEmbeddingModel if the underlying provider has it (V3).\n if (provider.textEmbeddingModel) {\n return provider.textEmbeddingModel(model)\n }\n // V4 providers removed textEmbeddingModel — fall back to embeddingModel\n // which exists in both V3 and V4 interfaces.\n if (provider.embeddingModel) {\n return provider.embeddingModel(model)\n }\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n },\n\n transcriptionModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.transcriptionModel) {\n throw new NoSuchModelError({ modelId, modelType: 'transcriptionModel' })\n }\n return provider.transcriptionModel(model)\n },\n\n speechModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.speechModel) {\n throw new NoSuchModelError({ modelId, modelType: 'speechModel' })\n }\n return provider.speechModel(model)\n },\n\n rerankingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.rerankingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'rerankingModel' })\n }\n return provider.rerankingModel(model)\n },\n }\n}\n\n/**\n * Build internal `ProviderFactories` from user-provided `EnvProviderFactories`.\n *\n * Uses lazy-strict semantics: each factory slot is only evaluated when actually called.\n * If the user provided a factory for a given compatible mode, it is used;\n * otherwise, a clear error is thrown (no silent fallback to dynamic `require()`).\n */\nfunction buildUserFactories(userFactories: EnvProviderFactories): ProviderFactories {\n function missingFactory(key: string, fnName: string, pkg: string): never {\n throw new Error(\n `[ai-sdk-provider-env] No factory provided for \"${key}\". `\n + `When using the factories option, provide a factory for each compatibility mode you use. `\n + `Add: import { ${fnName} } from '${pkg}' and set factories: { ${key}: ${fnName} }`,\n )\n }\n\n return {\n createOpenAI: (opts) => {\n if (userFactories.openai)\n return userFactories.openai(opts)\n // Signal fallback — createUnderlying will catch this and use openai-compatible\n throw new ProviderNotAvailableError('openai')\n },\n createAnthropic: opts =>\n userFactories.anthropic\n ? userFactories.anthropic(opts)\n : missingFactory('anthropic', 'createAnthropic', '@ai-sdk/anthropic'),\n createGemini: opts =>\n userFactories.gemini\n ? userFactories.gemini(opts)\n : missingFactory('gemini', 'createGoogleGenerativeAI', '@ai-sdk/google'),\n createOpenAICompatible: opts =>\n userFactories.openaiCompatible\n ? userFactories.openaiCompatible(opts)\n : createOpenAICompatibleProvider(opts),\n }\n}\n\n/**\n * Create a dynamic, environment-variable-driven AI SDK provider.\n *\n * Automatically resolves provider configurations from env var naming conventions,\n * with built-in preset support for quick setup.\n *\n * Config set naming rules:\n * - Names must match `[A-Za-z_][A-Za-z0-9_-]*` (ASCII letters, digits, underscores, hyphens)\n * - Hyphens are normalized to underscores for env var lookup:\n * `my-api/model` → reads `MY_API_API_KEY`, `MY_API_BASE_URL`, etc.\n * - For arbitrary names, use the `configs` option instead\n *\n * Env var convention (using config set `ZHIPU` with default separator `_` as example):\n * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)\n * - `ZHIPU_BASE_URL` — API base URL\n * - `ZHIPU_API_KEY` — API key (required)\n * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)\n * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)\n *\n * @example\n * ```ts\n * import { createProviderRegistry } from 'ai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const registry = createProviderRegistry({\n * env: envProvider(),\n * })\n *\n * // Use a preset (only API_KEY is required)\n * // DEEPSEEK_PRESET=deepseek\n * // DEEPSEEK_API_KEY=sk-xxx\n * const model = registry.languageModel('env:deepseek/deepseek-chat')\n *\n * // Specify all parameters manually\n * // MYAPI_BASE_URL=https://api.example.com/v1\n * // MYAPI_API_KEY=xxx\n * const model2 = registry.languageModel('env:myapi/some-model')\n * ```\n *\n * @example Bundler-safe usage with explicit factories\n * ```ts\n * import { createOpenAI } from '@ai-sdk/openai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const provider = envProvider({\n * factories: { openai: createOpenAI },\n * })\n * ```\n */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n const factories = options.factories\n ? buildUserFactories(options.factories)\n : defaultFactories\n return createEnvProvider(factories, options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["process","NoSuchModelError"],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, ProviderV3Compatible } from './types'\n\n/**\n * Check if an error is a \"module not found\" error for a specific package.\n *\n * Handles Node.js (error.code), Bun, and Bun compiled binaries\n * which may throw non-standard error objects.\n */\nexport function isModuleNotFoundError(error: unknown, packageName: string): boolean {\n if (typeof error !== 'object' || error === null)\n return false\n\n const message = 'message' in error && typeof error.message === 'string' ? error.message : ''\n const code = 'code' in error ? (error as Record<string, unknown>).code : undefined\n\n // Only match the module name in the \"Cannot find module/package 'X'\" part,\n // not in the require stack that follows. This prevents false positives when\n // a sub-dependency fails but the parent package appears in the stack trace.\n const isResolutionError = code === 'MODULE_NOT_FOUND'\n || code === 'ERR_MODULE_NOT_FOUND'\n || message.startsWith('Cannot find module')\n || message.startsWith('Cannot find package')\n\n if (!isResolutionError)\n return false\n\n // Extract the quoted module name from \"Cannot find module 'X'\" or \"Cannot find package 'X'\"\n // Handles both single and double quote styles across runtimes.\n const quoted = message.match(/Cannot find (?:module|package) ['\"]([^'\"]+)['\"]/)\n if (quoted)\n return quoted[1] === packageName || quoted[1].startsWith(`${packageName}/`)\n\n // Fallback for non-standard message formats (e.g. some Bun versions):\n // check if the package name appears before any \"Require stack:\" section,\n // with boundary checking to avoid false positives (e.g. @ai-sdk/openai matching @ai-sdk/openai-compatible).\n const requireStackIndex = message.indexOf('\\nRequire stack:')\n const relevantPart = requireStackIndex !== -1 ? message.slice(0, requireStackIndex) : message\n const boundaryPattern = new RegExp(`(?:^|[\\\\s'\"/@])${packageName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?:[\\\\s'\"/@]|$)`)\n return boundaryPattern.test(relevantPart)\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n * When not installed, errors propagate to allow fallback to OpenAI-compatible provider.\n */\nexport function createOpenAIProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n * No fallback is available — Anthropic does not support the OpenAI-compatible protocol.\n */\nexport function createAnthropicProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/anthropic')) {\n throw new Error(\n '[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n throw error\n }\n}\n\n/**\n * Create a Google Generative AI (Gemini) provider.\n *\n * Dynamically requires `@ai-sdk/google`, so it only needs to be installed when actually used.\n * No fallback is available — Google does not support the OpenAI-compatible protocol.\n */\nexport function createGeminiProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createGoogleGenerativeAI } = require('@ai-sdk/google')\n return createGoogleGenerativeAI(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/google')) {\n throw new Error(\n '[ai-sdk-provider-env] Google provider requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n throw error\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * `@ai-sdk/openai-compatible` is a regular dependency and always available.\n */\nexport function createOpenAICompatibleProvider(opts: EnvProviderNamedFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n}\n","import type { PresetConfig } from './types'\n\n/**\n * Built-in preset configurations for common providers.\n *\n * When using a preset, only `{PREFIX}__PRESET` and `{PREFIX}__API_KEY`\n * are required; `BASE_URL` and `COMPATIBLE` are provided by the preset.\n */\nexport const builtinPresets: Record<string, PresetConfig> = {\n // OpenAI\n 'openai': {\n baseURL: 'https://api.openai.com/v1',\n compatible: 'openai',\n },\n\n // Anthropic\n 'anthropic': {\n baseURL: 'https://api.anthropic.com',\n compatible: 'anthropic',\n },\n\n // Google AI Studio (Gemini)\n 'google': {\n baseURL: 'https://generativelanguage.googleapis.com/v1beta',\n compatible: 'gemini',\n },\n\n // DeepSeek\n 'deepseek': {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI (GLM series)\n 'zhipu': {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // Groq\n 'groq': {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai-compatible',\n },\n\n // Together AI\n 'together': {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai-compatible',\n },\n\n // Fireworks AI\n 'fireworks': {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai-compatible',\n },\n\n // Mistral AI\n 'mistral': {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (international)\n 'moonshot': {\n baseURL: 'https://api.moonshot.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (China mainland)\n 'moonshot-china': {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // Perplexity\n 'perplexity': {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai-compatible',\n },\n\n // OpenRouter\n 'openrouter': {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (international)\n 'siliconflow': {\n baseURL: 'https://api.siliconflow.com/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (China mainland)\n 'siliconflow-china': {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // xAI (Grok series)\n 'xai': {\n baseURL: 'https://api.x.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI International (Z.AI — global endpoint for GLM series)\n 'zai': {\n baseURL: 'https://api.z.ai/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // OpenCode Zen (curated multi-model AI gateway)\n 'opencode-zen': {\n baseURL: 'https://opencode.ai/zen/v1',\n compatible: 'openai-compatible',\n nativeRouting: true,\n },\n\n // OpenCode Go (low-cost subscription for open coding models)\n 'opencode-go': {\n baseURL: 'https://opencode.ai/zen/go/v1',\n compatible: 'openai-compatible',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { EnvProviderFactories, EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, EnvProviderOptions, ProviderV3Compatible } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createGeminiProvider, createOpenAICompatibleProvider, createOpenAIProvider, isModuleNotFoundError } from './factories'\nimport { builtinPresets } from './presets'\n\n/**\n * Thrown when a provider is not available (factory not provided by user).\n * Used internally to trigger fallback to OpenAI-compatible provider.\n */\nclass ProviderNotAvailableError extends Error {\n constructor(provider: string) {\n super(`Provider \"${provider}\" is not available`)\n this.name = 'ProviderNotAvailableError'\n }\n}\n\n/**\n * Interface for provider factory functions, used for dependency injection.\n *\n * In production, `defaultFactories` delegates to the real SDK implementations.\n * In tests, fake factories can be injected to avoid module mocking.\n *\n * Return types use `ProviderV3Compatible` so that both `ProviderV3`\n * (from `@ai-sdk/provider@3.x`) and `ProviderV4` (`@ai-sdk/provider@4.x`)\n * are accepted.\n */\nexport interface ProviderFactories {\n createOpenAI: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createAnthropic: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createGemini: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createOpenAICompatible: (opts: EnvProviderNamedFactoryOptions) => ProviderV3Compatible\n}\n\n/**\n * Default factories that delegate to the real implementations in `factories.ts`.\n */\nconst defaultFactories: ProviderFactories = {\n createOpenAI: createOpenAIProvider,\n createAnthropic: createAnthropicProvider,\n createGemini: createGeminiProvider,\n createOpenAICompatible: createOpenAICompatibleProvider,\n}\n\n/**\n * Detect the native compatible mode for a model based on its ID prefix.\n *\n * Used by nativeRouting to auto-route model families to their native provider SDKs.\n * Only `claude-*`, `gemini-*`, and `gpt-*` prefixes are matched.\n * Known limitation: `o1-*`, `o3-*`, `chatgpt-*` are NOT matched (use explicit compatible mode).\n *\n * @returns The detected compatible mode, or `undefined` if no match.\n */\nexport function detectNativeCompatible(model: string): 'openai' | 'anthropic' | 'gemini' | undefined {\n if (model.startsWith('claude-'))\n return 'anthropic'\n if (model.startsWith('gemini-'))\n return 'gemini'\n if (model.startsWith('gpt-'))\n return 'openai'\n return undefined\n}\n\n/**\n * Internally resolved configuration with all required fields determined.\n */\ninterface ResolvedConfig {\n baseURL: string\n apiKey: string\n compatible: string\n headers?: Record<string, string>\n nativeRouting?: boolean\n}\n\n/**\n * Testable core implementation that accepts injected provider factories.\n *\n * In tests, call this function directly with fake factories\n * to avoid module mocking entirely.\n */\nexport function createEnvProvider(\n factories: ProviderFactories,\n options: Omit<EnvProviderOptions, 'factories'> = {},\n): ProviderV3 {\n const separator = options.separator ?? '_'\n const defaultFetch = options.defaults?.fetch\n const defaultHeaders = options.defaults?.headers\n\n // Cache created providers to avoid redundant initialization\n // Stored as ProviderV3 via safe cast from ProviderV3Compatible,\n // since V3 and V4 provider interfaces are structurally identical.\n const cache = new Map<string, ProviderV3>()\n\n /**\n * Resolve baseURL and compatible from a preset name.\n */\n function resolvePreset(presetName: string): { baseURL: string, compatible: string, nativeRouting?: boolean } {\n const preset = builtinPresets[presetName]\n if (!preset) {\n const available = Object.keys(builtinPresets).join(', ')\n throw new Error(\n `[ai-sdk-provider-env] Unknown preset \"${presetName}\". Available presets: ${available}`,\n )\n }\n return {\n baseURL: preset.baseURL,\n compatible: preset.compatible ?? 'openai-compatible',\n nativeRouting: preset.nativeRouting,\n }\n }\n\n /**\n * Resolve config set configuration from explicit configs, presets, or environment variables.\n */\n function resolveConfig(configSet: string): ResolvedConfig {\n // Explicit configs take precedence over env vars\n if (options.configs?.[configSet]) {\n const config = options.configs[configSet]\n\n // Code-based configs also support presets\n if (config.preset) {\n const preset = resolvePreset(config.preset)\n return {\n baseURL: config.baseURL ?? preset.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? preset.compatible,\n nativeRouting: config.nativeRouting ?? preset.nativeRouting,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n if (!config.baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing baseURL in config for \"${configSet}\"`\n + ` (or set preset to use a built-in preset)`,\n )\n }\n\n return {\n baseURL: config.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? 'openai-compatible',\n nativeRouting: config.nativeRouting,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n // --- All checks below apply only to the env-var resolution path ---\n // The configs option (above) bypasses these and accepts arbitrary names/separators.\n\n // Validate separator produces shell-safe env var names (deferred to first env-var use)\n if (!/^\\w+$/.test(separator)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid separator \"${separator}\". `\n + `Separator must only contain ASCII letters, digits, or underscores to produce shell-safe env var names.`,\n )\n }\n\n // Validate config set name (ASCII shell-safe names only)\n if (!/^[A-Z_][\\w-]*$/i.test(configSet)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid config set name \"${configSet}\". `\n + `Names must start with a letter or underscore, followed by letters, digits, underscores, or hyphens. `\n + `For arbitrary names, use the configs option instead.`,\n )\n }\n\n // Normalize hyphens to underscores for POSIX shell-safe env var names.\n // e.g. \"my-api\" → \"MY_API\", so env vars are MY_API_API_KEY, MY_API_BASE_URL, etc.\n const prefix = configSet.replace(/-/g, '_').toUpperCase()\n const env = (key: string): string | undefined => process.env[`${prefix}${separator}${key}`]\n\n const apiKey = env('API_KEY')\n if (!apiKey) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}API_KEY`,\n )\n }\n\n // Parse headers from env var (JSON format)\n const headersRaw = env('HEADERS')\n let headers: Record<string, string> | undefined\n if (headersRaw) {\n try {\n headers = JSON.parse(headersRaw)\n }\n catch {\n throw new Error(\n `[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`,\n )\n }\n }\n\n // Parse NATIVE_ROUTING env var (boolean: 'true'/'false', case-insensitive)\n const nativeRoutingRaw = env('NATIVE_ROUTING')\n let nativeRoutingFromEnv: boolean | undefined\n if (nativeRoutingRaw !== undefined) {\n const lower = nativeRoutingRaw.toLowerCase()\n if (lower === 'true') {\n nativeRoutingFromEnv = true\n }\n else if (lower === 'false') {\n nativeRoutingFromEnv = false\n }\n else {\n throw new Error(\n `[ai-sdk-provider-env] Invalid value for ${prefix}${separator}NATIVE_ROUTING: \"${nativeRoutingRaw}\". `\n + `Expected \"true\" or \"false\".`,\n )\n }\n }\n\n // Check for preset\n const presetName = env('PRESET')\n if (presetName) {\n const preset = resolvePreset(presetName)\n return {\n baseURL: env('BASE_URL') ?? preset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? preset.compatible,\n nativeRouting: nativeRoutingFromEnv ?? preset.nativeRouting,\n ...(headers && { headers }),\n }\n }\n\n // Without a preset, try BASE_URL first\n const baseURL = env('BASE_URL')\n if (baseURL) {\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai-compatible',\n nativeRouting: nativeRoutingFromEnv,\n ...(headers && { headers }),\n }\n }\n\n // Auto-detect: if configSet name matches a built-in preset, use it automatically\n if (options.presetAutoDetect !== false) {\n const autoPreset = builtinPresets[configSet.toLowerCase()]\n if (autoPreset) {\n return {\n baseURL: autoPreset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? autoPreset.compatible ?? 'openai-compatible',\n nativeRouting: nativeRoutingFromEnv ?? autoPreset.nativeRouting,\n ...(headers && { headers }),\n }\n }\n }\n\n // Error: neither BASE_URL nor a matching preset found\n const available = Object.keys(builtinPresets).join(', ')\n const presetHint = builtinPresets[configSet.toLowerCase()]\n ? ` (Note: \"${configSet}\" matches a built-in preset, but presetAutoDetect is disabled.)`\n : ''\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL`\n + ` (or set ${prefix}${separator}PRESET to use a preset.`\n + ` Available presets: ${available})${presetHint}`,\n )\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(configSet: string, config: ResolvedConfig): ProviderV3Compatible {\n const { baseURL, apiKey, compatible, headers } = config\n\n // Merge headers: defaults.headers as base, config-set headers override matching keys\n const mergedHeaders = (defaultHeaders || headers)\n ? { ...defaultHeaders, ...headers }\n : undefined\n\n const baseOpts = {\n baseURL,\n apiKey,\n ...(mergedHeaders && { headers: mergedHeaders }),\n ...(defaultFetch && { fetch: defaultFetch }),\n }\n\n switch (compatible) {\n case 'openai':\n try {\n return factories.createOpenAI(baseOpts)\n }\n catch (error) {\n // Fallback to OpenAI-compatible when:\n // - @ai-sdk/openai is not installed (module not found)\n // - User-provided factories don't include openai (ProviderNotAvailableError)\n if (isModuleNotFoundError(error, '@ai-sdk/openai') || error instanceof ProviderNotAvailableError) {\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (fallbackError) {\n // Only swallow module-not-found errors from the fallback itself\n if (isModuleNotFoundError(fallbackError, '@ai-sdk/openai-compatible') || fallbackError instanceof ProviderNotAvailableError) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai or its openai-compatible fallback. '\n + 'Install @ai-sdk/openai for full OpenAI features, or if using a bundler, '\n + 'provide factories: { openai: createOpenAI }',\n )\n }\n // Non-module-not-found errors from openai-compatible (e.g. config issues) should propagate\n throw fallbackError\n }\n }\n throw error\n }\n case 'anthropic':\n try {\n return factories.createAnthropic(baseOpts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/anthropic')) {\n throw new Error(\n '[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n throw error\n }\n case 'gemini':\n try {\n return factories.createGemini(baseOpts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/google')) {\n throw new Error(\n '[ai-sdk-provider-env] Google provider requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n throw error\n }\n case 'openai-compatible':\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/openai-compatible')) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai-compatible. '\n + 'If using a bundler, provide factories: { openaiCompatible: createOpenAICompatible }',\n )\n }\n throw error\n }\n default:\n throw new Error(\n `[ai-sdk-provider-env] Unknown compatible mode \"${compatible}\".`\n + ` Supported values: \"openai\", \"anthropic\", \"gemini\", \"openai-compatible\".`\n + ` Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`,\n )\n }\n }\n\n /**\n * Compute a cache key for the given config set.\n *\n * Explicit configs use the raw (uppercased) name, so `foo-bar` and `foo_bar`\n * remain distinct when both are defined in `configs`.\n * Env-var-backed config sets normalize hyphens to underscores, so aliases\n * like `my-api` and `my_api` share one cached provider.\n */\n function getCacheKey(configSet: string, config: ResolvedConfig, effectiveCompatible: string): string {\n if (options.configs?.[configSet]) {\n if (config.nativeRouting) {\n return `config:${configSet.toUpperCase()}:${effectiveCompatible}`\n }\n return `config:${configSet.toUpperCase()}`\n }\n if (config.nativeRouting) {\n return `env:${configSet.replace(/-/g, '_').toUpperCase()}:${effectiveCompatible}`\n }\n return `env:${configSet.replace(/-/g, '_').toUpperCase()}`\n }\n\n /**\n * Get or create a cached provider for the given config set.\n *\n * Returns `ProviderV3` via a safe cast from `ProviderV3Compatible`.\n * This is safe because `ProviderV3` and `ProviderV4` have identical\n * method signatures — only `specificationVersion` and model type brands differ.\n */\n function getProvider(configSet: string, model?: string): ProviderV3 {\n const config = resolveConfig(configSet)\n\n const detectedCompatible = (config.nativeRouting && model)\n ? detectNativeCompatible(model)\n : undefined\n const effectiveCompatible = detectedCompatible ?? config.compatible\n const wasAutoRouted = detectedCompatible != null && detectedCompatible !== config.compatible\n\n const key = getCacheKey(configSet, config, effectiveCompatible)\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const configForProvider = wasAutoRouted\n ? { ...config, compatible: effectiveCompatible }\n : config\n\n try {\n // Safe cast: V3 and V4 providers are structurally identical.\n // The underlying provider may be ProviderV3 or ProviderV4 depending\n // on which SDK version the user has installed.\n const provider = createUnderlying(configSet, configForProvider) as unknown as ProviderV3\n cache.set(key, provider)\n return provider\n }\n catch (error) {\n // When nativeRouting auto-routed to a provider whose SDK is not installed,\n // append a hint so users know they can disable routing to fall back.\n // Only for module-not-found errors — other errors (config issues, SDK bugs)\n // should propagate without misleading nativeRouting context.\n if (wasAutoRouted && error instanceof Error && error.message.includes('[ai-sdk-provider-env]')) {\n const prefix = configSet.replace(/-/g, '_').toUpperCase()\n throw new Error(\n `${error.message}`\n + ` (nativeRouting auto-detected this model as ${effectiveCompatible}.`\n + ` Disable with ${prefix}${separator}NATIVE_ROUTING=false to use ${config.compatible} instead.)`,\n { cause: error },\n )\n }\n throw error\n }\n }\n\n /**\n * Parse a model ID. The first `/` separates the config set name from the actual model ID.\n */\n function parseModelId(modelId: string): { configSet: string, model: string } {\n const slashIndex = modelId.indexOf('/')\n if (slashIndex === -1) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid model ID \"${modelId}\". `\n + `Expected format: \"{configSet}/{modelId}\", e.g. \"zhipu/glm-4\"`,\n )\n }\n return {\n configSet: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n }\n\n return {\n specificationVersion: 'v3' as const,\n\n languageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet, model).languageModel(model)\n },\n\n embeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).embeddingModel(model)\n },\n\n imageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).imageModel(model)\n },\n\n textEmbeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n // Prefer textEmbeddingModel if the underlying provider has it (V3).\n if (provider.textEmbeddingModel) {\n return provider.textEmbeddingModel(model)\n }\n // V4 providers removed textEmbeddingModel — fall back to embeddingModel\n // which exists in both V3 and V4 interfaces.\n if (provider.embeddingModel) {\n return provider.embeddingModel(model)\n }\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n },\n\n transcriptionModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.transcriptionModel) {\n throw new NoSuchModelError({ modelId, modelType: 'transcriptionModel' })\n }\n return provider.transcriptionModel(model)\n },\n\n speechModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.speechModel) {\n throw new NoSuchModelError({ modelId, modelType: 'speechModel' })\n }\n return provider.speechModel(model)\n },\n\n rerankingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.rerankingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'rerankingModel' })\n }\n return provider.rerankingModel(model)\n },\n }\n}\n\n/**\n * Build internal `ProviderFactories` from user-provided `EnvProviderFactories`.\n *\n * Uses lazy-strict semantics: each factory slot is only evaluated when actually called.\n * If the user provided a factory for a given compatible mode, it is used;\n * otherwise, a clear error is thrown (no silent fallback to dynamic `require()`).\n */\nfunction buildUserFactories(userFactories: EnvProviderFactories): ProviderFactories {\n function missingFactory(key: string, fnName: string, pkg: string): never {\n throw new Error(\n `[ai-sdk-provider-env] No factory provided for \"${key}\". `\n + `When using the factories option, provide a factory for each compatibility mode you use. `\n + `Add: import { ${fnName} } from '${pkg}' and set factories: { ${key}: ${fnName} }`,\n )\n }\n\n return {\n createOpenAI: (opts) => {\n if (userFactories.openai)\n return userFactories.openai(opts)\n // Signal fallback — createUnderlying will catch this and use openai-compatible\n throw new ProviderNotAvailableError('openai')\n },\n createAnthropic: opts =>\n userFactories.anthropic\n ? userFactories.anthropic(opts)\n : missingFactory('anthropic', 'createAnthropic', '@ai-sdk/anthropic'),\n createGemini: opts =>\n userFactories.gemini\n ? userFactories.gemini(opts)\n : missingFactory('gemini', 'createGoogleGenerativeAI', '@ai-sdk/google'),\n createOpenAICompatible: opts =>\n userFactories.openaiCompatible\n ? userFactories.openaiCompatible(opts)\n : createOpenAICompatibleProvider(opts),\n }\n}\n\n/**\n * Create a dynamic, environment-variable-driven AI SDK provider.\n *\n * Automatically resolves provider configurations from env var naming conventions,\n * with built-in preset support for quick setup.\n *\n * Config set naming rules:\n * - Names must match `[A-Za-z_][A-Za-z0-9_-]*` (ASCII letters, digits, underscores, hyphens)\n * - Hyphens are normalized to underscores for env var lookup:\n * `my-api/model` → reads `MY_API_API_KEY`, `MY_API_BASE_URL`, etc.\n * - For arbitrary names, use the `configs` option instead\n *\n * Env var convention (using config set `ZHIPU` with default separator `_` as example):\n * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)\n * - `ZHIPU_BASE_URL` — API base URL\n * - `ZHIPU_API_KEY` — API key (required)\n * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)\n * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)\n *\n * @example\n * ```ts\n * import { createProviderRegistry } from 'ai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const registry = createProviderRegistry({\n * env: envProvider(),\n * })\n *\n * // Use a preset (only API_KEY is required)\n * // DEEPSEEK_PRESET=deepseek\n * // DEEPSEEK_API_KEY=sk-xxx\n * const model = registry.languageModel('env:deepseek/deepseek-chat')\n *\n * // Specify all parameters manually\n * // MYAPI_BASE_URL=https://api.example.com/v1\n * // MYAPI_API_KEY=xxx\n * const model2 = registry.languageModel('env:myapi/some-model')\n * ```\n *\n * @example Bundler-safe usage with explicit factories\n * ```ts\n * import { createOpenAI } from '@ai-sdk/openai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const provider = envProvider({\n * factories: { openai: createOpenAI },\n * })\n * ```\n */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n const factories = options.factories\n ? buildUserFactories(options.factories)\n : defaultFactories\n return createEnvProvider(factories, options)\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -58,6 +58,14 @@ interface ConfigSetEntry {
|
|
|
58
58
|
* `{PREFIX}_HEADERS={"X-Custom-Header":"value"}`
|
|
59
59
|
*/
|
|
60
60
|
headers?: Record<string, string>;
|
|
61
|
+
/**
|
|
62
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
63
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
64
|
+
* Non-matching models fall back to this config set's default `compatible` mode.
|
|
65
|
+
*
|
|
66
|
+
* Can also be set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
67
|
+
*/
|
|
68
|
+
nativeRouting?: boolean;
|
|
61
69
|
}
|
|
62
70
|
/**
|
|
63
71
|
* Preset provider configuration.
|
|
@@ -76,6 +84,14 @@ interface PresetConfig {
|
|
|
76
84
|
* - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
|
|
77
85
|
*/
|
|
78
86
|
compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
|
|
87
|
+
/**
|
|
88
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
89
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
90
|
+
* Non-matching models fall back to the preset's default `compatible` mode.
|
|
91
|
+
*
|
|
92
|
+
* Control per-config-set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
93
|
+
*/
|
|
94
|
+
nativeRouting?: boolean;
|
|
79
95
|
}
|
|
80
96
|
/**
|
|
81
97
|
* Global defaults for `envProvider()`.
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAcA;;;;;;;;;;;UAAiB,oBAAA;EAAA,SACN,oBAAA;EACT,aAAA,GAAgB,OAAA;EAChB,cAAA,GAAiB,OAAA;EACjB,UAAA,GAAa,OAAA;EACb,kBAAA,KAAuB,OAAA;EACvB,kBAAA,KAAuB,OAAA;EACvB,WAAA,KAAgB,OAAA;EAChB,cAAA,KAAmB,OAAA;AAAA;AASrB;;;;;;AAAA,UAAiB,cAAA;EAmBf;EAjBA,MAAA;EAwBU
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAcA;;;;;;;;;;;UAAiB,oBAAA;EAAA,SACN,oBAAA;EACT,aAAA,GAAgB,OAAA;EAChB,cAAA,GAAiB,OAAA;EACjB,UAAA,GAAa,OAAA;EACb,kBAAA,KAAuB,OAAA;EACvB,kBAAA,KAAuB,OAAA;EACvB,WAAA,KAAgB,OAAA;EAChB,cAAA,KAAmB,OAAA;AAAA;AASrB;;;;;;AAAA,UAAiB,cAAA;EAmBf;EAjBA,MAAA;EAwBU;;;;AAgBZ;EAlCE,MAAA;;EAEA,OAAA;EAkCA;;;;;AAyBF;;;EAlDE,UAAA;EA2DA;;;;;;EApDA,OAAA,GAAU,MAAA;EA4EK;;;;;;;EApEf,aAAA;AAAA;;;;;AAqFF;UA7EiB,YAAA;;EAEf,OAAA;EA6EI;AAwBN;;;;;;;EA5FE,UAAA;EAgHiD;;;;;;;EAxGjD,aAAA;AAAA;;;;;;UAQe,mBAAA;EAgGL;;;;;;;;EAvFV,KAAA,UAAe,UAAA,CAAW,KAAA;EAoGO;;;;;;;;;;;;;EArFjC,OAAA,GAAU,MAAA;AAAA;;;;;;;UASK,yBAAA;EC+cD;ED7cd,OAAA;;EAEA,MAAA;EC2cmC;EDzcnC,OAAA,GAAU,MAAA;ECycmD;EDvc7D,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;AErI5B;;;UF8IiB,8BAAA,SAAuC,yBAAA;EE9IA;EFgJtD,IAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;UAwBe,oBAAA;;;;;;;;EAQf,MAAA,IAAU,OAAA,EAAS,yBAAA,KAA8B,oBAAA;;;;;;EAMjD,SAAA,IAAa,OAAA,EAAS,yBAAA,KAA8B,oBAAA;;;;;;EAMpD,MAAA,IAAU,OAAA,EAAS,yBAAA,KAA8B,oBAAA;;;;;;;EAOjD,gBAAA,IAAoB,OAAA,EAAS,8BAAA,KAAmC,oBAAA;AAAA;;;;UAMjD,kBAAA;;;;;;;;;;;;;;;;;;EAkBf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;;;;;;;;;;;;;;;;;;;EAoBX,gBAAA;;;;;;;;;;;;;;;;;;EAmBA,SAAA,GAAY,oBAAA;AAAA;;;;;AArOd;;;;;;;;;AA2BA;;;;;;;;;;;AAiCA;;;;;;;;;;;;;AAiBA;;;;;AA0BA;;;;;;;;;iBCoagB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;ADtkB/D;;;;cENa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -58,6 +58,14 @@ interface ConfigSetEntry {
|
|
|
58
58
|
* `{PREFIX}_HEADERS={"X-Custom-Header":"value"}`
|
|
59
59
|
*/
|
|
60
60
|
headers?: Record<string, string>;
|
|
61
|
+
/**
|
|
62
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
63
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
64
|
+
* Non-matching models fall back to this config set's default `compatible` mode.
|
|
65
|
+
*
|
|
66
|
+
* Can also be set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
67
|
+
*/
|
|
68
|
+
nativeRouting?: boolean;
|
|
61
69
|
}
|
|
62
70
|
/**
|
|
63
71
|
* Preset provider configuration.
|
|
@@ -76,6 +84,14 @@ interface PresetConfig {
|
|
|
76
84
|
* - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
|
|
77
85
|
*/
|
|
78
86
|
compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
|
|
87
|
+
/**
|
|
88
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
89
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
90
|
+
* Non-matching models fall back to the preset's default `compatible` mode.
|
|
91
|
+
*
|
|
92
|
+
* Control per-config-set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
93
|
+
*/
|
|
94
|
+
nativeRouting?: boolean;
|
|
79
95
|
}
|
|
80
96
|
/**
|
|
81
97
|
* Global defaults for `envProvider()`.
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAcA;;;;;;;;;;;UAAiB,oBAAA;EAAA,SACN,oBAAA;EACT,aAAA,GAAgB,OAAA;EAChB,cAAA,GAAiB,OAAA;EACjB,UAAA,GAAa,OAAA;EACb,kBAAA,KAAuB,OAAA;EACvB,kBAAA,KAAuB,OAAA;EACvB,WAAA,KAAgB,OAAA;EAChB,cAAA,KAAmB,OAAA;AAAA;AASrB;;;;;;AAAA,UAAiB,cAAA;EAmBf;EAjBA,MAAA;EAwBU
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAcA;;;;;;;;;;;UAAiB,oBAAA;EAAA,SACN,oBAAA;EACT,aAAA,GAAgB,OAAA;EAChB,cAAA,GAAiB,OAAA;EACjB,UAAA,GAAa,OAAA;EACb,kBAAA,KAAuB,OAAA;EACvB,kBAAA,KAAuB,OAAA;EACvB,WAAA,KAAgB,OAAA;EAChB,cAAA,KAAmB,OAAA;AAAA;AASrB;;;;;;AAAA,UAAiB,cAAA;EAmBf;EAjBA,MAAA;EAwBU;;;;AAgBZ;EAlCE,MAAA;;EAEA,OAAA;EAkCA;;;;;AAyBF;;;EAlDE,UAAA;EA2DA;;;;;;EApDA,OAAA,GAAU,MAAA;EA4EK;;;;;;;EApEf,aAAA;AAAA;;;;;AAqFF;UA7EiB,YAAA;;EAEf,OAAA;EA6EI;AAwBN;;;;;;;EA5FE,UAAA;EAgHiD;;;;;;;EAxGjD,aAAA;AAAA;;;;;;UAQe,mBAAA;EAgGL;;;;;;;;EAvFV,KAAA,UAAe,UAAA,CAAW,KAAA;EAoGO;;;;;;;;;;;;;EArFjC,OAAA,GAAU,MAAA;AAAA;;;;;;;UASK,yBAAA;EC+cD;ED7cd,OAAA;;EAEA,MAAA;EC2cmC;EDzcnC,OAAA,GAAU,MAAA;ECycmD;EDvc7D,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;AErI5B;;;UF8IiB,8BAAA,SAAuC,yBAAA;EE9IA;EFgJtD,IAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;UAwBe,oBAAA;;;;;;;;EAQf,MAAA,IAAU,OAAA,EAAS,yBAAA,KAA8B,oBAAA;;;;;;EAMjD,SAAA,IAAa,OAAA,EAAS,yBAAA,KAA8B,oBAAA;;;;;;EAMpD,MAAA,IAAU,OAAA,EAAS,yBAAA,KAA8B,oBAAA;;;;;;;EAOjD,gBAAA,IAAoB,OAAA,EAAS,8BAAA,KAAmC,oBAAA;AAAA;;;;UAMjD,kBAAA;;;;;;;;;;;;;;;;;;EAkBf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;;;;;;;;;;;;;;;;;;;EAoBX,gBAAA;;;;;;;;;;;;;;;;;;EAmBA,SAAA,GAAY,oBAAA;AAAA;;;;;AArOd;;;;;;;;;AA2BA;;;;;;;;;;;AAiCA;;;;;;;;;;;;;AAiBA;;;;;AA0BA;;;;;;;;;iBCoagB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;ADtkB/D;;;;cENa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -153,7 +153,8 @@ const builtinPresets = {
|
|
|
153
153
|
},
|
|
154
154
|
"opencode-zen": {
|
|
155
155
|
baseURL: "https://opencode.ai/zen/v1",
|
|
156
|
-
compatible: "openai-compatible"
|
|
156
|
+
compatible: "openai-compatible",
|
|
157
|
+
nativeRouting: true
|
|
157
158
|
},
|
|
158
159
|
"opencode-go": {
|
|
159
160
|
baseURL: "https://opencode.ai/zen/go/v1",
|
|
@@ -183,6 +184,20 @@ const defaultFactories = {
|
|
|
183
184
|
createOpenAICompatible: createOpenAICompatibleProvider
|
|
184
185
|
};
|
|
185
186
|
/**
|
|
187
|
+
* Detect the native compatible mode for a model based on its ID prefix.
|
|
188
|
+
*
|
|
189
|
+
* Used by nativeRouting to auto-route model families to their native provider SDKs.
|
|
190
|
+
* Only `claude-*`, `gemini-*`, and `gpt-*` prefixes are matched.
|
|
191
|
+
* Known limitation: `o1-*`, `o3-*`, `chatgpt-*` are NOT matched (use explicit compatible mode).
|
|
192
|
+
*
|
|
193
|
+
* @returns The detected compatible mode, or `undefined` if no match.
|
|
194
|
+
*/
|
|
195
|
+
function detectNativeCompatible(model) {
|
|
196
|
+
if (model.startsWith("claude-")) return "anthropic";
|
|
197
|
+
if (model.startsWith("gemini-")) return "gemini";
|
|
198
|
+
if (model.startsWith("gpt-")) return "openai";
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
186
201
|
* Testable core implementation that accepts injected provider factories.
|
|
187
202
|
*
|
|
188
203
|
* In tests, call this function directly with fake factories
|
|
@@ -204,7 +219,8 @@ function createEnvProvider(factories, options = {}) {
|
|
|
204
219
|
}
|
|
205
220
|
return {
|
|
206
221
|
baseURL: preset.baseURL,
|
|
207
|
-
compatible: preset.compatible ?? "openai-compatible"
|
|
222
|
+
compatible: preset.compatible ?? "openai-compatible",
|
|
223
|
+
nativeRouting: preset.nativeRouting
|
|
208
224
|
};
|
|
209
225
|
}
|
|
210
226
|
/**
|
|
@@ -219,6 +235,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
219
235
|
baseURL: config.baseURL ?? preset.baseURL,
|
|
220
236
|
apiKey: config.apiKey,
|
|
221
237
|
compatible: config.compatible ?? preset.compatible,
|
|
238
|
+
nativeRouting: config.nativeRouting ?? preset.nativeRouting,
|
|
222
239
|
...config.headers && { headers: config.headers }
|
|
223
240
|
};
|
|
224
241
|
}
|
|
@@ -227,6 +244,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
227
244
|
baseURL: config.baseURL,
|
|
228
245
|
apiKey: config.apiKey,
|
|
229
246
|
compatible: config.compatible ?? "openai-compatible",
|
|
247
|
+
nativeRouting: config.nativeRouting,
|
|
230
248
|
...config.headers && { headers: config.headers }
|
|
231
249
|
};
|
|
232
250
|
}
|
|
@@ -243,6 +261,14 @@ function createEnvProvider(factories, options = {}) {
|
|
|
243
261
|
} catch {
|
|
244
262
|
throw new Error(`[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`);
|
|
245
263
|
}
|
|
264
|
+
const nativeRoutingRaw = env("NATIVE_ROUTING");
|
|
265
|
+
let nativeRoutingFromEnv;
|
|
266
|
+
if (nativeRoutingRaw !== void 0) {
|
|
267
|
+
const lower = nativeRoutingRaw.toLowerCase();
|
|
268
|
+
if (lower === "true") nativeRoutingFromEnv = true;
|
|
269
|
+
else if (lower === "false") nativeRoutingFromEnv = false;
|
|
270
|
+
else throw new Error(`[ai-sdk-provider-env] Invalid value for ${prefix}${separator}NATIVE_ROUTING: "${nativeRoutingRaw}". Expected "true" or "false".`);
|
|
271
|
+
}
|
|
246
272
|
const presetName = env("PRESET");
|
|
247
273
|
if (presetName) {
|
|
248
274
|
const preset = resolvePreset(presetName);
|
|
@@ -250,6 +276,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
250
276
|
baseURL: env("BASE_URL") ?? preset.baseURL,
|
|
251
277
|
apiKey,
|
|
252
278
|
compatible: env("COMPATIBLE") ?? preset.compatible,
|
|
279
|
+
nativeRouting: nativeRoutingFromEnv ?? preset.nativeRouting,
|
|
253
280
|
...headers && { headers }
|
|
254
281
|
};
|
|
255
282
|
}
|
|
@@ -258,6 +285,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
258
285
|
baseURL,
|
|
259
286
|
apiKey,
|
|
260
287
|
compatible: env("COMPATIBLE") ?? "openai-compatible",
|
|
288
|
+
nativeRouting: nativeRoutingFromEnv,
|
|
261
289
|
...headers && { headers }
|
|
262
290
|
};
|
|
263
291
|
if (options.presetAutoDetect !== false) {
|
|
@@ -266,6 +294,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
266
294
|
baseURL: autoPreset.baseURL,
|
|
267
295
|
apiKey,
|
|
268
296
|
compatible: env("COMPATIBLE") ?? autoPreset.compatible ?? "openai-compatible",
|
|
297
|
+
nativeRouting: nativeRoutingFromEnv ?? autoPreset.nativeRouting,
|
|
269
298
|
...headers && { headers }
|
|
270
299
|
};
|
|
271
300
|
}
|
|
@@ -303,8 +332,18 @@ function createEnvProvider(factories, options = {}) {
|
|
|
303
332
|
}
|
|
304
333
|
throw error;
|
|
305
334
|
}
|
|
306
|
-
case "anthropic":
|
|
307
|
-
|
|
335
|
+
case "anthropic": try {
|
|
336
|
+
return factories.createAnthropic(baseOpts);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (isModuleNotFoundError(error, "@ai-sdk/anthropic")) throw new Error("[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. Run: npm install @ai-sdk/anthropic");
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
case "gemini": try {
|
|
342
|
+
return factories.createGemini(baseOpts);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (isModuleNotFoundError(error, "@ai-sdk/google")) throw new Error("[ai-sdk-provider-env] Google provider requires @ai-sdk/google. Run: npm install @ai-sdk/google");
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
308
347
|
case "openai-compatible": try {
|
|
309
348
|
return factories.createOpenAICompatible({
|
|
310
349
|
name: configSet,
|
|
@@ -325,8 +364,12 @@ function createEnvProvider(factories, options = {}) {
|
|
|
325
364
|
* Env-var-backed config sets normalize hyphens to underscores, so aliases
|
|
326
365
|
* like `my-api` and `my_api` share one cached provider.
|
|
327
366
|
*/
|
|
328
|
-
function getCacheKey(configSet) {
|
|
329
|
-
if (options.configs?.[configSet])
|
|
367
|
+
function getCacheKey(configSet, config, effectiveCompatible) {
|
|
368
|
+
if (options.configs?.[configSet]) {
|
|
369
|
+
if (config.nativeRouting) return `config:${configSet.toUpperCase()}:${effectiveCompatible}`;
|
|
370
|
+
return `config:${configSet.toUpperCase()}`;
|
|
371
|
+
}
|
|
372
|
+
if (config.nativeRouting) return `env:${configSet.replace(/-/g, "_").toUpperCase()}:${effectiveCompatible}`;
|
|
330
373
|
return `env:${configSet.replace(/-/g, "_").toUpperCase()}`;
|
|
331
374
|
}
|
|
332
375
|
/**
|
|
@@ -336,13 +379,29 @@ function createEnvProvider(factories, options = {}) {
|
|
|
336
379
|
* This is safe because `ProviderV3` and `ProviderV4` have identical
|
|
337
380
|
* method signatures — only `specificationVersion` and model type brands differ.
|
|
338
381
|
*/
|
|
339
|
-
function getProvider(configSet) {
|
|
340
|
-
const
|
|
382
|
+
function getProvider(configSet, model) {
|
|
383
|
+
const config = resolveConfig(configSet);
|
|
384
|
+
const detectedCompatible = config.nativeRouting && model ? detectNativeCompatible(model) : void 0;
|
|
385
|
+
const effectiveCompatible = detectedCompatible ?? config.compatible;
|
|
386
|
+
const wasAutoRouted = detectedCompatible != null && detectedCompatible !== config.compatible;
|
|
387
|
+
const key = getCacheKey(configSet, config, effectiveCompatible);
|
|
341
388
|
const cached = cache.get(key);
|
|
342
389
|
if (cached) return cached;
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
390
|
+
const configForProvider = wasAutoRouted ? {
|
|
391
|
+
...config,
|
|
392
|
+
compatible: effectiveCompatible
|
|
393
|
+
} : config;
|
|
394
|
+
try {
|
|
395
|
+
const provider = createUnderlying(configSet, configForProvider);
|
|
396
|
+
cache.set(key, provider);
|
|
397
|
+
return provider;
|
|
398
|
+
} catch (error) {
|
|
399
|
+
if (wasAutoRouted && error instanceof Error && error.message.includes("[ai-sdk-provider-env]")) {
|
|
400
|
+
const prefix = configSet.replace(/-/g, "_").toUpperCase();
|
|
401
|
+
throw new Error(`${error.message} (nativeRouting auto-detected this model as ${effectiveCompatible}. Disable with ${prefix}${separator}NATIVE_ROUTING=false to use ${config.compatible} instead.)`, { cause: error });
|
|
402
|
+
}
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
346
405
|
}
|
|
347
406
|
/**
|
|
348
407
|
* Parse a model ID. The first `/` separates the config set name from the actual model ID.
|
|
@@ -359,7 +418,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
359
418
|
specificationVersion: "v3",
|
|
360
419
|
languageModel(modelId) {
|
|
361
420
|
const { configSet, model } = parseModelId(modelId);
|
|
362
|
-
return getProvider(configSet).languageModel(model);
|
|
421
|
+
return getProvider(configSet, model).languageModel(model);
|
|
363
422
|
},
|
|
364
423
|
embeddingModel(modelId) {
|
|
365
424
|
const { configSet, model } = parseModelId(modelId);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, ProviderV3Compatible } from './types'\n\n/**\n * Check if an error is a \"module not found\" error for a specific package.\n *\n * Handles Node.js (error.code), Bun, and Bun compiled binaries\n * which may throw non-standard error objects.\n */\nexport function isModuleNotFoundError(error: unknown, packageName: string): boolean {\n if (typeof error !== 'object' || error === null)\n return false\n\n const message = 'message' in error && typeof error.message === 'string' ? error.message : ''\n const code = 'code' in error ? (error as Record<string, unknown>).code : undefined\n\n // Only match the module name in the \"Cannot find module/package 'X'\" part,\n // not in the require stack that follows. This prevents false positives when\n // a sub-dependency fails but the parent package appears in the stack trace.\n const isResolutionError = code === 'MODULE_NOT_FOUND'\n || code === 'ERR_MODULE_NOT_FOUND'\n || message.startsWith('Cannot find module')\n || message.startsWith('Cannot find package')\n\n if (!isResolutionError)\n return false\n\n // Extract the quoted module name from \"Cannot find module 'X'\" or \"Cannot find package 'X'\"\n // Handles both single and double quote styles across runtimes.\n const quoted = message.match(/Cannot find (?:module|package) ['\"]([^'\"]+)['\"]/)\n if (quoted)\n return quoted[1] === packageName || quoted[1].startsWith(`${packageName}/`)\n\n // Fallback for non-standard message formats (e.g. some Bun versions):\n // check if the package name appears before any \"Require stack:\" section,\n // with boundary checking to avoid false positives (e.g. @ai-sdk/openai matching @ai-sdk/openai-compatible).\n const requireStackIndex = message.indexOf('\\nRequire stack:')\n const relevantPart = requireStackIndex !== -1 ? message.slice(0, requireStackIndex) : message\n const boundaryPattern = new RegExp(`(?:^|[\\\\s'\"/@])${packageName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?:[\\\\s'\"/@]|$)`)\n return boundaryPattern.test(relevantPart)\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n * When not installed, errors propagate to allow fallback to OpenAI-compatible provider.\n */\nexport function createOpenAIProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n * No fallback is available — Anthropic does not support the OpenAI-compatible protocol.\n */\nexport function createAnthropicProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/anthropic')) {\n throw new Error(\n '[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n throw error\n }\n}\n\n/**\n * Create a Google Generative AI (Gemini) provider.\n *\n * Dynamically requires `@ai-sdk/google`, so it only needs to be installed when actually used.\n * No fallback is available — Google does not support the OpenAI-compatible protocol.\n */\nexport function createGeminiProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createGoogleGenerativeAI } = require('@ai-sdk/google')\n return createGoogleGenerativeAI(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/google')) {\n throw new Error(\n '[ai-sdk-provider-env] Google provider requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n throw error\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * `@ai-sdk/openai-compatible` is a regular dependency and always available.\n */\nexport function createOpenAICompatibleProvider(opts: EnvProviderNamedFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n}\n","import type { PresetConfig } from './types'\n\n/**\n * Built-in preset configurations for common providers.\n *\n * When using a preset, only `{PREFIX}__PRESET` and `{PREFIX}__API_KEY`\n * are required; `BASE_URL` and `COMPATIBLE` are provided by the preset.\n */\nexport const builtinPresets: Record<string, PresetConfig> = {\n // OpenAI\n 'openai': {\n baseURL: 'https://api.openai.com/v1',\n compatible: 'openai',\n },\n\n // Anthropic\n 'anthropic': {\n baseURL: 'https://api.anthropic.com',\n compatible: 'anthropic',\n },\n\n // Google AI Studio (Gemini)\n 'google': {\n baseURL: 'https://generativelanguage.googleapis.com/v1beta',\n compatible: 'gemini',\n },\n\n // DeepSeek\n 'deepseek': {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI (GLM series)\n 'zhipu': {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // Groq\n 'groq': {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai-compatible',\n },\n\n // Together AI\n 'together': {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai-compatible',\n },\n\n // Fireworks AI\n 'fireworks': {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai-compatible',\n },\n\n // Mistral AI\n 'mistral': {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (international)\n 'moonshot': {\n baseURL: 'https://api.moonshot.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (China mainland)\n 'moonshot-china': {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // Perplexity\n 'perplexity': {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai-compatible',\n },\n\n // OpenRouter\n 'openrouter': {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (international)\n 'siliconflow': {\n baseURL: 'https://api.siliconflow.com/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (China mainland)\n 'siliconflow-china': {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // xAI (Grok series)\n 'xai': {\n baseURL: 'https://api.x.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI International (Z.AI — global endpoint for GLM series)\n 'zai': {\n baseURL: 'https://api.z.ai/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // OpenCode Zen (curated multi-model AI gateway)\n 'opencode-zen': {\n baseURL: 'https://opencode.ai/zen/v1',\n compatible: 'openai-compatible',\n },\n\n // OpenCode Go (low-cost subscription for open coding models)\n 'opencode-go': {\n baseURL: 'https://opencode.ai/zen/go/v1',\n compatible: 'openai-compatible',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { EnvProviderFactories, EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, EnvProviderOptions, ProviderV3Compatible } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createGeminiProvider, createOpenAICompatibleProvider, createOpenAIProvider, isModuleNotFoundError } from './factories'\nimport { builtinPresets } from './presets'\n\n/**\n * Thrown when a provider is not available (factory not provided by user).\n * Used internally to trigger fallback to OpenAI-compatible provider.\n */\nclass ProviderNotAvailableError extends Error {\n constructor(provider: string) {\n super(`Provider \"${provider}\" is not available`)\n this.name = 'ProviderNotAvailableError'\n }\n}\n\n/**\n * Interface for provider factory functions, used for dependency injection.\n *\n * In production, `defaultFactories` delegates to the real SDK implementations.\n * In tests, fake factories can be injected to avoid module mocking.\n *\n * Return types use `ProviderV3Compatible` so that both `ProviderV3`\n * (from `@ai-sdk/provider@3.x`) and `ProviderV4` (`@ai-sdk/provider@4.x`)\n * are accepted.\n */\nexport interface ProviderFactories {\n createOpenAI: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createAnthropic: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createGemini: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createOpenAICompatible: (opts: EnvProviderNamedFactoryOptions) => ProviderV3Compatible\n}\n\n/**\n * Default factories that delegate to the real implementations in `factories.ts`.\n */\nconst defaultFactories: ProviderFactories = {\n createOpenAI: createOpenAIProvider,\n createAnthropic: createAnthropicProvider,\n createGemini: createGeminiProvider,\n createOpenAICompatible: createOpenAICompatibleProvider,\n}\n\n/**\n * Internally resolved configuration with all required fields determined.\n */\ninterface ResolvedConfig {\n baseURL: string\n apiKey: string\n compatible: string\n headers?: Record<string, string>\n}\n\n/**\n * Testable core implementation that accepts injected provider factories.\n *\n * In tests, call this function directly with fake factories\n * to avoid module mocking entirely.\n */\nexport function createEnvProvider(\n factories: ProviderFactories,\n options: Omit<EnvProviderOptions, 'factories'> = {},\n): ProviderV3 {\n const separator = options.separator ?? '_'\n const defaultFetch = options.defaults?.fetch\n const defaultHeaders = options.defaults?.headers\n\n // Cache created providers to avoid redundant initialization\n // Stored as ProviderV3 via safe cast from ProviderV3Compatible,\n // since V3 and V4 provider interfaces are structurally identical.\n const cache = new Map<string, ProviderV3>()\n\n /**\n * Resolve baseURL and compatible from a preset name.\n */\n function resolvePreset(presetName: string): { baseURL: string, compatible: string } {\n const preset = builtinPresets[presetName]\n if (!preset) {\n const available = Object.keys(builtinPresets).join(', ')\n throw new Error(\n `[ai-sdk-provider-env] Unknown preset \"${presetName}\". Available presets: ${available}`,\n )\n }\n return {\n baseURL: preset.baseURL,\n compatible: preset.compatible ?? 'openai-compatible',\n }\n }\n\n /**\n * Resolve config set configuration from explicit configs, presets, or environment variables.\n */\n function resolveConfig(configSet: string): ResolvedConfig {\n // Explicit configs take precedence over env vars\n if (options.configs?.[configSet]) {\n const config = options.configs[configSet]\n\n // Code-based configs also support presets\n if (config.preset) {\n const preset = resolvePreset(config.preset)\n return {\n baseURL: config.baseURL ?? preset.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? preset.compatible,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n if (!config.baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing baseURL in config for \"${configSet}\"`\n + ` (or set preset to use a built-in preset)`,\n )\n }\n\n return {\n baseURL: config.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? 'openai-compatible',\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n // --- All checks below apply only to the env-var resolution path ---\n // The configs option (above) bypasses these and accepts arbitrary names/separators.\n\n // Validate separator produces shell-safe env var names (deferred to first env-var use)\n if (!/^\\w+$/.test(separator)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid separator \"${separator}\". `\n + `Separator must only contain ASCII letters, digits, or underscores to produce shell-safe env var names.`,\n )\n }\n\n // Validate config set name (ASCII shell-safe names only)\n if (!/^[A-Z_][\\w-]*$/i.test(configSet)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid config set name \"${configSet}\". `\n + `Names must start with a letter or underscore, followed by letters, digits, underscores, or hyphens. `\n + `For arbitrary names, use the configs option instead.`,\n )\n }\n\n // Normalize hyphens to underscores for POSIX shell-safe env var names.\n // e.g. \"my-api\" → \"MY_API\", so env vars are MY_API_API_KEY, MY_API_BASE_URL, etc.\n const prefix = configSet.replace(/-/g, '_').toUpperCase()\n const env = (key: string): string | undefined => process.env[`${prefix}${separator}${key}`]\n\n const apiKey = env('API_KEY')\n if (!apiKey) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}API_KEY`,\n )\n }\n\n // Parse headers from env var (JSON format)\n const headersRaw = env('HEADERS')\n let headers: Record<string, string> | undefined\n if (headersRaw) {\n try {\n headers = JSON.parse(headersRaw)\n }\n catch {\n throw new Error(\n `[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`,\n )\n }\n }\n\n // Check for preset\n const presetName = env('PRESET')\n if (presetName) {\n const preset = resolvePreset(presetName)\n return {\n baseURL: env('BASE_URL') ?? preset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? preset.compatible,\n ...(headers && { headers }),\n }\n }\n\n // Without a preset, try BASE_URL first\n const baseURL = env('BASE_URL')\n if (baseURL) {\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai-compatible',\n ...(headers && { headers }),\n }\n }\n\n // Auto-detect: if configSet name matches a built-in preset, use it automatically\n if (options.presetAutoDetect !== false) {\n const autoPreset = builtinPresets[configSet.toLowerCase()]\n if (autoPreset) {\n return {\n baseURL: autoPreset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? autoPreset.compatible ?? 'openai-compatible',\n ...(headers && { headers }),\n }\n }\n }\n\n // Error: neither BASE_URL nor a matching preset found\n const available = Object.keys(builtinPresets).join(', ')\n const presetHint = builtinPresets[configSet.toLowerCase()]\n ? ` (Note: \"${configSet}\" matches a built-in preset, but presetAutoDetect is disabled.)`\n : ''\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL`\n + ` (or set ${prefix}${separator}PRESET to use a preset.`\n + ` Available presets: ${available})${presetHint}`,\n )\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(configSet: string, config: ResolvedConfig): ProviderV3Compatible {\n const { baseURL, apiKey, compatible, headers } = config\n\n // Merge headers: defaults.headers as base, config-set headers override matching keys\n const mergedHeaders = (defaultHeaders || headers)\n ? { ...defaultHeaders, ...headers }\n : undefined\n\n const baseOpts = {\n baseURL,\n apiKey,\n ...(mergedHeaders && { headers: mergedHeaders }),\n ...(defaultFetch && { fetch: defaultFetch }),\n }\n\n switch (compatible) {\n case 'openai':\n try {\n return factories.createOpenAI(baseOpts)\n }\n catch (error) {\n // Fallback to OpenAI-compatible when:\n // - @ai-sdk/openai is not installed (module not found)\n // - User-provided factories don't include openai (ProviderNotAvailableError)\n if (isModuleNotFoundError(error, '@ai-sdk/openai') || error instanceof ProviderNotAvailableError) {\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (fallbackError) {\n // Only swallow module-not-found errors from the fallback itself\n if (isModuleNotFoundError(fallbackError, '@ai-sdk/openai-compatible') || fallbackError instanceof ProviderNotAvailableError) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai or its openai-compatible fallback. '\n + 'Install @ai-sdk/openai for full OpenAI features, or if using a bundler, '\n + 'provide factories: { openai: createOpenAI }',\n )\n }\n // Non-module-not-found errors from openai-compatible (e.g. config issues) should propagate\n throw fallbackError\n }\n }\n throw error\n }\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n case 'gemini':\n return factories.createGemini(baseOpts)\n case 'openai-compatible':\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/openai-compatible')) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai-compatible. '\n + 'If using a bundler, provide factories: { openaiCompatible: createOpenAICompatible }',\n )\n }\n throw error\n }\n default:\n throw new Error(\n `[ai-sdk-provider-env] Unknown compatible mode \"${compatible}\".`\n + ` Supported values: \"openai\", \"anthropic\", \"gemini\", \"openai-compatible\".`\n + ` Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`,\n )\n }\n }\n\n /**\n * Compute a cache key for the given config set.\n *\n * Explicit configs use the raw (uppercased) name, so `foo-bar` and `foo_bar`\n * remain distinct when both are defined in `configs`.\n * Env-var-backed config sets normalize hyphens to underscores, so aliases\n * like `my-api` and `my_api` share one cached provider.\n */\n function getCacheKey(configSet: string): string {\n if (options.configs?.[configSet]) {\n return `config:${configSet.toUpperCase()}`\n }\n return `env:${configSet.replace(/-/g, '_').toUpperCase()}`\n }\n\n /**\n * Get or create a cached provider for the given config set.\n *\n * Returns `ProviderV3` via a safe cast from `ProviderV3Compatible`.\n * This is safe because `ProviderV3` and `ProviderV4` have identical\n * method signatures — only `specificationVersion` and model type brands differ.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = getCacheKey(configSet)\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n // Safe cast: V3 and V4 providers are structurally identical.\n // The underlying provider may be ProviderV3 or ProviderV4 depending\n // on which SDK version the user has installed.\n const provider = createUnderlying(configSet, config) as unknown as ProviderV3\n cache.set(key, provider)\n return provider\n }\n\n /**\n * Parse a model ID. The first `/` separates the config set name from the actual model ID.\n */\n function parseModelId(modelId: string): { configSet: string, model: string } {\n const slashIndex = modelId.indexOf('/')\n if (slashIndex === -1) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid model ID \"${modelId}\". `\n + `Expected format: \"{configSet}/{modelId}\", e.g. \"zhipu/glm-4\"`,\n )\n }\n return {\n configSet: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n }\n\n return {\n specificationVersion: 'v3' as const,\n\n languageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).languageModel(model)\n },\n\n embeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).embeddingModel(model)\n },\n\n imageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).imageModel(model)\n },\n\n textEmbeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n // Prefer textEmbeddingModel if the underlying provider has it (V3).\n if (provider.textEmbeddingModel) {\n return provider.textEmbeddingModel(model)\n }\n // V4 providers removed textEmbeddingModel — fall back to embeddingModel\n // which exists in both V3 and V4 interfaces.\n if (provider.embeddingModel) {\n return provider.embeddingModel(model)\n }\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n },\n\n transcriptionModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.transcriptionModel) {\n throw new NoSuchModelError({ modelId, modelType: 'transcriptionModel' })\n }\n return provider.transcriptionModel(model)\n },\n\n speechModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.speechModel) {\n throw new NoSuchModelError({ modelId, modelType: 'speechModel' })\n }\n return provider.speechModel(model)\n },\n\n rerankingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.rerankingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'rerankingModel' })\n }\n return provider.rerankingModel(model)\n },\n }\n}\n\n/**\n * Build internal `ProviderFactories` from user-provided `EnvProviderFactories`.\n *\n * Uses lazy-strict semantics: each factory slot is only evaluated when actually called.\n * If the user provided a factory for a given compatible mode, it is used;\n * otherwise, a clear error is thrown (no silent fallback to dynamic `require()`).\n */\nfunction buildUserFactories(userFactories: EnvProviderFactories): ProviderFactories {\n function missingFactory(key: string, fnName: string, pkg: string): never {\n throw new Error(\n `[ai-sdk-provider-env] No factory provided for \"${key}\". `\n + `When using the factories option, provide a factory for each compatibility mode you use. `\n + `Add: import { ${fnName} } from '${pkg}' and set factories: { ${key}: ${fnName} }`,\n )\n }\n\n return {\n createOpenAI: (opts) => {\n if (userFactories.openai)\n return userFactories.openai(opts)\n // Signal fallback — createUnderlying will catch this and use openai-compatible\n throw new ProviderNotAvailableError('openai')\n },\n createAnthropic: opts =>\n userFactories.anthropic\n ? userFactories.anthropic(opts)\n : missingFactory('anthropic', 'createAnthropic', '@ai-sdk/anthropic'),\n createGemini: opts =>\n userFactories.gemini\n ? userFactories.gemini(opts)\n : missingFactory('gemini', 'createGoogleGenerativeAI', '@ai-sdk/google'),\n createOpenAICompatible: opts =>\n userFactories.openaiCompatible\n ? userFactories.openaiCompatible(opts)\n : createOpenAICompatibleProvider(opts),\n }\n}\n\n/**\n * Create a dynamic, environment-variable-driven AI SDK provider.\n *\n * Automatically resolves provider configurations from env var naming conventions,\n * with built-in preset support for quick setup.\n *\n * Config set naming rules:\n * - Names must match `[A-Za-z_][A-Za-z0-9_-]*` (ASCII letters, digits, underscores, hyphens)\n * - Hyphens are normalized to underscores for env var lookup:\n * `my-api/model` → reads `MY_API_API_KEY`, `MY_API_BASE_URL`, etc.\n * - For arbitrary names, use the `configs` option instead\n *\n * Env var convention (using config set `ZHIPU` with default separator `_` as example):\n * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)\n * - `ZHIPU_BASE_URL` — API base URL\n * - `ZHIPU_API_KEY` — API key (required)\n * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)\n * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)\n *\n * @example\n * ```ts\n * import { createProviderRegistry } from 'ai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const registry = createProviderRegistry({\n * env: envProvider(),\n * })\n *\n * // Use a preset (only API_KEY is required)\n * // DEEPSEEK_PRESET=deepseek\n * // DEEPSEEK_API_KEY=sk-xxx\n * const model = registry.languageModel('env:deepseek/deepseek-chat')\n *\n * // Specify all parameters manually\n * // MYAPI_BASE_URL=https://api.example.com/v1\n * // MYAPI_API_KEY=xxx\n * const model2 = registry.languageModel('env:myapi/some-model')\n * ```\n *\n * @example Bundler-safe usage with explicit factories\n * ```ts\n * import { createOpenAI } from '@ai-sdk/openai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const provider = envProvider({\n * factories: { openai: createOpenAI },\n * })\n * ```\n */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n const factories = options.factories\n ? buildUserFactories(options.factories)\n : defaultFactories\n return createEnvProvider(factories, options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, ProviderV3Compatible } from './types'\n\n/**\n * Check if an error is a \"module not found\" error for a specific package.\n *\n * Handles Node.js (error.code), Bun, and Bun compiled binaries\n * which may throw non-standard error objects.\n */\nexport function isModuleNotFoundError(error: unknown, packageName: string): boolean {\n if (typeof error !== 'object' || error === null)\n return false\n\n const message = 'message' in error && typeof error.message === 'string' ? error.message : ''\n const code = 'code' in error ? (error as Record<string, unknown>).code : undefined\n\n // Only match the module name in the \"Cannot find module/package 'X'\" part,\n // not in the require stack that follows. This prevents false positives when\n // a sub-dependency fails but the parent package appears in the stack trace.\n const isResolutionError = code === 'MODULE_NOT_FOUND'\n || code === 'ERR_MODULE_NOT_FOUND'\n || message.startsWith('Cannot find module')\n || message.startsWith('Cannot find package')\n\n if (!isResolutionError)\n return false\n\n // Extract the quoted module name from \"Cannot find module 'X'\" or \"Cannot find package 'X'\"\n // Handles both single and double quote styles across runtimes.\n const quoted = message.match(/Cannot find (?:module|package) ['\"]([^'\"]+)['\"]/)\n if (quoted)\n return quoted[1] === packageName || quoted[1].startsWith(`${packageName}/`)\n\n // Fallback for non-standard message formats (e.g. some Bun versions):\n // check if the package name appears before any \"Require stack:\" section,\n // with boundary checking to avoid false positives (e.g. @ai-sdk/openai matching @ai-sdk/openai-compatible).\n const requireStackIndex = message.indexOf('\\nRequire stack:')\n const relevantPart = requireStackIndex !== -1 ? message.slice(0, requireStackIndex) : message\n const boundaryPattern = new RegExp(`(?:^|[\\\\s'\"/@])${packageName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?:[\\\\s'\"/@]|$)`)\n return boundaryPattern.test(relevantPart)\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n * When not installed, errors propagate to allow fallback to OpenAI-compatible provider.\n */\nexport function createOpenAIProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n * No fallback is available — Anthropic does not support the OpenAI-compatible protocol.\n */\nexport function createAnthropicProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/anthropic')) {\n throw new Error(\n '[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n throw error\n }\n}\n\n/**\n * Create a Google Generative AI (Gemini) provider.\n *\n * Dynamically requires `@ai-sdk/google`, so it only needs to be installed when actually used.\n * No fallback is available — Google does not support the OpenAI-compatible protocol.\n */\nexport function createGeminiProvider(opts: EnvProviderFactoryOptions): ProviderV3Compatible {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createGoogleGenerativeAI } = require('@ai-sdk/google')\n return createGoogleGenerativeAI(opts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/google')) {\n throw new Error(\n '[ai-sdk-provider-env] Google provider requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n throw error\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * `@ai-sdk/openai-compatible` is a regular dependency and always available.\n */\nexport function createOpenAICompatibleProvider(opts: EnvProviderNamedFactoryOptions): ProviderV3Compatible {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n}\n","import type { PresetConfig } from './types'\n\n/**\n * Built-in preset configurations for common providers.\n *\n * When using a preset, only `{PREFIX}__PRESET` and `{PREFIX}__API_KEY`\n * are required; `BASE_URL` and `COMPATIBLE` are provided by the preset.\n */\nexport const builtinPresets: Record<string, PresetConfig> = {\n // OpenAI\n 'openai': {\n baseURL: 'https://api.openai.com/v1',\n compatible: 'openai',\n },\n\n // Anthropic\n 'anthropic': {\n baseURL: 'https://api.anthropic.com',\n compatible: 'anthropic',\n },\n\n // Google AI Studio (Gemini)\n 'google': {\n baseURL: 'https://generativelanguage.googleapis.com/v1beta',\n compatible: 'gemini',\n },\n\n // DeepSeek\n 'deepseek': {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI (GLM series)\n 'zhipu': {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // Groq\n 'groq': {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai-compatible',\n },\n\n // Together AI\n 'together': {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai-compatible',\n },\n\n // Fireworks AI\n 'fireworks': {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai-compatible',\n },\n\n // Mistral AI\n 'mistral': {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (international)\n 'moonshot': {\n baseURL: 'https://api.moonshot.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Moonshot AI (China mainland)\n 'moonshot-china': {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // Perplexity\n 'perplexity': {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai-compatible',\n },\n\n // OpenRouter\n 'openrouter': {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (international)\n 'siliconflow': {\n baseURL: 'https://api.siliconflow.com/v1',\n compatible: 'openai-compatible',\n },\n\n // SiliconFlow (China mainland)\n 'siliconflow-china': {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai-compatible',\n },\n\n // xAI (Grok series)\n 'xai': {\n baseURL: 'https://api.x.ai/v1',\n compatible: 'openai-compatible',\n },\n\n // Zhipu AI International (Z.AI — global endpoint for GLM series)\n 'zai': {\n baseURL: 'https://api.z.ai/api/paas/v4',\n compatible: 'openai-compatible',\n },\n\n // OpenCode Zen (curated multi-model AI gateway)\n 'opencode-zen': {\n baseURL: 'https://opencode.ai/zen/v1',\n compatible: 'openai-compatible',\n nativeRouting: true,\n },\n\n // OpenCode Go (low-cost subscription for open coding models)\n 'opencode-go': {\n baseURL: 'https://opencode.ai/zen/go/v1',\n compatible: 'openai-compatible',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { EnvProviderFactories, EnvProviderFactoryOptions, EnvProviderNamedFactoryOptions, EnvProviderOptions, ProviderV3Compatible } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createGeminiProvider, createOpenAICompatibleProvider, createOpenAIProvider, isModuleNotFoundError } from './factories'\nimport { builtinPresets } from './presets'\n\n/**\n * Thrown when a provider is not available (factory not provided by user).\n * Used internally to trigger fallback to OpenAI-compatible provider.\n */\nclass ProviderNotAvailableError extends Error {\n constructor(provider: string) {\n super(`Provider \"${provider}\" is not available`)\n this.name = 'ProviderNotAvailableError'\n }\n}\n\n/**\n * Interface for provider factory functions, used for dependency injection.\n *\n * In production, `defaultFactories` delegates to the real SDK implementations.\n * In tests, fake factories can be injected to avoid module mocking.\n *\n * Return types use `ProviderV3Compatible` so that both `ProviderV3`\n * (from `@ai-sdk/provider@3.x`) and `ProviderV4` (`@ai-sdk/provider@4.x`)\n * are accepted.\n */\nexport interface ProviderFactories {\n createOpenAI: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createAnthropic: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createGemini: (opts: EnvProviderFactoryOptions) => ProviderV3Compatible\n createOpenAICompatible: (opts: EnvProviderNamedFactoryOptions) => ProviderV3Compatible\n}\n\n/**\n * Default factories that delegate to the real implementations in `factories.ts`.\n */\nconst defaultFactories: ProviderFactories = {\n createOpenAI: createOpenAIProvider,\n createAnthropic: createAnthropicProvider,\n createGemini: createGeminiProvider,\n createOpenAICompatible: createOpenAICompatibleProvider,\n}\n\n/**\n * Detect the native compatible mode for a model based on its ID prefix.\n *\n * Used by nativeRouting to auto-route model families to their native provider SDKs.\n * Only `claude-*`, `gemini-*`, and `gpt-*` prefixes are matched.\n * Known limitation: `o1-*`, `o3-*`, `chatgpt-*` are NOT matched (use explicit compatible mode).\n *\n * @returns The detected compatible mode, or `undefined` if no match.\n */\nexport function detectNativeCompatible(model: string): 'openai' | 'anthropic' | 'gemini' | undefined {\n if (model.startsWith('claude-'))\n return 'anthropic'\n if (model.startsWith('gemini-'))\n return 'gemini'\n if (model.startsWith('gpt-'))\n return 'openai'\n return undefined\n}\n\n/**\n * Internally resolved configuration with all required fields determined.\n */\ninterface ResolvedConfig {\n baseURL: string\n apiKey: string\n compatible: string\n headers?: Record<string, string>\n nativeRouting?: boolean\n}\n\n/**\n * Testable core implementation that accepts injected provider factories.\n *\n * In tests, call this function directly with fake factories\n * to avoid module mocking entirely.\n */\nexport function createEnvProvider(\n factories: ProviderFactories,\n options: Omit<EnvProviderOptions, 'factories'> = {},\n): ProviderV3 {\n const separator = options.separator ?? '_'\n const defaultFetch = options.defaults?.fetch\n const defaultHeaders = options.defaults?.headers\n\n // Cache created providers to avoid redundant initialization\n // Stored as ProviderV3 via safe cast from ProviderV3Compatible,\n // since V3 and V4 provider interfaces are structurally identical.\n const cache = new Map<string, ProviderV3>()\n\n /**\n * Resolve baseURL and compatible from a preset name.\n */\n function resolvePreset(presetName: string): { baseURL: string, compatible: string, nativeRouting?: boolean } {\n const preset = builtinPresets[presetName]\n if (!preset) {\n const available = Object.keys(builtinPresets).join(', ')\n throw new Error(\n `[ai-sdk-provider-env] Unknown preset \"${presetName}\". Available presets: ${available}`,\n )\n }\n return {\n baseURL: preset.baseURL,\n compatible: preset.compatible ?? 'openai-compatible',\n nativeRouting: preset.nativeRouting,\n }\n }\n\n /**\n * Resolve config set configuration from explicit configs, presets, or environment variables.\n */\n function resolveConfig(configSet: string): ResolvedConfig {\n // Explicit configs take precedence over env vars\n if (options.configs?.[configSet]) {\n const config = options.configs[configSet]\n\n // Code-based configs also support presets\n if (config.preset) {\n const preset = resolvePreset(config.preset)\n return {\n baseURL: config.baseURL ?? preset.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? preset.compatible,\n nativeRouting: config.nativeRouting ?? preset.nativeRouting,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n if (!config.baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing baseURL in config for \"${configSet}\"`\n + ` (or set preset to use a built-in preset)`,\n )\n }\n\n return {\n baseURL: config.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? 'openai-compatible',\n nativeRouting: config.nativeRouting,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n // --- All checks below apply only to the env-var resolution path ---\n // The configs option (above) bypasses these and accepts arbitrary names/separators.\n\n // Validate separator produces shell-safe env var names (deferred to first env-var use)\n if (!/^\\w+$/.test(separator)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid separator \"${separator}\". `\n + `Separator must only contain ASCII letters, digits, or underscores to produce shell-safe env var names.`,\n )\n }\n\n // Validate config set name (ASCII shell-safe names only)\n if (!/^[A-Z_][\\w-]*$/i.test(configSet)) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid config set name \"${configSet}\". `\n + `Names must start with a letter or underscore, followed by letters, digits, underscores, or hyphens. `\n + `For arbitrary names, use the configs option instead.`,\n )\n }\n\n // Normalize hyphens to underscores for POSIX shell-safe env var names.\n // e.g. \"my-api\" → \"MY_API\", so env vars are MY_API_API_KEY, MY_API_BASE_URL, etc.\n const prefix = configSet.replace(/-/g, '_').toUpperCase()\n const env = (key: string): string | undefined => process.env[`${prefix}${separator}${key}`]\n\n const apiKey = env('API_KEY')\n if (!apiKey) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}API_KEY`,\n )\n }\n\n // Parse headers from env var (JSON format)\n const headersRaw = env('HEADERS')\n let headers: Record<string, string> | undefined\n if (headersRaw) {\n try {\n headers = JSON.parse(headersRaw)\n }\n catch {\n throw new Error(\n `[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`,\n )\n }\n }\n\n // Parse NATIVE_ROUTING env var (boolean: 'true'/'false', case-insensitive)\n const nativeRoutingRaw = env('NATIVE_ROUTING')\n let nativeRoutingFromEnv: boolean | undefined\n if (nativeRoutingRaw !== undefined) {\n const lower = nativeRoutingRaw.toLowerCase()\n if (lower === 'true') {\n nativeRoutingFromEnv = true\n }\n else if (lower === 'false') {\n nativeRoutingFromEnv = false\n }\n else {\n throw new Error(\n `[ai-sdk-provider-env] Invalid value for ${prefix}${separator}NATIVE_ROUTING: \"${nativeRoutingRaw}\". `\n + `Expected \"true\" or \"false\".`,\n )\n }\n }\n\n // Check for preset\n const presetName = env('PRESET')\n if (presetName) {\n const preset = resolvePreset(presetName)\n return {\n baseURL: env('BASE_URL') ?? preset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? preset.compatible,\n nativeRouting: nativeRoutingFromEnv ?? preset.nativeRouting,\n ...(headers && { headers }),\n }\n }\n\n // Without a preset, try BASE_URL first\n const baseURL = env('BASE_URL')\n if (baseURL) {\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai-compatible',\n nativeRouting: nativeRoutingFromEnv,\n ...(headers && { headers }),\n }\n }\n\n // Auto-detect: if configSet name matches a built-in preset, use it automatically\n if (options.presetAutoDetect !== false) {\n const autoPreset = builtinPresets[configSet.toLowerCase()]\n if (autoPreset) {\n return {\n baseURL: autoPreset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? autoPreset.compatible ?? 'openai-compatible',\n nativeRouting: nativeRoutingFromEnv ?? autoPreset.nativeRouting,\n ...(headers && { headers }),\n }\n }\n }\n\n // Error: neither BASE_URL nor a matching preset found\n const available = Object.keys(builtinPresets).join(', ')\n const presetHint = builtinPresets[configSet.toLowerCase()]\n ? ` (Note: \"${configSet}\" matches a built-in preset, but presetAutoDetect is disabled.)`\n : ''\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL`\n + ` (or set ${prefix}${separator}PRESET to use a preset.`\n + ` Available presets: ${available})${presetHint}`,\n )\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(configSet: string, config: ResolvedConfig): ProviderV3Compatible {\n const { baseURL, apiKey, compatible, headers } = config\n\n // Merge headers: defaults.headers as base, config-set headers override matching keys\n const mergedHeaders = (defaultHeaders || headers)\n ? { ...defaultHeaders, ...headers }\n : undefined\n\n const baseOpts = {\n baseURL,\n apiKey,\n ...(mergedHeaders && { headers: mergedHeaders }),\n ...(defaultFetch && { fetch: defaultFetch }),\n }\n\n switch (compatible) {\n case 'openai':\n try {\n return factories.createOpenAI(baseOpts)\n }\n catch (error) {\n // Fallback to OpenAI-compatible when:\n // - @ai-sdk/openai is not installed (module not found)\n // - User-provided factories don't include openai (ProviderNotAvailableError)\n if (isModuleNotFoundError(error, '@ai-sdk/openai') || error instanceof ProviderNotAvailableError) {\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (fallbackError) {\n // Only swallow module-not-found errors from the fallback itself\n if (isModuleNotFoundError(fallbackError, '@ai-sdk/openai-compatible') || fallbackError instanceof ProviderNotAvailableError) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai or its openai-compatible fallback. '\n + 'Install @ai-sdk/openai for full OpenAI features, or if using a bundler, '\n + 'provide factories: { openai: createOpenAI }',\n )\n }\n // Non-module-not-found errors from openai-compatible (e.g. config issues) should propagate\n throw fallbackError\n }\n }\n throw error\n }\n case 'anthropic':\n try {\n return factories.createAnthropic(baseOpts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/anthropic')) {\n throw new Error(\n '[ai-sdk-provider-env] Anthropic provider requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n throw error\n }\n case 'gemini':\n try {\n return factories.createGemini(baseOpts)\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/google')) {\n throw new Error(\n '[ai-sdk-provider-env] Google provider requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n throw error\n }\n case 'openai-compatible':\n try {\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\n }\n catch (error) {\n if (isModuleNotFoundError(error, '@ai-sdk/openai-compatible')) {\n throw new Error(\n '[ai-sdk-provider-env] Could not load @ai-sdk/openai-compatible. '\n + 'If using a bundler, provide factories: { openaiCompatible: createOpenAICompatible }',\n )\n }\n throw error\n }\n default:\n throw new Error(\n `[ai-sdk-provider-env] Unknown compatible mode \"${compatible}\".`\n + ` Supported values: \"openai\", \"anthropic\", \"gemini\", \"openai-compatible\".`\n + ` Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`,\n )\n }\n }\n\n /**\n * Compute a cache key for the given config set.\n *\n * Explicit configs use the raw (uppercased) name, so `foo-bar` and `foo_bar`\n * remain distinct when both are defined in `configs`.\n * Env-var-backed config sets normalize hyphens to underscores, so aliases\n * like `my-api` and `my_api` share one cached provider.\n */\n function getCacheKey(configSet: string, config: ResolvedConfig, effectiveCompatible: string): string {\n if (options.configs?.[configSet]) {\n if (config.nativeRouting) {\n return `config:${configSet.toUpperCase()}:${effectiveCompatible}`\n }\n return `config:${configSet.toUpperCase()}`\n }\n if (config.nativeRouting) {\n return `env:${configSet.replace(/-/g, '_').toUpperCase()}:${effectiveCompatible}`\n }\n return `env:${configSet.replace(/-/g, '_').toUpperCase()}`\n }\n\n /**\n * Get or create a cached provider for the given config set.\n *\n * Returns `ProviderV3` via a safe cast from `ProviderV3Compatible`.\n * This is safe because `ProviderV3` and `ProviderV4` have identical\n * method signatures — only `specificationVersion` and model type brands differ.\n */\n function getProvider(configSet: string, model?: string): ProviderV3 {\n const config = resolveConfig(configSet)\n\n const detectedCompatible = (config.nativeRouting && model)\n ? detectNativeCompatible(model)\n : undefined\n const effectiveCompatible = detectedCompatible ?? config.compatible\n const wasAutoRouted = detectedCompatible != null && detectedCompatible !== config.compatible\n\n const key = getCacheKey(configSet, config, effectiveCompatible)\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const configForProvider = wasAutoRouted\n ? { ...config, compatible: effectiveCompatible }\n : config\n\n try {\n // Safe cast: V3 and V4 providers are structurally identical.\n // The underlying provider may be ProviderV3 or ProviderV4 depending\n // on which SDK version the user has installed.\n const provider = createUnderlying(configSet, configForProvider) as unknown as ProviderV3\n cache.set(key, provider)\n return provider\n }\n catch (error) {\n // When nativeRouting auto-routed to a provider whose SDK is not installed,\n // append a hint so users know they can disable routing to fall back.\n // Only for module-not-found errors — other errors (config issues, SDK bugs)\n // should propagate without misleading nativeRouting context.\n if (wasAutoRouted && error instanceof Error && error.message.includes('[ai-sdk-provider-env]')) {\n const prefix = configSet.replace(/-/g, '_').toUpperCase()\n throw new Error(\n `${error.message}`\n + ` (nativeRouting auto-detected this model as ${effectiveCompatible}.`\n + ` Disable with ${prefix}${separator}NATIVE_ROUTING=false to use ${config.compatible} instead.)`,\n { cause: error },\n )\n }\n throw error\n }\n }\n\n /**\n * Parse a model ID. The first `/` separates the config set name from the actual model ID.\n */\n function parseModelId(modelId: string): { configSet: string, model: string } {\n const slashIndex = modelId.indexOf('/')\n if (slashIndex === -1) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid model ID \"${modelId}\". `\n + `Expected format: \"{configSet}/{modelId}\", e.g. \"zhipu/glm-4\"`,\n )\n }\n return {\n configSet: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n }\n\n return {\n specificationVersion: 'v3' as const,\n\n languageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet, model).languageModel(model)\n },\n\n embeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).embeddingModel(model)\n },\n\n imageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).imageModel(model)\n },\n\n textEmbeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n // Prefer textEmbeddingModel if the underlying provider has it (V3).\n if (provider.textEmbeddingModel) {\n return provider.textEmbeddingModel(model)\n }\n // V4 providers removed textEmbeddingModel — fall back to embeddingModel\n // which exists in both V3 and V4 interfaces.\n if (provider.embeddingModel) {\n return provider.embeddingModel(model)\n }\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n },\n\n transcriptionModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.transcriptionModel) {\n throw new NoSuchModelError({ modelId, modelType: 'transcriptionModel' })\n }\n return provider.transcriptionModel(model)\n },\n\n speechModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.speechModel) {\n throw new NoSuchModelError({ modelId, modelType: 'speechModel' })\n }\n return provider.speechModel(model)\n },\n\n rerankingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.rerankingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'rerankingModel' })\n }\n return provider.rerankingModel(model)\n },\n }\n}\n\n/**\n * Build internal `ProviderFactories` from user-provided `EnvProviderFactories`.\n *\n * Uses lazy-strict semantics: each factory slot is only evaluated when actually called.\n * If the user provided a factory for a given compatible mode, it is used;\n * otherwise, a clear error is thrown (no silent fallback to dynamic `require()`).\n */\nfunction buildUserFactories(userFactories: EnvProviderFactories): ProviderFactories {\n function missingFactory(key: string, fnName: string, pkg: string): never {\n throw new Error(\n `[ai-sdk-provider-env] No factory provided for \"${key}\". `\n + `When using the factories option, provide a factory for each compatibility mode you use. `\n + `Add: import { ${fnName} } from '${pkg}' and set factories: { ${key}: ${fnName} }`,\n )\n }\n\n return {\n createOpenAI: (opts) => {\n if (userFactories.openai)\n return userFactories.openai(opts)\n // Signal fallback — createUnderlying will catch this and use openai-compatible\n throw new ProviderNotAvailableError('openai')\n },\n createAnthropic: opts =>\n userFactories.anthropic\n ? userFactories.anthropic(opts)\n : missingFactory('anthropic', 'createAnthropic', '@ai-sdk/anthropic'),\n createGemini: opts =>\n userFactories.gemini\n ? userFactories.gemini(opts)\n : missingFactory('gemini', 'createGoogleGenerativeAI', '@ai-sdk/google'),\n createOpenAICompatible: opts =>\n userFactories.openaiCompatible\n ? userFactories.openaiCompatible(opts)\n : createOpenAICompatibleProvider(opts),\n }\n}\n\n/**\n * Create a dynamic, environment-variable-driven AI SDK provider.\n *\n * Automatically resolves provider configurations from env var naming conventions,\n * with built-in preset support for quick setup.\n *\n * Config set naming rules:\n * - Names must match `[A-Za-z_][A-Za-z0-9_-]*` (ASCII letters, digits, underscores, hyphens)\n * - Hyphens are normalized to underscores for env var lookup:\n * `my-api/model` → reads `MY_API_API_KEY`, `MY_API_BASE_URL`, etc.\n * - For arbitrary names, use the `configs` option instead\n *\n * Env var convention (using config set `ZHIPU` with default separator `_` as example):\n * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)\n * - `ZHIPU_BASE_URL` — API base URL\n * - `ZHIPU_API_KEY` — API key (required)\n * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)\n * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)\n *\n * @example\n * ```ts\n * import { createProviderRegistry } from 'ai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const registry = createProviderRegistry({\n * env: envProvider(),\n * })\n *\n * // Use a preset (only API_KEY is required)\n * // DEEPSEEK_PRESET=deepseek\n * // DEEPSEEK_API_KEY=sk-xxx\n * const model = registry.languageModel('env:deepseek/deepseek-chat')\n *\n * // Specify all parameters manually\n * // MYAPI_BASE_URL=https://api.example.com/v1\n * // MYAPI_API_KEY=xxx\n * const model2 = registry.languageModel('env:myapi/some-model')\n * ```\n *\n * @example Bundler-safe usage with explicit factories\n * ```ts\n * import { createOpenAI } from '@ai-sdk/openai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const provider = envProvider({\n * factories: { openai: createOpenAI },\n * })\n * ```\n */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n const factories = options.factories\n ? buildUserFactories(options.factories)\n : defaultFactories\n return createEnvProvider(factories, options)\n}\n"],"mappings":"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"}
|
package/llms.txt
CHANGED
|
@@ -38,6 +38,7 @@ Config set maps to UPPERCASED env var prefix. Hyphens normalize to underscores:
|
|
|
38
38
|
| `{PREFIX}_PRESET` | No | Built-in preset name |
|
|
39
39
|
| `{PREFIX}_COMPATIBLE` | No | `openai`·`anthropic`·`gemini`·`openai-compatible` (default) |
|
|
40
40
|
| `{PREFIX}_HEADERS` | No | Custom HTTP headers (JSON string) |
|
|
41
|
+
| `{PREFIX}_NATIVE_ROUTING` | No | Enable/disable native model routing (`true`/`false`) |
|
|
41
42
|
|
|
42
43
|
## Built-in Presets
|
|
43
44
|
|
|
@@ -74,6 +75,7 @@ interface ConfigSetEntry {
|
|
|
74
75
|
baseURL?: string // optional when preset is set
|
|
75
76
|
compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible'
|
|
76
77
|
headers?: Record<string, string>
|
|
78
|
+
nativeRouting?: boolean // enable native SDK routing per model prefix
|
|
77
79
|
}
|
|
78
80
|
```
|
|
79
81
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-sdk-provider-env",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "A dynamic, environment-variable-driven provider for Vercel AI SDK — resolves provider configurations from env var conventions at runtime",
|
|
6
6
|
"author": "maou-shonen",
|
|
7
7
|
"license": "MIT",
|
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
"@types/bun": "latest",
|
|
65
65
|
"ai": "^6.0.0 || ^7.0.0-beta.0",
|
|
66
66
|
"eslint": "^10.0.0",
|
|
67
|
+
"pkg-pr-new": "^0.0.66",
|
|
67
68
|
"tsdown": "^0.21.0-beta.2",
|
|
68
|
-
"typescript": "^
|
|
69
|
+
"typescript": "^6.0.0"
|
|
69
70
|
}
|
|
70
71
|
}
|