ai-sdk-provider-env 0.1.0 → 0.2.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
@@ -17,7 +17,7 @@ Using multiple AI providers with Vercel AI SDK means importing each SDK, configu
17
17
 
18
18
  - Resolve provider config (base URL, API key, compatibility mode) from environment variables automatically
19
19
  - Built-in presets for popular providers, so you only need to set an API key
20
- - Supports OpenAI, Anthropic, and any OpenAI-compatible API
20
+ - Supports OpenAI, Anthropic, Google Gemini, and any OpenAI-compatible API
21
21
  - Implements `ProviderV3`, plugs directly into `createProviderRegistry`
22
22
  - Provider instances are cached, no redundant initialization
23
23
  - Fully customizable: custom fetch, env-based headers, custom separator, code-based configs
@@ -31,8 +31,9 @@ pnpm add ai-sdk-provider-env
31
31
  Install provider SDKs as needed:
32
32
 
33
33
  ```bash
34
- pnpm add @ai-sdk/openai # for OpenAI and OpenAI-compatible
34
+ pnpm add @ai-sdk/openai # for OpenAI
35
35
  pnpm add @ai-sdk/anthropic # for Anthropic
36
+ pnpm add @ai-sdk/google # for Google AI Studio (Gemini)
36
37
  pnpm add @ai-sdk/openai-compatible # for generic OpenAI-compatible APIs
37
38
  ```
38
39
 
@@ -47,8 +48,7 @@ const registry = createProviderRegistry({
47
48
  })
48
49
 
49
50
  // Use a preset: only API_KEY is required
50
- // OPENAI_PRESET=openai
51
- // OPENAI_API_KEY=sk-xxx
51
+ // OPENAI_API_KEY=sk-xxx (OPENAI_PRESET=openai is optional — auto-detected)
52
52
  const model = registry.languageModel('env:openai/gpt-4o')
53
53
 
54
54
  const { text } = await generateText({ model, prompt: 'Hello!' })
