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 +44 -19
- package/dist/index.cjs +51 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +31 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +51 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -1
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
|
|
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`
|
|
100
|
+
| `openai` | Uses `@ai-sdk/openai` |
|
|
101
101
|
| `anthropic` | Uses `@ai-sdk/anthropic` |
|
|
102
|
-
|
|
|
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
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
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?:
|
|
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 (
|
|
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
|
-
|
|
240
|
-
|
|
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
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["process","NoSuchModelError"],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { 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`
|
|
24
|
+
* - `'openai'` — uses `createOpenAI`
|
|
25
25
|
* - `'anthropic'` — uses `createAnthropic`
|
|
26
|
-
* -
|
|
26
|
+
* - `'gemini'` — uses `createGoogleGenerativeAI`
|
|
27
|
+
* - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
|
|
27
28
|
*/
|
|
28
|
-
compatible?:
|
|
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
|
|
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?:
|
|
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
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;
|
|
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`
|
|
24
|
+
* - `'openai'` — uses `createOpenAI`
|
|
25
25
|
* - `'anthropic'` — uses `createAnthropic`
|
|
26
|
-
* -
|
|
26
|
+
* - `'gemini'` — uses `createGoogleGenerativeAI`
|
|
27
|
+
* - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
|
|
27
28
|
*/
|
|
28
|
-
compatible?:
|
|
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
|
|
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?:
|
|
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
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;
|
|
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 (
|
|
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
|
-
|
|
216
|
-
|
|
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
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { 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.
|
|
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",
|