ai-sdk-provider-env 0.4.1 → 0.5.1
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 +79 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +29 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +79 -17
- package/dist/index.mjs.map +1 -1
- package/llms.txt +6 -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,8 @@ 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,
|
|
263
|
+
...config.providerOptions && { providerOptions: config.providerOptions },
|
|
246
264
|
...config.headers && { headers: config.headers }
|
|
247
265
|
};
|
|
248
266
|
}
|
|
@@ -251,6 +269,8 @@ function createEnvProvider(factories, options = {}) {
|
|
|
251
269
|
baseURL: config.baseURL,
|
|
252
270
|
apiKey: config.apiKey,
|
|
253
271
|
compatible: config.compatible ?? "openai-compatible",
|
|
272
|
+
nativeRouting: config.nativeRouting,
|
|
273
|
+
...config.providerOptions && { providerOptions: config.providerOptions },
|
|
254
274
|
...config.headers && { headers: config.headers }
|
|
255
275
|
};
|
|
256
276
|
}
|
|
@@ -267,6 +287,14 @@ function createEnvProvider(factories, options = {}) {
|
|
|
267
287
|
} catch {
|
|
268
288
|
throw new Error(`[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`);
|
|
269
289
|
}
|
|
290
|
+
const nativeRoutingRaw = env("NATIVE_ROUTING");
|
|
291
|
+
let nativeRoutingFromEnv;
|
|
292
|
+
if (nativeRoutingRaw !== void 0) {
|
|
293
|
+
const lower = nativeRoutingRaw.toLowerCase();
|
|
294
|
+
if (lower === "true") nativeRoutingFromEnv = true;
|
|
295
|
+
else if (lower === "false") nativeRoutingFromEnv = false;
|
|
296
|
+
else throw new Error(`[ai-sdk-provider-env] Invalid value for ${prefix}${separator}NATIVE_ROUTING: "${nativeRoutingRaw}". Expected "true" or "false".`);
|
|
297
|
+
}
|
|
270
298
|
const presetName = env("PRESET");
|
|
271
299
|
if (presetName) {
|
|
272
300
|
const preset = resolvePreset(presetName);
|
|
@@ -274,6 +302,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
274
302
|
baseURL: env("BASE_URL") ?? preset.baseURL,
|
|
275
303
|
apiKey,
|
|
276
304
|
compatible: env("COMPATIBLE") ?? preset.compatible,
|
|
305
|
+
nativeRouting: nativeRoutingFromEnv ?? preset.nativeRouting,
|
|
277
306
|
...headers && { headers }
|
|
278
307
|
};
|
|
279
308
|
}
|
|
@@ -282,6 +311,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
282
311
|
baseURL,
|
|
283
312
|
apiKey,
|
|
284
313
|
compatible: env("COMPATIBLE") ?? "openai-compatible",
|
|
314
|
+
nativeRouting: nativeRoutingFromEnv,
|
|
285
315
|
...headers && { headers }
|
|
286
316
|
};
|
|
287
317
|
if (options.presetAutoDetect !== false) {
|
|
@@ -290,6 +320,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
290
320
|
baseURL: autoPreset.baseURL,
|
|
291
321
|
apiKey,
|
|
292
322
|
compatible: env("COMPATIBLE") ?? autoPreset.compatible ?? "openai-compatible",
|
|
323
|
+
nativeRouting: nativeRoutingFromEnv ?? autoPreset.nativeRouting,
|
|
293
324
|
...headers && { headers }
|
|
294
325
|
};
|
|
295
326
|
}
|
|
@@ -301,12 +332,13 @@ function createEnvProvider(factories, options = {}) {
|
|
|
301
332
|
* Create the underlying provider based on the compatibility mode.
|
|
302
333
|
*/
|
|
303
334
|
function createUnderlying(configSet, config) {
|
|
304
|
-
const { baseURL, apiKey, compatible, headers } = config;
|
|
335
|
+
const { baseURL, apiKey, compatible, headers, providerOptions } = config;
|
|
305
336
|
const mergedHeaders = defaultHeaders || headers ? {
|
|
306
337
|
...defaultHeaders,
|
|
307
338
|
...headers
|
|
308
339
|
} : void 0;
|
|
309
340
|
const baseOpts = {
|
|
341
|
+
...providerOptions,
|
|
310
342
|
baseURL,
|
|
311
343
|
apiKey,
|
|
312
344
|
...mergedHeaders && { headers: mergedHeaders },
|
|
@@ -318,8 +350,8 @@ function createEnvProvider(factories, options = {}) {
|
|
|
318
350
|
} catch (error) {
|
|
319
351
|
if (isModuleNotFoundError(error, "@ai-sdk/openai") || error instanceof ProviderNotAvailableError) try {
|
|
320
352
|
return factories.createOpenAICompatible({
|
|
321
|
-
|
|
322
|
-
|
|
353
|
+
...baseOpts,
|
|
354
|
+
name: configSet
|
|
323
355
|
});
|
|
324
356
|
} catch (fallbackError) {
|
|
325
357
|
if (isModuleNotFoundError(fallbackError, "@ai-sdk/openai-compatible") || fallbackError instanceof ProviderNotAvailableError) throw new Error("[ai-sdk-provider-env] Could not load @ai-sdk/openai or its openai-compatible fallback. Install @ai-sdk/openai for full OpenAI features, or if using a bundler, provide factories: { openai: createOpenAI }");
|
|
@@ -327,12 +359,22 @@ function createEnvProvider(factories, options = {}) {
|
|
|
327
359
|
}
|
|
328
360
|
throw error;
|
|
329
361
|
}
|
|
330
|
-
case "anthropic":
|
|
331
|
-
|
|
362
|
+
case "anthropic": try {
|
|
363
|
+
return factories.createAnthropic(baseOpts);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
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");
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
case "gemini": try {
|
|
369
|
+
return factories.createGemini(baseOpts);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
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");
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
332
374
|
case "openai-compatible": try {
|
|
333
375
|
return factories.createOpenAICompatible({
|
|
334
|
-
|
|
335
|
-
|
|
376
|
+
...baseOpts,
|
|
377
|
+
name: configSet
|
|
336
378
|
});
|
|
337
379
|
} catch (error) {
|
|
338
380
|
if (isModuleNotFoundError(error, "@ai-sdk/openai-compatible")) throw new Error("[ai-sdk-provider-env] Could not load @ai-sdk/openai-compatible. If using a bundler, provide factories: { openaiCompatible: createOpenAICompatible }");
|
|
@@ -349,8 +391,12 @@ function createEnvProvider(factories, options = {}) {
|
|
|
349
391
|
* Env-var-backed config sets normalize hyphens to underscores, so aliases
|
|
350
392
|
* like `my-api` and `my_api` share one cached provider.
|
|
351
393
|
*/
|
|
352
|
-
function getCacheKey(configSet) {
|
|
353
|
-
if (options.configs?.[configSet])
|
|
394
|
+
function getCacheKey(configSet, config, effectiveCompatible) {
|
|
395
|
+
if (options.configs?.[configSet]) {
|
|
396
|
+
if (config.nativeRouting) return `config:${configSet.toUpperCase()}:${effectiveCompatible}`;
|
|
397
|
+
return `config:${configSet.toUpperCase()}`;
|
|
398
|
+
}
|
|
399
|
+
if (config.nativeRouting) return `env:${configSet.replace(/-/g, "_").toUpperCase()}:${effectiveCompatible}`;
|
|
354
400
|
return `env:${configSet.replace(/-/g, "_").toUpperCase()}`;
|
|
355
401
|
}
|
|
356
402
|
/**
|
|
@@ -360,13 +406,29 @@ function createEnvProvider(factories, options = {}) {
|
|
|
360
406
|
* This is safe because `ProviderV3` and `ProviderV4` have identical
|
|
361
407
|
* method signatures — only `specificationVersion` and model type brands differ.
|
|
362
408
|
*/
|
|
363
|
-
function getProvider(configSet) {
|
|
364
|
-
const
|
|
409
|
+
function getProvider(configSet, model) {
|
|
410
|
+
const config = resolveConfig(configSet);
|
|
411
|
+
const detectedCompatible = config.nativeRouting && model ? detectNativeCompatible(model) : void 0;
|
|
412
|
+
const effectiveCompatible = detectedCompatible ?? config.compatible;
|
|
413
|
+
const wasAutoRouted = detectedCompatible != null && detectedCompatible !== config.compatible;
|
|
414
|
+
const key = getCacheKey(configSet, config, effectiveCompatible);
|
|
365
415
|
const cached = cache.get(key);
|
|
366
416
|
if (cached) return cached;
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
417
|
+
const configForProvider = wasAutoRouted ? {
|
|
418
|
+
...config,
|
|
419
|
+
compatible: effectiveCompatible
|
|
420
|
+
} : config;
|
|
421
|
+
try {
|
|
422
|
+
const provider = createUnderlying(configSet, configForProvider);
|
|
423
|
+
cache.set(key, provider);
|
|
424
|
+
return provider;
|
|
425
|
+
} catch (error) {
|
|
426
|
+
if (wasAutoRouted && error instanceof Error && error.message.includes("[ai-sdk-provider-env]")) {
|
|
427
|
+
const prefix = configSet.replace(/-/g, "_").toUpperCase();
|
|
428
|
+
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 });
|
|
429
|
+
}
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
370
432
|
}
|
|
371
433
|
/**
|
|
372
434
|
* Parse a model ID. The first `/` separates the config set name from the actual model ID.
|
|
@@ -383,7 +445,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
383
445
|
specificationVersion: "v3",
|
|
384
446
|
languageModel(modelId) {
|
|
385
447
|
const { configSet, model } = parseModelId(modelId);
|
|
386
|
-
return getProvider(configSet).languageModel(model);
|
|
448
|
+
return getProvider(configSet, model).languageModel(model);
|
|
387
449
|
},
|
|
388
450
|
embeddingModel(modelId) {
|
|
389
451
|
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 providerOptions?: Record<string, unknown>\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.providerOptions && { providerOptions: config.providerOptions }),\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.providerOptions && { providerOptions: config.providerOptions }),\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, providerOptions } = 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 ...providerOptions,\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({ ...baseOpts, name: configSet })\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({ ...baseOpts, name: configSet })\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,27 @@ interface ConfigSetEntry {
|
|
|
58
58
|
* `{PREFIX}_HEADERS={"X-Custom-Header":"value"}`
|
|
59
59
|
*/
|
|
60
60
|
headers?: Record<string, string>;
|
|
61
|
+
/**
|
|
62
|
+
* Extra options passed through to the underlying provider SDK factory.
|
|
63
|
+
*
|
|
64
|
+
* These are spread directly into the factory call (`createOpenAI`, `createAnthropic`,
|
|
65
|
+
* `createOpenAICompatible`, etc.), allowing any SDK-specific option without
|
|
66
|
+
* `ai-sdk-provider-env` needing to know about it.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* providerOptions: { supportsStructuredOutputs: true }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
providerOptions?: Record<string, unknown>;
|
|
74
|
+
/**
|
|
75
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
76
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
77
|
+
* Non-matching models fall back to this config set's default `compatible` mode.
|
|
78
|
+
*
|
|
79
|
+
* Can also be set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
80
|
+
*/
|
|
81
|
+
nativeRouting?: boolean;
|
|
61
82
|
}
|
|
62
83
|
/**
|
|
63
84
|
* Preset provider configuration.
|
|
@@ -76,6 +97,14 @@ interface PresetConfig {
|
|
|
76
97
|
* - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
|
|
77
98
|
*/
|
|
78
99
|
compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
|
|
100
|
+
/**
|
|
101
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
102
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
103
|
+
* Non-matching models fall back to the preset's default `compatible` mode.
|
|
104
|
+
*
|
|
105
|
+
* Control per-config-set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
106
|
+
*/
|
|
107
|
+
nativeRouting?: boolean;
|
|
79
108
|
}
|
|
80
109
|
/**
|
|
81
110
|
* 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;;;;;EAlBV,MAAA;EA+Ce;EA7Cf,OAAA;;;;;;;;AAwEF;EA/DE,UAAA;;;;;;;EAOA,OAAA,GAAU,MAAA;EAgFM;;AASlB;;;;;;;;;;EA5EE,eAAA,GAAkB,MAAA;EAoFa;;AASjC;;;;;EArFE,aAAA;AAAA;;;;;;UAQe,YAAA;EA2HkC;EAzHjD,OAAA;EAgIgE;;;;;;;;EAvHhE,UAAA;EA0Ga;;;;;;;EAlGb,aAAA;AAAA;;;;AAqHF;;UA7GiB,mBAAA;EAmJU;;;;;;;;EA1IzB,KAAA,UAAe,UAAA,CAAW,KAAA;EA0ID;;;;;;;;;;;ACoV3B;;ED/cE,OAAA,GAAU,MAAA;AAAA;;;;;;;UASK,yBAAA;;EAEf,OAAA;EEzBD;EF2BC,MAAA;EE9I2B;EFgJ3B,OAAA,GAAU,MAAA;;EAEV,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;UASX,8BAAA,SAAuC,yBAAA;;EAEtD,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;;;;;;;iBC2ZgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD1kB/D;;;;cENa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -58,6 +58,27 @@ interface ConfigSetEntry {
|
|
|
58
58
|
* `{PREFIX}_HEADERS={"X-Custom-Header":"value"}`
|
|
59
59
|
*/
|
|
60
60
|
headers?: Record<string, string>;
|
|
61
|
+
/**
|
|
62
|
+
* Extra options passed through to the underlying provider SDK factory.
|
|
63
|
+
*
|
|
64
|
+
* These are spread directly into the factory call (`createOpenAI`, `createAnthropic`,
|
|
65
|
+
* `createOpenAICompatible`, etc.), allowing any SDK-specific option without
|
|
66
|
+
* `ai-sdk-provider-env` needing to know about it.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* providerOptions: { supportsStructuredOutputs: true }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
providerOptions?: Record<string, unknown>;
|
|
74
|
+
/**
|
|
75
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
76
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
77
|
+
* Non-matching models fall back to this config set's default `compatible` mode.
|
|
78
|
+
*
|
|
79
|
+
* Can also be set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
80
|
+
*/
|
|
81
|
+
nativeRouting?: boolean;
|
|
61
82
|
}
|
|
62
83
|
/**
|
|
63
84
|
* Preset provider configuration.
|
|
@@ -76,6 +97,14 @@ interface PresetConfig {
|
|
|
76
97
|
* - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
|
|
77
98
|
*/
|
|
78
99
|
compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
|
|
100
|
+
/**
|
|
101
|
+
* When enabled, auto-detects model family from model ID prefix and routes to the
|
|
102
|
+
* native provider SDK (`claude-*`→anthropic, `gemini-*`→gemini, `gpt-*`→openai).
|
|
103
|
+
* Non-matching models fall back to the preset's default `compatible` mode.
|
|
104
|
+
*
|
|
105
|
+
* Control per-config-set via `{PREFIX}_NATIVE_ROUTING=true|false` env var.
|
|
106
|
+
*/
|
|
107
|
+
nativeRouting?: boolean;
|
|
79
108
|
}
|
|
80
109
|
/**
|
|
81
110
|
* 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;;;;;EAlBV,MAAA;EA+Ce;EA7Cf,OAAA;;;;;;;;AAwEF;EA/DE,UAAA;;;;;;;EAOA,OAAA,GAAU,MAAA;EAgFM;;AASlB;;;;;;;;;;EA5EE,eAAA,GAAkB,MAAA;EAoFa;;AASjC;;;;;EArFE,aAAA;AAAA;;;;;;UAQe,YAAA;EA2HkC;EAzHjD,OAAA;EAgIgE;;;;;;;;EAvHhE,UAAA;EA0Ga;;;;;;;EAlGb,aAAA;AAAA;;;;AAqHF;;UA7GiB,mBAAA;EAmJU;;;;;;;;EA1IzB,KAAA,UAAe,UAAA,CAAW,KAAA;EA0ID;;;;;;;;;;;ACoV3B;;ED/cE,OAAA,GAAU,MAAA;AAAA;;;;;;;UASK,yBAAA;;EAEf,OAAA;EEzBD;EF2BC,MAAA;EE9I2B;EFgJ3B,OAAA,GAAU,MAAA;;EAEV,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;UASX,8BAAA,SAAuC,yBAAA;;EAEtD,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;;;;;;;iBC2ZgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD1kB/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,8 @@ 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,
|
|
239
|
+
...config.providerOptions && { providerOptions: config.providerOptions },
|
|
222
240
|
...config.headers && { headers: config.headers }
|
|
223
241
|
};
|
|
224
242
|
}
|
|
@@ -227,6 +245,8 @@ function createEnvProvider(factories, options = {}) {
|
|
|
227
245
|
baseURL: config.baseURL,
|
|
228
246
|
apiKey: config.apiKey,
|
|
229
247
|
compatible: config.compatible ?? "openai-compatible",
|
|
248
|
+
nativeRouting: config.nativeRouting,
|
|
249
|
+
...config.providerOptions && { providerOptions: config.providerOptions },
|
|
230
250
|
...config.headers && { headers: config.headers }
|
|
231
251
|
};
|
|
232
252
|
}
|
|
@@ -243,6 +263,14 @@ function createEnvProvider(factories, options = {}) {
|
|
|
243
263
|
} catch {
|
|
244
264
|
throw new Error(`[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`);
|
|
245
265
|
}
|
|
266
|
+
const nativeRoutingRaw = env("NATIVE_ROUTING");
|
|
267
|
+
let nativeRoutingFromEnv;
|
|
268
|
+
if (nativeRoutingRaw !== void 0) {
|
|
269
|
+
const lower = nativeRoutingRaw.toLowerCase();
|
|
270
|
+
if (lower === "true") nativeRoutingFromEnv = true;
|
|
271
|
+
else if (lower === "false") nativeRoutingFromEnv = false;
|
|
272
|
+
else throw new Error(`[ai-sdk-provider-env] Invalid value for ${prefix}${separator}NATIVE_ROUTING: "${nativeRoutingRaw}". Expected "true" or "false".`);
|
|
273
|
+
}
|
|
246
274
|
const presetName = env("PRESET");
|
|
247
275
|
if (presetName) {
|
|
248
276
|
const preset = resolvePreset(presetName);
|
|
@@ -250,6 +278,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
250
278
|
baseURL: env("BASE_URL") ?? preset.baseURL,
|
|
251
279
|
apiKey,
|
|
252
280
|
compatible: env("COMPATIBLE") ?? preset.compatible,
|
|
281
|
+
nativeRouting: nativeRoutingFromEnv ?? preset.nativeRouting,
|
|
253
282
|
...headers && { headers }
|
|
254
283
|
};
|
|
255
284
|
}
|
|
@@ -258,6 +287,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
258
287
|
baseURL,
|
|
259
288
|
apiKey,
|
|
260
289
|
compatible: env("COMPATIBLE") ?? "openai-compatible",
|
|
290
|
+
nativeRouting: nativeRoutingFromEnv,
|
|
261
291
|
...headers && { headers }
|
|
262
292
|
};
|
|
263
293
|
if (options.presetAutoDetect !== false) {
|
|
@@ -266,6 +296,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
266
296
|
baseURL: autoPreset.baseURL,
|
|
267
297
|
apiKey,
|
|
268
298
|
compatible: env("COMPATIBLE") ?? autoPreset.compatible ?? "openai-compatible",
|
|
299
|
+
nativeRouting: nativeRoutingFromEnv ?? autoPreset.nativeRouting,
|
|
269
300
|
...headers && { headers }
|
|
270
301
|
};
|
|
271
302
|
}
|
|
@@ -277,12 +308,13 @@ function createEnvProvider(factories, options = {}) {
|
|
|
277
308
|
* Create the underlying provider based on the compatibility mode.
|
|
278
309
|
*/
|
|
279
310
|
function createUnderlying(configSet, config) {
|
|
280
|
-
const { baseURL, apiKey, compatible, headers } = config;
|
|
311
|
+
const { baseURL, apiKey, compatible, headers, providerOptions } = config;
|
|
281
312
|
const mergedHeaders = defaultHeaders || headers ? {
|
|
282
313
|
...defaultHeaders,
|
|
283
314
|
...headers
|
|
284
315
|
} : void 0;
|
|
285
316
|
const baseOpts = {
|
|
317
|
+
...providerOptions,
|
|
286
318
|
baseURL,
|
|
287
319
|
apiKey,
|
|
288
320
|
...mergedHeaders && { headers: mergedHeaders },
|
|
@@ -294,8 +326,8 @@ function createEnvProvider(factories, options = {}) {
|
|
|
294
326
|
} catch (error) {
|
|
295
327
|
if (isModuleNotFoundError(error, "@ai-sdk/openai") || error instanceof ProviderNotAvailableError) try {
|
|
296
328
|
return factories.createOpenAICompatible({
|
|
297
|
-
|
|
298
|
-
|
|
329
|
+
...baseOpts,
|
|
330
|
+
name: configSet
|
|
299
331
|
});
|
|
300
332
|
} catch (fallbackError) {
|
|
301
333
|
if (isModuleNotFoundError(fallbackError, "@ai-sdk/openai-compatible") || fallbackError instanceof ProviderNotAvailableError) throw new Error("[ai-sdk-provider-env] Could not load @ai-sdk/openai or its openai-compatible fallback. Install @ai-sdk/openai for full OpenAI features, or if using a bundler, provide factories: { openai: createOpenAI }");
|
|
@@ -303,12 +335,22 @@ function createEnvProvider(factories, options = {}) {
|
|
|
303
335
|
}
|
|
304
336
|
throw error;
|
|
305
337
|
}
|
|
306
|
-
case "anthropic":
|
|
307
|
-
|
|
338
|
+
case "anthropic": try {
|
|
339
|
+
return factories.createAnthropic(baseOpts);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
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");
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
case "gemini": try {
|
|
345
|
+
return factories.createGemini(baseOpts);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
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");
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
308
350
|
case "openai-compatible": try {
|
|
309
351
|
return factories.createOpenAICompatible({
|
|
310
|
-
|
|
311
|
-
|
|
352
|
+
...baseOpts,
|
|
353
|
+
name: configSet
|
|
312
354
|
});
|
|
313
355
|
} catch (error) {
|
|
314
356
|
if (isModuleNotFoundError(error, "@ai-sdk/openai-compatible")) throw new Error("[ai-sdk-provider-env] Could not load @ai-sdk/openai-compatible. If using a bundler, provide factories: { openaiCompatible: createOpenAICompatible }");
|
|
@@ -325,8 +367,12 @@ function createEnvProvider(factories, options = {}) {
|
|
|
325
367
|
* Env-var-backed config sets normalize hyphens to underscores, so aliases
|
|
326
368
|
* like `my-api` and `my_api` share one cached provider.
|
|
327
369
|
*/
|
|
328
|
-
function getCacheKey(configSet) {
|
|
329
|
-
if (options.configs?.[configSet])
|
|
370
|
+
function getCacheKey(configSet, config, effectiveCompatible) {
|
|
371
|
+
if (options.configs?.[configSet]) {
|
|
372
|
+
if (config.nativeRouting) return `config:${configSet.toUpperCase()}:${effectiveCompatible}`;
|
|
373
|
+
return `config:${configSet.toUpperCase()}`;
|
|
374
|
+
}
|
|
375
|
+
if (config.nativeRouting) return `env:${configSet.replace(/-/g, "_").toUpperCase()}:${effectiveCompatible}`;
|
|
330
376
|
return `env:${configSet.replace(/-/g, "_").toUpperCase()}`;
|
|
331
377
|
}
|
|
332
378
|
/**
|
|
@@ -336,13 +382,29 @@ function createEnvProvider(factories, options = {}) {
|
|
|
336
382
|
* This is safe because `ProviderV3` and `ProviderV4` have identical
|
|
337
383
|
* method signatures — only `specificationVersion` and model type brands differ.
|
|
338
384
|
*/
|
|
339
|
-
function getProvider(configSet) {
|
|
340
|
-
const
|
|
385
|
+
function getProvider(configSet, model) {
|
|
386
|
+
const config = resolveConfig(configSet);
|
|
387
|
+
const detectedCompatible = config.nativeRouting && model ? detectNativeCompatible(model) : void 0;
|
|
388
|
+
const effectiveCompatible = detectedCompatible ?? config.compatible;
|
|
389
|
+
const wasAutoRouted = detectedCompatible != null && detectedCompatible !== config.compatible;
|
|
390
|
+
const key = getCacheKey(configSet, config, effectiveCompatible);
|
|
341
391
|
const cached = cache.get(key);
|
|
342
392
|
if (cached) return cached;
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
393
|
+
const configForProvider = wasAutoRouted ? {
|
|
394
|
+
...config,
|
|
395
|
+
compatible: effectiveCompatible
|
|
396
|
+
} : config;
|
|
397
|
+
try {
|
|
398
|
+
const provider = createUnderlying(configSet, configForProvider);
|
|
399
|
+
cache.set(key, provider);
|
|
400
|
+
return provider;
|
|
401
|
+
} catch (error) {
|
|
402
|
+
if (wasAutoRouted && error instanceof Error && error.message.includes("[ai-sdk-provider-env]")) {
|
|
403
|
+
const prefix = configSet.replace(/-/g, "_").toUpperCase();
|
|
404
|
+
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 });
|
|
405
|
+
}
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
346
408
|
}
|
|
347
409
|
/**
|
|
348
410
|
* Parse a model ID. The first `/` separates the config set name from the actual model ID.
|
|
@@ -359,7 +421,7 @@ function createEnvProvider(factories, options = {}) {
|
|
|
359
421
|
specificationVersion: "v3",
|
|
360
422
|
languageModel(modelId) {
|
|
361
423
|
const { configSet, model } = parseModelId(modelId);
|
|
362
|
-
return getProvider(configSet).languageModel(model);
|
|
424
|
+
return getProvider(configSet, model).languageModel(model);
|
|
363
425
|
},
|
|
364
426
|
embeddingModel(modelId) {
|
|
365
427
|
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 providerOptions?: Record<string, unknown>\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.providerOptions && { providerOptions: config.providerOptions }),\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.providerOptions && { providerOptions: config.providerOptions }),\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, providerOptions } = 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 ...providerOptions,\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({ ...baseOpts, name: configSet })\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({ ...baseOpts, name: configSet })\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,8 @@ 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
|
+
providerOptions?: Record<string, unknown> // extra options passed through to the underlying SDK factory
|
|
79
|
+
nativeRouting?: boolean // enable native SDK routing per model prefix
|
|
77
80
|
}
|
|
78
81
|
```
|
|
79
82
|
|
|
@@ -132,11 +135,14 @@ const provider = envProvider({
|
|
|
132
135
|
apiKey: 'key',
|
|
133
136
|
baseURL: 'https://custom.api.com/v1',
|
|
134
137
|
compatible: 'openai-compatible',
|
|
138
|
+
providerOptions: { supportsStructuredOutputs: true },
|
|
135
139
|
},
|
|
136
140
|
},
|
|
137
141
|
})
|
|
138
142
|
```
|
|
139
143
|
|
|
144
|
+
Use `providerOptions` to pass any SDK-specific option (e.g. `supportsStructuredOutputs`) directly to the underlying provider factory.
|
|
145
|
+
|
|
140
146
|
### Provider registry
|
|
141
147
|
```ts
|
|
142
148
|
import { createProviderRegistry } from 'ai'
|
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.1",
|
|
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
|
}
|