ai-sdk-provider-env 0.4.1 → 0.5.0

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