@@ -86,9 +86,9 @@ With the default separator `_`, a config set reads these variables (`[MYAI]` = y
86
86
  | Variable | Required | Description |
87
87
  |---|---|---|
88
88
  | `[MYAI]_API_KEY` | Yes | API key |
89
- | `[MYAI]_BASE_URL` | Yes (unless preset is set) | API base URL |
89
+ | `[MYAI]_BASE_URL` | Yes (unless preset is set or auto-detected) | API base URL |
90
90
  | `[MYAI]_PRESET` | No | Built-in preset name (e.g. `openai`) |
91
- | `[MYAI]_COMPATIBLE` | No | Compatibility mode (default: `openai`) |
91
+ | `[MYAI]_COMPATIBLE` | No | Compatibility mode (default: `openai-compatible`) |
92
92
  | `[MYAI]_HEADERS` | No | Custom HTTP headers (JSON format) |
93
93
 
94
94
  When `PRESET` is set, `BASE_URL` and `COMPATIBLE` become optional and fall back to the preset's values.
@@ -97,9 +97,10 @@ When `PRESET` is set, `BASE_URL` and `COMPATIBLE` become optional and fall back
97
97
 
98
98
  | Value | Behavior |
99
99
  |---|---|
100
- | `openai` | Uses `@ai-sdk/openai` (default) |
100
+ | `openai` | Uses `@ai-sdk/openai` |
101
101
  | `anthropic` | Uses `@ai-sdk/anthropic` |
102
- | any other string | Uses `@ai-sdk/openai-compatible` with that string as the provider name |
102
+ | `gemini` | Uses `@ai-sdk/google` |
103
+ | `openai-compatible` | Uses `@ai-sdk/openai-compatible` with the config set name as the provider name (default) |
103
104
 
104
105
  ## Built-in Presets
105
106
 
@@ -107,16 +108,39 @@ When `PRESET` is set, `BASE_URL` and `COMPATIBLE` become optional and fall back
107
108
  |---|---|---|
108
109
  | `openai` | `https://api.openai.com/v1` | `openai` |
109
110
  | `anthropic` | `https://api.anthropic.com` | `anthropic` |
110
- | `deepseek` | `https://api.deepseek.com` | `openai` |
111
- | `zhipu` | `https://open.bigmodel.cn/api/paas/v4` | `openai` |
112
- | `groq` | `https://api.groq.com/openai/v1` | `openai` |
113
- | `together` | `https://api.together.xyz/v1` | `openai` |
114
- | `fireworks` | `https://api.fireworks.ai/inference/v1` | `openai` |
115
- | `mistral` | `https://api.mistral.ai/v1` | `openai` |
116
- | `moonshot` | `https://api.moonshot.cn/v1` | `openai` |
117
- | `perplexity` | `https://api.perplexity.ai` | `openai` |
118
- | `openrouter` | `https://openrouter.ai/api/v1` | `openai` |
119
- | `siliconflow` | `https://api.siliconflow.cn/v1` | `openai` |
111
+ | `google` | `https://generativelanguage.googleapis.com/v1beta` | `gemini` |
112
+ | `deepseek` | `https://api.deepseek.com` | `openai-compatible` |
113
+ | `zhipu` | `https://open.bigmodel.cn/api/paas/v4` | `openai-compatible` |
114
+ | `groq` | `https://api.groq.com/openai/v1` | `openai-compatible` |
115
+ | `together` | `https://api.together.xyz/v1` | `openai-compatible` |
116
+ | `fireworks` | `https://api.fireworks.ai/inference/v1` | `openai-compatible` |
117
+ | `mistral` | `https://api.mistral.ai/v1` | `openai-compatible` |
118
+ | `moonshot` | `https://api.moonshot.cn/v1` | `openai-compatible` |
119
+ | `perplexity` | `https://api.perplexity.ai` | `openai-compatible` |
120
+ | `openrouter` | `https://openrouter.ai/api/v1` | `openai-compatible` |
121
+ | `siliconflow` | `https://api.siliconflow.cn/v1` | `openai-compatible` |
122
+
123
+ ## Preset Auto-Detect
124
+
125
+ `presetAutoDetect` is enabled by default. When the config set name exactly matches a built-in preset name, the preset is applied automatically — no `_PRESET` env var needed. Only an API key is required:
126
+
127
+ ```bash
128
+ # OPENROUTER_API_KEY is all you need
129
+ OPENROUTER_API_KEY=sk-or-xxx
130
+ ```
131
+
132
+ ```ts
133
+ const provider = envProvider()
134
+
135
+ // Works — openrouter preset auto-detected from config set name
136
+ const model = provider.languageModel('openrouter/some-model')
137
+ ```
138
+
139
+ Explicit `_PRESET` and `_BASE_URL` env vars always take precedence over auto-detect. To disable this behavior:
140
+
141
+ ```ts
142
+ envProvider({ presetAutoDetect: false })
143
+ ```
120
144
 
121
145
  ## API Reference
122
146
 
@@ -137,6 +161,7 @@ const provider = envProvider(options)
137
161
  | `separator` | `string` | `'_'` | Separator between the prefix and the variable name |
138
162
  | `configs` | `Record<string, ConfigSetEntry>` | `undefined` | Explicit config sets (takes precedence over env vars) |
139
163
  | `defaults` | `EnvProviderDefaults` | `undefined` | Global defaults applied to all providers (can be overridden per config set) |
164
+ | `presetAutoDetect` | `boolean` | `true` | Auto-apply a built-in preset when the config set name matches. Set to `false` to require explicit `_PRESET` configuration. |
140
165
 
141
166
  **`EnvProviderDefaults`:**
142
167
 
@@ -152,7 +177,7 @@ interface ConfigSetEntry {
152
177
  apiKey: string
153
178
  preset?: string
154
179
  baseURL?: string
155
- compatible?: string // 'openai' | 'anthropic' | any string (default: 'openai')
180
+ compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible' // default: 'openai-compatible'
156
181
  headers?: Record<string, string>
157
182
  }
158
183
  ```
package/dist/index.cjs CHANGED
@@ -58,6 +58,19 @@ function createAnthropicProvider(opts) {
58
58
  }
59
59
  }
60
60
  /**
61
+ * Create a Google Generative AI (Gemini) provider.
62
+ *
63
+ * Dynamically requires `@ai-sdk/google`, so it only needs to be installed when actually used.
64
+ */
65
+ function createGeminiProvider(opts) {
66
+ try {
67
+ const { createGoogleGenerativeAI } = require("@ai-sdk/google");
68
+ return createGoogleGenerativeAI(opts);
69
+ } catch {
70
+ throw new Error("[ai-sdk-provider-env] gemini compatibility mode requires @ai-sdk/google. Run: npm install @ai-sdk/google");
71
+ }
72
+ }
73
+ /**
61
74
  * Create an OpenAI Compatible provider.
62
75
  *
63
76
  * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.
@@ -88,45 +101,49 @@ const builtinPresets = {
88
101
  baseURL: "https://api.anthropic.com",
89
102
  compatible: "anthropic"
90
103
  },
104
+ google: {
105
+ baseURL: "https://generativelanguage.googleapis.com/v1beta",
106
+ compatible: "gemini"
107
+ },
91
108
  deepseek: {
92
109
  baseURL: "https://api.deepseek.com",
93
- compatible: "openai"
110
+ compatible: "openai-compatible"
94
111
  },
95
112
  zhipu: {
96
113
  baseURL: "https://open.bigmodel.cn/api/paas/v4",
97
- compatible: "openai"
114
+ compatible: "openai-compatible"
98
115
  },
99
116
  groq: {
100
117
  baseURL: "https://api.groq.com/openai/v1",
101
- compatible: "openai"
118
+ compatible: "openai-compatible"
102
119
  },
103
120
  together: {
104
121
  baseURL: "https://api.together.xyz/v1",
105
- compatible: "openai"
122
+ compatible: "openai-compatible"
106
123
  },
107
124
  fireworks: {
108
125
  baseURL: "https://api.fireworks.ai/inference/v1",
109
- compatible: "openai"
126
+ compatible: "openai-compatible"
110
127
  },
111
128
  mistral: {
112
129
  baseURL: "https://api.mistral.ai/v1",
113
- compatible: "openai"
130
+ compatible: "openai-compatible"
114
131
  },
115
132
  moonshot: {
116
133
  baseURL: "https://api.moonshot.cn/v1",
117
- compatible: "openai"
134
+ compatible: "openai-compatible"
118
135
  },
119
136
  perplexity: {
120
137
  baseURL: "https://api.perplexity.ai",
121
- compatible: "openai"
138
+ compatible: "openai-compatible"
122
139
  },
123
140
  openrouter: {
124
141
  baseURL: "https://openrouter.ai/api/v1",
125
- compatible: "openai"
142
+ compatible: "openai-compatible"
126
143
  },
127
144
  siliconflow: {
128
145
  baseURL: "https://api.siliconflow.cn/v1",
129
- compatible: "openai"
146
+ compatible: "openai-compatible"
130
147
  }
131
148
  };
132
149
 
@@ -138,6 +155,7 @@ const builtinPresets = {
138
155
  const defaultFactories = {
139
156
  createOpenAI: createOpenAIProvider,
140
157
  createAnthropic: createAnthropicProvider,
158
+ createGemini: createGeminiProvider,
141
159
  createOpenAICompatible: createOpenAICompatibleProvider
142
160
  };
143
161
  /**
@@ -162,7 +180,7 @@ function createEnvProvider(factories, options = {}) {
162
180
  }
163
181
  return {
164
182
  baseURL: preset.baseURL,
165
- compatible: preset.compatible ?? "openai"
183
+ compatible: preset.compatible ?? "openai-compatible"
166
184
  };
167
185
  }
168
186
  /**
@@ -184,7 +202,7 @@ function createEnvProvider(factories, options = {}) {
184
202
  return {
185
203
  baseURL: config.baseURL,
186
204
  apiKey: config.apiKey,
187
- compatible: config.compatible ?? "openai",
205
+ compatible: config.compatible ?? "openai-compatible",
188
206
  ...config.headers && { headers: config.headers }
189
207
  };
190
208
  }
@@ -210,18 +228,29 @@ function createEnvProvider(factories, options = {}) {
210
228
  };
211
229
  }
212
230
  const baseURL = env("BASE_URL");
213
- if (!baseURL) throw new Error(`[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL (or set ${prefix}${separator}PRESET to use a preset)`);
214
- return {
231
+ if (baseURL) return {
215
232
  baseURL,
216
233
  apiKey,
217
- compatible: env("COMPATIBLE") ?? "openai",
234
+ compatible: env("COMPATIBLE") ?? "openai-compatible",
218
235
  ...headers && { headers }
219
236
  };
237
+ if (options.presetAutoDetect !== false) {
238
+ const autoPreset = builtinPresets[configSet.toLowerCase()];
239
+ if (autoPreset) return {
240
+ baseURL: autoPreset.baseURL,
241
+ apiKey,
242
+ compatible: env("COMPATIBLE") ?? autoPreset.compatible ?? "openai-compatible",
243
+ ...headers && { headers }
244
+ };
245
+ }
246
+ const available = Object.keys(builtinPresets).join(", ");
247
+ const presetHint = builtinPresets[configSet.toLowerCase()] ? ` (Note: "${configSet}" matches a built-in preset, but presetAutoDetect is disabled.)` : "";
248
+ throw new Error(`[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL (or set ${prefix}${separator}PRESET to use a preset. Available presets: ${available})${presetHint}`);
220
249
  }
221
250
  /**
222
251
  * Create the underlying provider based on the compatibility mode.
223
252
  */
224
- function createUnderlying(config) {
253
+ function createUnderlying(configSet, config) {
225
254
  const { baseURL, apiKey, compatible, headers } = config;
226
255
  const mergedHeaders = defaultHeaders || headers ? {
227
256
  ...defaultHeaders,
@@ -236,10 +265,12 @@ function createEnvProvider(factories, options = {}) {
236
265
  switch (compatible) {
237
266
  case "openai": return factories.createOpenAI(baseOpts);
238
267
  case "anthropic": return factories.createAnthropic(baseOpts);
239
- default: return factories.createOpenAICompatible({
240
- name: compatible,
268
+ case "gemini": return factories.createGemini(baseOpts);
269
+ case "openai-compatible": return factories.createOpenAICompatible({
270
+ name: configSet,
241
271
  ...baseOpts
242
272
  });
273
+ default: throw new Error(`[ai-sdk-provider-env] Unknown compatible mode "${compatible}". Supported values: "openai", "anthropic", "gemini", "openai-compatible". Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`);
243
274
  }
244
275
  }
245
276
  /**
@@ -249,7 +280,7 @@ function createEnvProvider(factories, options = {}) {
249
280
  const key = configSet.toUpperCase();
250
281
  const cached = cache.get(key);
251
282
  if (cached) return cached;
252
- const provider = createUnderlying(resolveConfig(configSet));
283
+ const provider = createUnderlying(configSet, resolveConfig(configSet));
253
284
  cache.set(key, provider);
254
285
  return provider;
255
286
  }
@@ -326,7 +357,7 @@ function createEnvProvider(factories, options = {}) {
326
357
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
327
358
  * - `ZHIPU_BASE_URL` — API base URL
328
359
  * - `ZHIPU_API_KEY` — API key (required)
329
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
360
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
330
361
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
331
362
  *
332
363
  * @example
@@ -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 { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\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 */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\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 */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\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 {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.\n */\nexport function createOpenAICompatibleProvider(opts: ProviderOpts & { name: string }): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai-compatible mode requires @ai-sdk/openai-compatible. '\n + 'Run: npm install @ai-sdk/openai-compatible',\n )\n }\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 // DeepSeek\n deepseek: {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai',\n },\n\n // Zhipu AI (GLM series)\n zhipu: {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai',\n },\n\n // Groq\n groq: {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai',\n },\n\n // Together AI\n together: {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai',\n },\n\n // Fireworks AI\n fireworks: {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai',\n },\n\n // Mistral AI\n mistral: {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai',\n },\n\n // Moonshot AI\n moonshot: {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai',\n },\n\n // Perplexity\n perplexity: {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai',\n },\n\n // OpenRouter\n openrouter: {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai',\n },\n\n // SiliconFlow\n siliconflow: {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { ProviderOpts } from './factories'\nimport type { EnvProviderOptions } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createOpenAICompatibleProvider, createOpenAIProvider } from './factories'\nimport { builtinPresets } from './presets'\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 */\nexport interface ProviderFactories {\n createOpenAI: (opts: ProviderOpts) => ProviderV3\n createAnthropic: (opts: ProviderOpts) => ProviderV3\n createOpenAICompatible: (opts: ProviderOpts & { name: string }) => ProviderV3\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 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: EnvProviderOptions = {},\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 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',\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',\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n const prefix = configSet.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, BASE_URL is required\n const baseURL = env('BASE_URL')\n if (!baseURL) {\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 )\n }\n\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai',\n ...(headers && { headers }),\n }\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(config: ResolvedConfig): ProviderV3 {\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 return factories.createOpenAI(baseOpts)\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n default:\n return factories.createOpenAICompatible({ name: compatible, ...baseOpts })\n }\n }\n\n /**\n * Get or create a cached provider for the given config set.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = configSet.toUpperCase()\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n const provider = createUnderlying(config)\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 if (!provider.textEmbeddingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n }\n return provider.textEmbeddingModel(model)\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 * 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 * 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'`)\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 */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n return createEnvProvider(defaultFactories, 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 { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\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 */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\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 */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\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 {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\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 */\nexport function createGeminiProvider(opts: ProviderOpts): ProviderV3 {\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 {\n throw new Error(\n '[ai-sdk-provider-env] gemini compatibility mode requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.\n */\nexport function createOpenAICompatibleProvider(opts: ProviderOpts & { name: string }): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai-compatible mode requires @ai-sdk/openai-compatible. '\n + 'Run: npm install @ai-sdk/openai-compatible',\n )\n }\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\n moonshot: {\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\n siliconflow: {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai-compatible',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { ProviderOpts } from './factories'\nimport type { EnvProviderOptions } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createGeminiProvider, createOpenAICompatibleProvider, createOpenAIProvider } from './factories'\nimport { builtinPresets } from './presets'\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 */\nexport interface ProviderFactories {\n createOpenAI: (opts: ProviderOpts) => ProviderV3\n createAnthropic: (opts: ProviderOpts) => ProviderV3\n createGemini: (opts: ProviderOpts) => ProviderV3\n createOpenAICompatible: (opts: ProviderOpts & { name: string }) => ProviderV3\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: EnvProviderOptions = {},\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 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 const prefix = configSet.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): ProviderV3 {\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 return factories.createOpenAI(baseOpts)\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n case 'gemini':\n return factories.createGemini(baseOpts)\n case 'openai-compatible':\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\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 * Get or create a cached provider for the given config set.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = configSet.toUpperCase()\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n const provider = createUnderlying(configSet, config)\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 if (!provider.textEmbeddingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n }\n return provider.textEmbeddingModel(model)\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 * 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 * 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 */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n return createEnvProvider(defaultFactories, options)\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -21,11 +21,12 @@ interface ConfigSetEntry {
21
21
  /**
22
22
  * Compatibility mode
23
23
  *
24
- * - `'openai'` — uses `createOpenAI` (default)
24
+ * - `'openai'` — uses `createOpenAI`
25
25
  * - `'anthropic'` — uses `createAnthropic`
26
- * - any other string — uses `createOpenAICompatible` with that string as the provider name
26
+ * - `'gemini'` — uses `createGoogleGenerativeAI`
27
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
27
28
  */
28
- compatible?: string;
29
+ compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
29
30
  /**
30
31
  * Custom HTTP headers appended to API requests.
31
32
  *
@@ -43,9 +44,14 @@ interface PresetConfig {
43
44
  /** API base URL */
44
45
  baseURL: string;
45
46
  /**
46
- * Compatibility mode (defaults to `'openai'`)
47
+ * Compatibility mode
48
+ *
49
+ * - `'openai'` — uses `createOpenAI`
50
+ * - `'anthropic'` — uses `createAnthropic`
51
+ * - `'gemini'` — uses `createGoogleGenerativeAI`
52
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
47
53
  */
48
- compatible?: string;
54
+ compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
49
55
  }
50
56
  /**
51
57
  * Global defaults for `envProvider()`.
@@ -129,6 +135,25 @@ interface EnvProviderOptions {
129
135
  * })
130
136
  */
131
137
  defaults?: EnvProviderDefaults;
138
+ /**
139
+ * Automatically use a built-in preset when the config set name matches a preset name.
140
+ *
141
+ * When enabled (default), if a config set name matches a built-in preset name (e.g., `openai`, `anthropic`, `deepseek`),
142
+ * that preset is automatically applied without requiring the `{PREFIX}_PRESET` environment variable.
143
+ * Set to `false` to disable this behavior and require explicit preset configuration.
144
+ *
145
+ * @example
146
+ * // With presetAutoDetect enabled (default):
147
+ * // OPENAI_API_KEY=sk-xxx
148
+ * // → automatically uses the 'openai' preset
149
+ *
150
+ * @example
151
+ * // With presetAutoDetect disabled:
152
+ * // OPENAI_API_KEY=sk-xxx
153
+ * // OPENAI_PRESET=openai (required)
154
+ * envProvider({ presetAutoDetect: false })
155
+ */
156
+ presetAutoDetect?: boolean;
132
157
  }
133
158
  //#endregion
134
159
  //#region src/env-provider.d.ts
@@ -142,7 +167,7 @@ interface EnvProviderOptions {
142
167
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
143
168
  * - `ZHIPU_BASE_URL` — API base URL
144
169
  * - `ZHIPU_API_KEY` — API key (required)
145
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
170
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
146
171
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
147
172
  *
148
173
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EAyBe;EAvBf,OAAA;;;;AAqCF;;;;EA7BE,UAAA;EAsCe;;;;;;EA/Bf,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA0Df;EAxDA,OAAA;EA4EU;;;EAxEV,UAAA;AAAA;;;;;ACwQF;UDhQiB,mBAAA;;;;;;;;;EASf,KAAA,UAAe,UAAA,CAAW,KAAA;;AEtD5B;;;;;;;;;;;;EFqEE,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;AAlDb;;;;;;;;;;;;;;;;;;;ACkOA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD/S/D;;;;cEEa,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":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EA0Be;EAxBf,OAAA;;;;AA2CF;;;;;EAlCE,UAAA;EA2C0B;;;;;AAqB5B;EAzDE,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EAmFf;EAjFA,OAAA;EAiFyB;;;;;;;;EAxEzB,UAAA;AAAA;;;;;;UAQe,mBAAA;ECuRwD;;;;;AC1UzE;;;EF4DE,KAAA,UAAe,UAAA,CAAW,KAAA;EE5D4B;;;;;;;;;;;;;EF2EtD,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;;;;;;;;;;;;;;;;;;;EAoBX,gBAAA;AAAA;;;;;;;;;;AAtEF;;;;;;;;;;;;;;;;;;;;ACyPA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD5U/D;;;;cEEa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
package/dist/index.d.mts CHANGED
@@ -21,11 +21,12 @@ interface ConfigSetEntry {
21
21
  /**
22
22
  * Compatibility mode
23
23
  *
24
- * - `'openai'` — uses `createOpenAI` (default)
24
+ * - `'openai'` — uses `createOpenAI`
25
25
  * - `'anthropic'` — uses `createAnthropic`
26
- * - any other string — uses `createOpenAICompatible` with that string as the provider name
26
+ * - `'gemini'` — uses `createGoogleGenerativeAI`
27
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
27
28
  */
28
- compatible?: string;
29
+ compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
29
30
  /**
30
31
  * Custom HTTP headers appended to API requests.
31
32
  *
@@ -43,9 +44,14 @@ interface PresetConfig {
43
44
  /** API base URL */
44
45
  baseURL: string;
45
46
  /**
46
- * Compatibility mode (defaults to `'openai'`)
47
+ * Compatibility mode
48
+ *
49
+ * - `'openai'` — uses `createOpenAI`
50
+ * - `'anthropic'` — uses `createAnthropic`
51
+ * - `'gemini'` — uses `createGoogleGenerativeAI`
52
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
47
53
  */
48
- compatible?: string;
54
+ compatible?: 'openai' | 'anthropic' | 'gemini' | 'openai-compatible';
49
55
  }
50
56
  /**
51
57
  * Global defaults for `envProvider()`.
@@ -129,6 +135,25 @@ interface EnvProviderOptions {
129
135
  * })
130
136
  */
131
137
  defaults?: EnvProviderDefaults;
138
+ /**
139
+ * Automatically use a built-in preset when the config set name matches a preset name.
140
+ *
141
+ * When enabled (default), if a config set name matches a built-in preset name (e.g., `openai`, `anthropic`, `deepseek`),
142
+ * that preset is automatically applied without requiring the `{PREFIX}_PRESET` environment variable.
143
+ * Set to `false` to disable this behavior and require explicit preset configuration.
144
+ *
145
+ * @example
146
+ * // With presetAutoDetect enabled (default):
147
+ * // OPENAI_API_KEY=sk-xxx
148
+ * // → automatically uses the 'openai' preset
149
+ *
150
+ * @example
151
+ * // With presetAutoDetect disabled:
152
+ * // OPENAI_API_KEY=sk-xxx
153
+ * // OPENAI_PRESET=openai (required)
154
+ * envProvider({ presetAutoDetect: false })
155
+ */
156
+ presetAutoDetect?: boolean;
132
157
  }
133
158
  //#endregion
134
159
  //#region src/env-provider.d.ts
@@ -142,7 +167,7 @@ interface EnvProviderOptions {
142
167
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
143
168
  * - `ZHIPU_BASE_URL` — API base URL
144
169
  * - `ZHIPU_API_KEY` — API key (required)
145
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
170
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
146
171
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
147
172
  *
148
173
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EAyBe;EAvBf,OAAA;;;;AAqCF;;;;EA7BE,UAAA;EAsCe;;;;;;EA/Bf,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA0Df;EAxDA,OAAA;EA4EU;;;EAxEV,UAAA;AAAA;;;;;ACwQF;UDhQiB,mBAAA;;;;;;;;;EASf,KAAA,UAAe,UAAA,CAAW,KAAA;;AEtD5B;;;;;;;;;;;;EFqEE,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;AAlDb;;;;;;;;;;;;;;;;;;;ACkOA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD/S/D;;;;cEEa,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":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EA0Be;EAxBf,OAAA;;;;AA2CF;;;;;EAlCE,UAAA;EA2C0B;;;;;AAqB5B;EAzDE,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EAmFf;EAjFA,OAAA;EAiFyB;;;;;;;;EAxEzB,UAAA;AAAA;;;;;;UAQe,mBAAA;ECuRwD;;;;;AC1UzE;;;EF4DE,KAAA,UAAe,UAAA,CAAW,KAAA;EE5D4B;;;;;;;;;;;;;EF2EtD,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;;;;;;;;;;;;;;;;;;;EAoBX,gBAAA;AAAA;;;;;;;;;;AAtEF;;;;;;;;;;;;;;;;;;;;ACyPA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD5U/D;;;;cEEa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
package/dist/index.mjs CHANGED
@@ -34,6 +34,19 @@ function createAnthropicProvider(opts) {
34
34
  }
35
35
  }
36
36
  /**
37
+ * Create a Google Generative AI (Gemini) provider.
38
+ *
39
+ * Dynamically requires `@ai-sdk/google`, so it only needs to be installed when actually used.
40
+ */
41
+ function createGeminiProvider(opts) {
42
+ try {
43
+ const { createGoogleGenerativeAI } = __require("@ai-sdk/google");
44
+ return createGoogleGenerativeAI(opts);
45
+ } catch {
46
+ throw new Error("[ai-sdk-provider-env] gemini compatibility mode requires @ai-sdk/google. Run: npm install @ai-sdk/google");
47
+ }
48
+ }
49
+ /**
37
50
  * Create an OpenAI Compatible provider.
38
51
  *
39
52
  * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.
@@ -64,45 +77,49 @@ const builtinPresets = {
64
77
  baseURL: "https://api.anthropic.com",
65
78
  compatible: "anthropic"
66
79
  },
80
+ google: {
81
+ baseURL: "https://generativelanguage.googleapis.com/v1beta",
82
+ compatible: "gemini"
83
+ },
67
84
  deepseek: {
68
85
  baseURL: "https://api.deepseek.com",
69
- compatible: "openai"
86
+ compatible: "openai-compatible"
70
87
  },
71
88
  zhipu: {
72
89
  baseURL: "https://open.bigmodel.cn/api/paas/v4",
73
- compatible: "openai"
90
+ compatible: "openai-compatible"
74
91
  },
75
92
  groq: {
76
93
  baseURL: "https://api.groq.com/openai/v1",
77
- compatible: "openai"
94
+ compatible: "openai-compatible"
78
95
  },
79
96
  together: {
80
97
  baseURL: "https://api.together.xyz/v1",
81
- compatible: "openai"
98
+ compatible: "openai-compatible"
82
99
  },
83
100
  fireworks: {
84
101
  baseURL: "https://api.fireworks.ai/inference/v1",
85
- compatible: "openai"
102
+ compatible: "openai-compatible"
86
103
  },
87
104
  mistral: {
88
105
  baseURL: "https://api.mistral.ai/v1",
89
- compatible: "openai"
106
+ compatible: "openai-compatible"
90
107
  },
91
108
  moonshot: {
92
109
  baseURL: "https://api.moonshot.cn/v1",
93
- compatible: "openai"
110
+ compatible: "openai-compatible"
94
111
  },
95
112
  perplexity: {
96
113
  baseURL: "https://api.perplexity.ai",
97
- compatible: "openai"
114
+ compatible: "openai-compatible"
98
115
  },
99
116
  openrouter: {
100
117
  baseURL: "https://openrouter.ai/api/v1",
101
- compatible: "openai"
118
+ compatible: "openai-compatible"
102
119
  },
103
120
  siliconflow: {
104
121
  baseURL: "https://api.siliconflow.cn/v1",
105
- compatible: "openai"
122
+ compatible: "openai-compatible"
106
123
  }
107
124
  };
108
125
 
@@ -114,6 +131,7 @@ const builtinPresets = {
114
131
  const defaultFactories = {
115
132
  createOpenAI: createOpenAIProvider,
116
133
  createAnthropic: createAnthropicProvider,
134
+ createGemini: createGeminiProvider,
117
135
  createOpenAICompatible: createOpenAICompatibleProvider
118
136
  };
119
137
  /**
@@ -138,7 +156,7 @@ function createEnvProvider(factories, options = {}) {
138
156
  }
139
157
  return {
140
158
  baseURL: preset.baseURL,
141
- compatible: preset.compatible ?? "openai"
159
+ compatible: preset.compatible ?? "openai-compatible"
142
160
  };
143
161
  }
144
162
  /**
@@ -160,7 +178,7 @@ function createEnvProvider(factories, options = {}) {
160
178
  return {
161
179
  baseURL: config.baseURL,
162
180
  apiKey: config.apiKey,
163
- compatible: config.compatible ?? "openai",
181
+ compatible: config.compatible ?? "openai-compatible",
164
182
  ...config.headers && { headers: config.headers }
165
183
  };
166
184
  }
@@ -186,18 +204,29 @@ function createEnvProvider(factories, options = {}) {
186
204
  };
187
205
  }
188
206
  const baseURL = env("BASE_URL");
189
- if (!baseURL) throw new Error(`[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL (or set ${prefix}${separator}PRESET to use a preset)`);
190
- return {
207
+ if (baseURL) return {
191
208
  baseURL,
192
209
  apiKey,
193
- compatible: env("COMPATIBLE") ?? "openai",
210
+ compatible: env("COMPATIBLE") ?? "openai-compatible",
194
211
  ...headers && { headers }
195
212
  };
213
+ if (options.presetAutoDetect !== false) {
214
+ const autoPreset = builtinPresets[configSet.toLowerCase()];
215
+ if (autoPreset) return {
216
+ baseURL: autoPreset.baseURL,
217
+ apiKey,
218
+ compatible: env("COMPATIBLE") ?? autoPreset.compatible ?? "openai-compatible",
219
+ ...headers && { headers }
220
+ };
221
+ }
222
+ const available = Object.keys(builtinPresets).join(", ");
223
+ const presetHint = builtinPresets[configSet.toLowerCase()] ? ` (Note: "${configSet}" matches a built-in preset, but presetAutoDetect is disabled.)` : "";
224
+ throw new Error(`[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL (or set ${prefix}${separator}PRESET to use a preset. Available presets: ${available})${presetHint}`);
196
225
  }
197
226
  /**
198
227
  * Create the underlying provider based on the compatibility mode.
199
228
  */
200
- function createUnderlying(config) {
229
+ function createUnderlying(configSet, config) {
201
230
  const { baseURL, apiKey, compatible, headers } = config;
202
231
  const mergedHeaders = defaultHeaders || headers ? {
203
232
  ...defaultHeaders,
@@ -212,10 +241,12 @@ function createEnvProvider(factories, options = {}) {
212
241
  switch (compatible) {
213
242
  case "openai": return factories.createOpenAI(baseOpts);
214
243
  case "anthropic": return factories.createAnthropic(baseOpts);
215
- default: return factories.createOpenAICompatible({
216
- name: compatible,
244
+ case "gemini": return factories.createGemini(baseOpts);
245
+ case "openai-compatible": return factories.createOpenAICompatible({
246
+ name: configSet,
217
247
  ...baseOpts
218
248
  });
249
+ default: throw new Error(`[ai-sdk-provider-env] Unknown compatible mode "${compatible}". Supported values: "openai", "anthropic", "gemini", "openai-compatible". Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`);
219
250
  }
220
251
  }
221
252
  /**
@@ -225,7 +256,7 @@ function createEnvProvider(factories, options = {}) {
225
256
  const key = configSet.toUpperCase();
226
257
  const cached = cache.get(key);
227
258
  if (cached) return cached;
228
- const provider = createUnderlying(resolveConfig(configSet));
259
+ const provider = createUnderlying(configSet, resolveConfig(configSet));
229
260
  cache.set(key, provider);
230
261
  return provider;
231
262
  }
@@ -302,7 +333,7 @@ function createEnvProvider(factories, options = {}) {
302
333
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
303
334
  * - `ZHIPU_BASE_URL` — API base URL
304
335
  * - `ZHIPU_API_KEY` — API key (required)
305
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
336
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
306
337
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
307
338
  *
308
339
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\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 */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\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 */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\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 {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.\n */\nexport function createOpenAICompatibleProvider(opts: ProviderOpts & { name: string }): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai-compatible mode requires @ai-sdk/openai-compatible. '\n + 'Run: npm install @ai-sdk/openai-compatible',\n )\n }\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 // DeepSeek\n deepseek: {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai',\n },\n\n // Zhipu AI (GLM series)\n zhipu: {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai',\n },\n\n // Groq\n groq: {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai',\n },\n\n // Together AI\n together: {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai',\n },\n\n // Fireworks AI\n fireworks: {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai',\n },\n\n // Mistral AI\n mistral: {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai',\n },\n\n // Moonshot AI\n moonshot: {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai',\n },\n\n // Perplexity\n perplexity: {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai',\n },\n\n // OpenRouter\n openrouter: {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai',\n },\n\n // SiliconFlow\n siliconflow: {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { ProviderOpts } from './factories'\nimport type { EnvProviderOptions } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createOpenAICompatibleProvider, createOpenAIProvider } from './factories'\nimport { builtinPresets } from './presets'\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 */\nexport interface ProviderFactories {\n createOpenAI: (opts: ProviderOpts) => ProviderV3\n createAnthropic: (opts: ProviderOpts) => ProviderV3\n createOpenAICompatible: (opts: ProviderOpts & { name: string }) => ProviderV3\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 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: EnvProviderOptions = {},\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 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',\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',\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n const prefix = configSet.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, BASE_URL is required\n const baseURL = env('BASE_URL')\n if (!baseURL) {\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 )\n }\n\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai',\n ...(headers && { headers }),\n }\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(config: ResolvedConfig): ProviderV3 {\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 return factories.createOpenAI(baseOpts)\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n default:\n return factories.createOpenAICompatible({ name: compatible, ...baseOpts })\n }\n }\n\n /**\n * Get or create a cached provider for the given config set.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = configSet.toUpperCase()\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n const provider = createUnderlying(config)\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 if (!provider.textEmbeddingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n }\n return provider.textEmbeddingModel(model)\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 * 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 * 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'`)\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 */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n return createEnvProvider(defaultFactories, options)\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,qBAAqB,MAAgC;AACnE,KAAI;EAEF,MAAM,EAAE,2BAAyB,iBAAiB;AAClD,SAAO,aAAa,KAAK;SAErB;AACJ,QAAM,IAAI,MACR,2GAED;;;;;;;;AASL,SAAgB,wBAAwB,MAAgC;AACtE,KAAI;EAEF,MAAM,EAAE,8BAA4B,oBAAoB;AACxD,SAAO,gBAAgB,KAAK;SAExB;AACJ,QAAM,IAAI,MACR,oHAED;;;;;;;;AASL,SAAgB,+BAA+B,MAAmD;AAChG,KAAI;EAEF,MAAM,EAAE,qCAAmC,4BAA4B;AACvE,SAAO,uBAAuB,KAAK;SAE/B;AACJ,QAAM,IAAI,MACR,8HAED;;;;;;;;;;;;ACtDL,MAAa,iBAA+C;CAE1D,QAAQ;EACN,SAAS;EACT,YAAY;EACb;CAGD,WAAW;EACT,SAAS;EACT,YAAY;EACb;CAGD,UAAU;EACR,SAAS;EACT,YAAY;EACb;CAGD,OAAO;EACL,SAAS;EACT,YAAY;EACb;CAGD,MAAM;EACJ,SAAS;EACT,YAAY;EACb;CAGD,UAAU;EACR,SAAS;EACT,YAAY;EACb;CAGD,WAAW;EACT,SAAS;EACT,YAAY;EACb;CAGD,SAAS;EACP,SAAS;EACT,YAAY;EACb;CAGD,UAAU;EACR,SAAS;EACT,YAAY;EACb;CAGD,YAAY;EACV,SAAS;EACT,YAAY;EACb;CAGD,YAAY;EACV,SAAS;EACT,YAAY;EACb;CAGD,aAAa;EACX,SAAS;EACT,YAAY;EACb;CACF;;;;;;;ACzDD,MAAM,mBAAsC;CAC1C,cAAc;CACd,iBAAiB;CACjB,wBAAwB;CACzB;;;;;;;AAkBD,SAAgB,kBACd,WACA,UAA8B,EAAE,EACpB;CACZ,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,eAAe,QAAQ,UAAU;CACvC,MAAM,iBAAiB,QAAQ,UAAU;CAGzC,MAAM,wBAAQ,IAAI,KAAyB;;;;CAK3C,SAAS,cAAc,YAA6D;EAClF,MAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,QAAQ;GACX,MAAM,YAAY,OAAO,KAAK,eAAe,CAAC,KAAK,KAAK;AACxD,SAAM,IAAI,MACR,yCAAyC,WAAW,wBAAwB,YAC7E;;AAEH,SAAO;GACL,SAAS,OAAO;GAChB,YAAY,OAAO,cAAc;GAClC;;;;;CAMH,SAAS,cAAc,WAAmC;AAExD,MAAI,QAAQ,UAAU,YAAY;GAChC,MAAM,SAAS,QAAQ,QAAQ;AAG/B,OAAI,OAAO,QAAQ;IACjB,MAAM,SAAS,cAAc,OAAO,OAAO;AAC3C,WAAO;KACL,SAAS,OAAO,WAAW,OAAO;KAClC,QAAQ,OAAO;KACf,YAAY,OAAO,cAAc,OAAO;KACxC,GAAI,OAAO,WAAW,EAAE,SAAS,OAAO,SAAS;KAClD;;AAGH,OAAI,CAAC,OAAO,QACV,OAAM,IAAI,MACR,wDAAwD,UAAU,4CAEnE;AAGH,UAAO;IACL,SAAS,OAAO;IAChB,QAAQ,OAAO;IACf,YAAY,OAAO,cAAc;IACjC,GAAI,OAAO,WAAW,EAAE,SAAS,OAAO,SAAS;IAClD;;EAGH,MAAM,SAAS,UAAU,aAAa;EACtC,MAAM,OAAO,QAAoC,QAAQ,IAAI,GAAG,SAAS,YAAY;EAErF,MAAM,SAAS,IAAI,UAAU;AAC7B,MAAI,CAAC,OACH,OAAM,IAAI,MACR,yCAAyC,SAAS,UAAU,SAC7D;EAIH,MAAM,aAAa,IAAI,UAAU;EACjC,IAAI;AACJ,MAAI,WACF,KAAI;AACF,aAAU,KAAK,MAAM,WAAW;UAE5B;AACJ,SAAM,IAAI,MACR,yCAAyC,SAAS,UAAU,WAAW,aACxE;;EAKL,MAAM,aAAa,IAAI,SAAS;AAChC,MAAI,YAAY;GACd,MAAM,SAAS,cAAc,WAAW;AACxC,UAAO;IACL,SAAS,IAAI,WAAW,IAAI,OAAO;IACnC;IACA,YAAY,IAAI,aAAa,IAAI,OAAO;IACxC,GAAI,WAAW,EAAE,SAAS;IAC3B;;EAIH,MAAM,UAAU,IAAI,WAAW;AAC/B,MAAI,CAAC,QACH,OAAM,IAAI,MACR,yCAAyC,SAAS,UAAU,mBAC9C,SAAS,UAAU,yBAClC;AAGH,SAAO;GACL;GACA;GACA,YAAY,IAAI,aAAa,IAAI;GACjC,GAAI,WAAW,EAAE,SAAS;GAC3B;;;;;CAMH,SAAS,iBAAiB,QAAoC;EAC5D,MAAM,EAAE,SAAS,QAAQ,YAAY,YAAY;EAGjD,MAAM,gBAAiB,kBAAkB,UACrC;GAAE,GAAG;GAAgB,GAAG;GAAS,GACjC;EAEJ,MAAM,WAAW;GACf;GACA;GACA,GAAI,iBAAiB,EAAE,SAAS,eAAe;GAC/C,GAAI,gBAAgB,EAAE,OAAO,cAAc;GAC5C;AAED,UAAQ,YAAR;GACE,KAAK,SACH,QAAO,UAAU,aAAa,SAAS;GACzC,KAAK,YACH,QAAO,UAAU,gBAAgB,SAAS;GAC5C,QACE,QAAO,UAAU,uBAAuB;IAAE,MAAM;IAAY,GAAG;IAAU,CAAC;;;;;;CAOhF,SAAS,YAAY,WAA+B;EAClD,MAAM,MAAM,UAAU,aAAa;EACnC,MAAM,SAAS,MAAM,IAAI,IAAI;AAC7B,MAAI,OACF,QAAO;EAGT,MAAM,WAAW,iBADF,cAAc,UAAU,CACE;AACzC,QAAM,IAAI,KAAK,SAAS;AACxB,SAAO;;;;;CAMT,SAAS,aAAa,SAAuD;EAC3E,MAAM,aAAa,QAAQ,QAAQ,IAAI;AACvC,MAAI,eAAe,GACjB,OAAM,IAAI,MACR,2CAA2C,QAAQ,iEAEpD;AAEH,SAAO;GACL,WAAW,QAAQ,MAAM,GAAG,WAAW;GACvC,OAAO,QAAQ,MAAM,aAAa,EAAE;GACrC;;AAGH,QAAO;EACL,sBAAsB;EAEtB,cAAc,SAAiB;GAC7B,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;AAClD,UAAO,YAAY,UAAU,CAAC,cAAc,MAAM;;EAGpD,eAAe,SAAiB;GAC9B,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;AAClD,UAAO,YAAY,UAAU,CAAC,eAAe,MAAM;;EAGrD,WAAW,SAAiB;GAC1B,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;AAClD,UAAO,YAAY,UAAU,CAAC,WAAW,MAAM;;EAGjD,mBAAmB,SAAiB;GAClC,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;GAClD,MAAM,WAAW,YAAY,UAAU;AACvC,OAAI,CAAC,SAAS,mBACZ,OAAM,IAAI,iBAAiB;IAAE;IAAS,WAAW;IAAkB,CAAC;AAEtE,UAAO,SAAS,mBAAmB,MAAM;;EAG3C,mBAAmB,SAAiB;GAClC,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;GAClD,MAAM,WAAW,YAAY,UAAU;AACvC,OAAI,CAAC,SAAS,mBACZ,OAAM,IAAI,iBAAiB;IAAE;IAAS,WAAW;IAAsB,CAAC;AAE1E,UAAO,SAAS,mBAAmB,MAAM;;EAG3C,YAAY,SAAiB;GAC3B,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;GAClD,MAAM,WAAW,YAAY,UAAU;AACvC,OAAI,CAAC,SAAS,YACZ,OAAM,IAAI,iBAAiB;IAAE;IAAS,WAAW;IAAe,CAAC;AAEnE,UAAO,SAAS,YAAY,MAAM;;EAGpC,eAAe,SAAiB;GAC9B,MAAM,EAAE,WAAW,UAAU,aAAa,QAAQ;GAClD,MAAM,WAAW,YAAY,UAAU;AACvC,OAAI,CAAC,SAAS,eACZ,OAAM,IAAI,iBAAiB;IAAE;IAAS,WAAW;IAAkB,CAAC;AAEtE,UAAO,SAAS,eAAe,MAAM;;EAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCH,SAAgB,YAAY,UAA8B,EAAE,EAAc;AACxE,QAAO,kBAAkB,kBAAkB,QAAQ"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\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 */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\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 */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\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 {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\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 */\nexport function createGeminiProvider(opts: ProviderOpts): ProviderV3 {\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 {\n throw new Error(\n '[ai-sdk-provider-env] gemini compatibility mode requires @ai-sdk/google. '\n + 'Run: npm install @ai-sdk/google',\n )\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.\n */\nexport function createOpenAICompatibleProvider(opts: ProviderOpts & { name: string }): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai-compatible mode requires @ai-sdk/openai-compatible. '\n + 'Run: npm install @ai-sdk/openai-compatible',\n )\n }\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\n moonshot: {\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\n siliconflow: {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai-compatible',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { ProviderOpts } from './factories'\nimport type { EnvProviderOptions } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createGeminiProvider, createOpenAICompatibleProvider, createOpenAIProvider } from './factories'\nimport { builtinPresets } from './presets'\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 */\nexport interface ProviderFactories {\n createOpenAI: (opts: ProviderOpts) => ProviderV3\n createAnthropic: (opts: ProviderOpts) => ProviderV3\n createGemini: (opts: ProviderOpts) => ProviderV3\n createOpenAICompatible: (opts: ProviderOpts & { name: string }) => ProviderV3\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: EnvProviderOptions = {},\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 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 const prefix = configSet.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): ProviderV3 {\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 return factories.createOpenAI(baseOpts)\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n case 'gemini':\n return factories.createGemini(baseOpts)\n case 'openai-compatible':\n return factories.createOpenAICompatible({ name: configSet, ...baseOpts })\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 * Get or create a cached provider for the given config set.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = configSet.toUpperCase()\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n const provider = createUnderlying(configSet, config)\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 if (!provider.textEmbeddingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n }\n return provider.textEmbeddingModel(model)\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 * 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 * 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 */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n return createEnvProvider(defaultFactories, options)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-sdk-provider-env",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.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",
@@ -48,6 +48,7 @@
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@ai-sdk/anthropic": ">=3.0.0",
51
+ "@ai-sdk/google": ">=3.0.0",
51
52
  "@ai-sdk/openai": ">=3.0.0",
52
53
  "@ai-sdk/openai-compatible": ">=2.0.0",
53
54
  "@ai-sdk/provider": ">=3.0.0",
@@ -57,6 +58,9 @@
57
58
  "@ai-sdk/anthropic": {
58
59
  "optional": true
59
60
  },
61
+ "@ai-sdk/google": {
62
+ "optional": true
63
+ },
60
64
  "@ai-sdk/openai": {
61
65
  "optional": true
62
66
  },
@@ -66,6 +70,7 @@
66
70
  },
67
71
  "devDependencies": {
68
72
  "@ai-sdk/anthropic": "^3.0.0",
73
+ "@ai-sdk/google": "^3.0.0",
69
74
  "@ai-sdk/openai": "^3.0.0",
70
75
  "@ai-sdk/openai-compatible": "^2.0.0",
71
76
  "@ai-sdk/provider": "^3.0.0",