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 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
- name: configSet,
322
- ...baseOpts
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": return factories.createAnthropic(baseOpts);
331
- case "gemini": return factories.createGemini(baseOpts);
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
- name: configSet,
335
- ...baseOpts
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]) return `config:${configSet.toUpperCase()}`;
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 key = getCacheKey(configSet);
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 provider = createUnderlying(configSet, resolveConfig(configSet));
368
- cache.set(key, provider);
369
- return provider;
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);
@@ -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()`.
@@ -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;;;AAQZ;;EA1BE,MAAA;EA4BA;EA1BA,OAAA;EA2Ce;;;;;;;;EAlCf,UAAA;EA0DgB;;AASlB;;;;EA5DE,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA4DgB;EA1D/B,OAAA;EAmE8C;;;;AA0BhD;;;;EApFE,UAAA;AAAA;;;;;;UAQe,mBAAA;EAuGqE;;;;;;;;EA9FpF,KAAA,UAAe,UAAA,CAAW,KAAA;EAuF1B;;;;;;;;;;AAaF;;;EArFE,OAAA,GAAU,MAAA;AAAA;;;;;;;UASK,yBAAA;EAkHU;EAhHzB,OAAA;EAgIW;EA9HX,MAAA;EAqKA;EAnKA,OAAA,GAAU,MAAA;EAmKsB;EAjKhC,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;ACiX5B;;;;UDxWiB,8BAAA,SAAuC,yBAAA;ECwW5B;EDtW1B,IAAA;AAAA;;;;;AEhIF;;;;;;;;;;;;;;;;;UFwJiB,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;;;;;;;;;;;;;;AA7Nd;;;;;AAmBA;;;;;;;;;;;AAiCA;;;;;;;;;;;;;AAiBA;;;;;AA0BA;;;;iBC8UgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;ADhe/D;;;;cENa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
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()`.
@@ -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;;;AAQZ;;EA1BE,MAAA;EA4BA;EA1BA,OAAA;EA2Ce;;;;;;;;EAlCf,UAAA;EA0DgB;;AASlB;;;;EA5DE,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA4DgB;EA1D/B,OAAA;EAmE8C;;;;AA0BhD;;;;EApFE,UAAA;AAAA;;;;;;UAQe,mBAAA;EAuGqE;;;;;;;;EA9FpF,KAAA,UAAe,UAAA,CAAW,KAAA;EAuF1B;;;;;;;;;;AAaF;;;EArFE,OAAA,GAAU,MAAA;AAAA;;;;;;;UASK,yBAAA;EAkHU;EAhHzB,OAAA;EAgIW;EA9HX,MAAA;EAqKA;EAnKA,OAAA,GAAU,MAAA;EAmKsB;EAjKhC,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;ACiX5B;;;;UDxWiB,8BAAA,SAAuC,yBAAA;ECwW5B;EDtW1B,IAAA;AAAA;;;;;AEhIF;;;;;;;;;;;;;;;;;UFwJiB,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;;;;;;;;;;;;;;AA7Nd;;;;;AAmBA;;;;;;;;;;;AAiCA;;;;;;;;;;;;;AAiBA;;;;;AA0BA;;;;iBC8UgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;ADhe/D;;;;cENa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
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
- name: configSet,
298
- ...baseOpts
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": return factories.createAnthropic(baseOpts);
307
- case "gemini": return factories.createGemini(baseOpts);
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
- name: configSet,
311
- ...baseOpts
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]) return `config:${configSet.toUpperCase()}`;
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 key = getCacheKey(configSet);
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 provider = createUnderlying(configSet, resolveConfig(configSet));
344
- cache.set(key, provider);
345
- return provider;
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);
@@ -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.1",
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": "^5.7.0"
69
+ "typescript": "^6.0.0"
69
70
  }
70
71
  }