ai-sdk-provider-env 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,7 +31,7 @@ pnpm add ai-sdk-provider-env
31
31
  Install provider SDKs as needed:
32
32
 
33
33
  ```bash
34
- pnpm add @ai-sdk/openai # for OpenAI and OpenAI-compatible
34
+ pnpm add @ai-sdk/openai # for OpenAI
35
35
  pnpm add @ai-sdk/anthropic # for Anthropic
36
36
  pnpm add @ai-sdk/openai-compatible # for generic OpenAI-compatible APIs
37
37
  ```
@@ -88,7 +88,7 @@ With the default separator `_`, a config set reads these variables (`[MYAI]` = y
88
88
  | `[MYAI]_API_KEY` | Yes | API key |
89
89
  | `[MYAI]_BASE_URL` | Yes (unless preset is set) | 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,9 @@ When `PRESET` is set, `BASE_URL` and `COMPATIBLE` become optional and fall back
97
97
 
98
98
  | Value | Behavior |
99
99
  |---|---|
100
- | `openai` | Uses `@ai-sdk/openai` (default) |
100
+ | `openai` | Uses `@ai-sdk/openai` |
101
101
  | `anthropic` | Uses `@ai-sdk/anthropic` |
102
- | any other string | Uses `@ai-sdk/openai-compatible` with that string as the provider name |
102
+ | `openai-compatible` | Uses `@ai-sdk/openai-compatible` with the config set name as the provider name (default) |
103
103
 
104
104
  ## Built-in Presets
105
105
 
@@ -107,16 +107,16 @@ When `PRESET` is set, `BASE_URL` and `COMPATIBLE` become optional and fall back
107
107
  |---|---|---|
108
108
  | `openai` | `https://api.openai.com/v1` | `openai` |
109
109
  | `anthropic` | `https://api.anthropic.com` | `anthropic` |
110
- | `deepseek` | `https://api.deepseek.com` | `openai` |
111
- | `zhipu` | `https://open.bigmodel.cn/api/paas/v4` | `openai` |
112
- | `groq` | `https://api.groq.com/openai/v1` | `openai` |
113
- | `together` | `https://api.together.xyz/v1` | `openai` |
114
- | `fireworks` | `https://api.fireworks.ai/inference/v1` | `openai` |
115
- | `mistral` | `https://api.mistral.ai/v1` | `openai` |
116
- | `moonshot` | `https://api.moonshot.cn/v1` | `openai` |
117
- | `perplexity` | `https://api.perplexity.ai` | `openai` |
118
- | `openrouter` | `https://openrouter.ai/api/v1` | `openai` |
119
- | `siliconflow` | `https://api.siliconflow.cn/v1` | `openai` |
110
+ | `deepseek` | `https://api.deepseek.com` | `openai-compatible` |
111
+ | `zhipu` | `https://open.bigmodel.cn/api/paas/v4` | `openai-compatible` |
112
+ | `groq` | `https://api.groq.com/openai/v1` | `openai-compatible` |
113
+ | `together` | `https://api.together.xyz/v1` | `openai-compatible` |
114
+ | `fireworks` | `https://api.fireworks.ai/inference/v1` | `openai-compatible` |
115
+ | `mistral` | `https://api.mistral.ai/v1` | `openai-compatible` |
116
+ | `moonshot` | `https://api.moonshot.cn/v1` | `openai-compatible` |
117
+ | `perplexity` | `https://api.perplexity.ai` | `openai-compatible` |
118
+ | `openrouter` | `https://openrouter.ai/api/v1` | `openai-compatible` |
119
+ | `siliconflow` | `https://api.siliconflow.cn/v1` | `openai-compatible` |
120
120
 
121
121
  ## API Reference
122
122
 
@@ -152,7 +152,7 @@ interface ConfigSetEntry {
152
152
  apiKey: string
153
153
  preset?: string
154
154
  baseURL?: string
155
- compatible?: string // 'openai' | 'anthropic' | any string (default: 'openai')
155
+ compatible?: 'openai' | 'anthropic' | 'openai-compatible' // default: 'openai-compatible'
156
156
  headers?: Record<string, string>
157
157
  }
158
158
  ```
package/dist/index.cjs CHANGED
@@ -90,43 +90,43 @@ const builtinPresets = {
90
90
  },
91
91
  deepseek: {
92
92
  baseURL: "https://api.deepseek.com",
93
- compatible: "openai"
93
+ compatible: "openai-compatible"
94
94
  },
95
95
  zhipu: {
96
96
  baseURL: "https://open.bigmodel.cn/api/paas/v4",
97
- compatible: "openai"
97
+ compatible: "openai-compatible"
98
98
  },
99
99
  groq: {
100
100
  baseURL: "https://api.groq.com/openai/v1",
101
- compatible: "openai"
101
+ compatible: "openai-compatible"
102
102
  },
103
103
  together: {
104
104
  baseURL: "https://api.together.xyz/v1",
105
- compatible: "openai"
105
+ compatible: "openai-compatible"
106
106
  },
107
107
  fireworks: {
108
108
  baseURL: "https://api.fireworks.ai/inference/v1",
109
- compatible: "openai"
109
+ compatible: "openai-compatible"
110
110
  },
111
111
  mistral: {
112
112
  baseURL: "https://api.mistral.ai/v1",
113
- compatible: "openai"
113
+ compatible: "openai-compatible"
114
114
  },
115
115
  moonshot: {
116
116
  baseURL: "https://api.moonshot.cn/v1",
117
- compatible: "openai"
117
+ compatible: "openai-compatible"
118
118
  },
119
119
  perplexity: {
120
120
  baseURL: "https://api.perplexity.ai",
121
- compatible: "openai"
121
+ compatible: "openai-compatible"
122
122
  },
123
123
  openrouter: {
124
124
  baseURL: "https://openrouter.ai/api/v1",
125
- compatible: "openai"
125
+ compatible: "openai-compatible"
126
126
  },
127
127
  siliconflow: {
128
128
  baseURL: "https://api.siliconflow.cn/v1",
129
- compatible: "openai"
129
+ compatible: "openai-compatible"
130
130
  }
131
131
  };
132
132
 
@@ -162,7 +162,7 @@ function createEnvProvider(factories, options = {}) {
162
162
  }
163
163
  return {
164
164
  baseURL: preset.baseURL,
165
- compatible: preset.compatible ?? "openai"
165
+ compatible: preset.compatible ?? "openai-compatible"
166
166
  };
167
167
  }
168
168
  /**
@@ -184,7 +184,7 @@ function createEnvProvider(factories, options = {}) {
184
184
  return {
185
185
  baseURL: config.baseURL,
186
186
  apiKey: config.apiKey,
187
- compatible: config.compatible ?? "openai",
187
+ compatible: config.compatible ?? "openai-compatible",
188
188
  ...config.headers && { headers: config.headers }
189
189
  };
190
190
  }
@@ -214,14 +214,14 @@ function createEnvProvider(factories, options = {}) {
214
214
  return {
215
215
  baseURL,
216
216
  apiKey,
217
- compatible: env("COMPATIBLE") ?? "openai",
217
+ compatible: env("COMPATIBLE") ?? "openai-compatible",
218
218
  ...headers && { headers }
219
219
  };
220
220
  }
221
221
  /**
222
222
  * Create the underlying provider based on the compatibility mode.
223
223
  */
224
- function createUnderlying(config) {
224
+ function createUnderlying(configSet, config) {
225
225
  const { baseURL, apiKey, compatible, headers } = config;
226
226
  const mergedHeaders = defaultHeaders || headers ? {
227
227
  ...defaultHeaders,
@@ -236,10 +236,11 @@ function createEnvProvider(factories, options = {}) {
236
236
  switch (compatible) {
237
237
  case "openai": return factories.createOpenAI(baseOpts);
238
238
  case "anthropic": return factories.createAnthropic(baseOpts);
239
- default: return factories.createOpenAICompatible({
240
- name: compatible,
239
+ case "openai-compatible": return factories.createOpenAICompatible({
240
+ name: configSet,
241
241
  ...baseOpts
242
242
  });
243
+ default: throw new Error(`[ai-sdk-provider-env] Unknown compatible mode "${compatible}". Supported values: "openai", "anthropic", "openai-compatible". Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`);
243
244
  }
244
245
  }
245
246
  /**
@@ -249,7 +250,7 @@ function createEnvProvider(factories, options = {}) {
249
250
  const key = configSet.toUpperCase();
250
251
  const cached = cache.get(key);
251
252
  if (cached) return cached;
252
- const provider = createUnderlying(resolveConfig(configSet));
253
+ const provider = createUnderlying(configSet, resolveConfig(configSet));
253
254
  cache.set(key, provider);
254
255
  return provider;
255
256
  }
@@ -326,7 +327,7 @@ function createEnvProvider(factories, options = {}) {
326
327
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
327
328
  * - `ZHIPU_BASE_URL` — API base URL
328
329
  * - `ZHIPU_API_KEY` — API key (required)
329
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
330
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
330
331
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
331
332
  *
332
333
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["process","NoSuchModelError"],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.\n */\nexport function createOpenAICompatibleProvider(opts: ProviderOpts & { name: string }): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai-compatible mode requires @ai-sdk/openai-compatible. '\n + 'Run: npm install @ai-sdk/openai-compatible',\n )\n }\n}\n","import type { PresetConfig } from './types'\n\n/**\n * Built-in preset configurations for common providers.\n *\n * When using a preset, only `{PREFIX}__PRESET` and `{PREFIX}__API_KEY`\n * are required; `BASE_URL` and `COMPATIBLE` are provided by the preset.\n */\nexport const builtinPresets: Record<string, PresetConfig> = {\n // OpenAI\n openai: {\n baseURL: 'https://api.openai.com/v1',\n compatible: 'openai',\n },\n\n // Anthropic\n anthropic: {\n baseURL: 'https://api.anthropic.com',\n compatible: 'anthropic',\n },\n\n // DeepSeek\n deepseek: {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai',\n },\n\n // Zhipu AI (GLM series)\n zhipu: {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai',\n },\n\n // Groq\n groq: {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai',\n },\n\n // Together AI\n together: {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai',\n },\n\n // Fireworks AI\n fireworks: {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai',\n },\n\n // Mistral AI\n mistral: {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai',\n },\n\n // Moonshot AI\n moonshot: {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai',\n },\n\n // Perplexity\n perplexity: {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai',\n },\n\n // OpenRouter\n openrouter: {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai',\n },\n\n // SiliconFlow\n siliconflow: {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { ProviderOpts } from './factories'\nimport type { EnvProviderOptions } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createOpenAICompatibleProvider, createOpenAIProvider } from './factories'\nimport { builtinPresets } from './presets'\n\n/**\n * Interface for provider factory functions, used for dependency injection.\n *\n * In production, `defaultFactories` delegates to the real SDK implementations.\n * In tests, fake factories can be injected to avoid module mocking.\n */\nexport interface ProviderFactories {\n createOpenAI: (opts: ProviderOpts) => ProviderV3\n createAnthropic: (opts: ProviderOpts) => ProviderV3\n createOpenAICompatible: (opts: ProviderOpts & { name: string }) => ProviderV3\n}\n\n/**\n * Default factories that delegate to the real implementations in `factories.ts`.\n */\nconst defaultFactories: ProviderFactories = {\n createOpenAI: createOpenAIProvider,\n createAnthropic: createAnthropicProvider,\n createOpenAICompatible: createOpenAICompatibleProvider,\n}\n\n/**\n * Internally resolved configuration with all required fields determined.\n */\ninterface ResolvedConfig {\n baseURL: string\n apiKey: string\n compatible: string\n headers?: Record<string, string>\n}\n\n/**\n * Testable core implementation that accepts injected provider factories.\n *\n * In tests, call this function directly with fake factories\n * to avoid module mocking entirely.\n */\nexport function createEnvProvider(\n factories: ProviderFactories,\n options: EnvProviderOptions = {},\n): ProviderV3 {\n const separator = options.separator ?? '_'\n const defaultFetch = options.defaults?.fetch\n const defaultHeaders = options.defaults?.headers\n\n // Cache created providers to avoid redundant initialization\n const cache = new Map<string, ProviderV3>()\n\n /**\n * Resolve baseURL and compatible from a preset name.\n */\n function resolvePreset(presetName: string): { baseURL: string, compatible: string } {\n const preset = builtinPresets[presetName]\n if (!preset) {\n const available = Object.keys(builtinPresets).join(', ')\n throw new Error(\n `[ai-sdk-provider-env] Unknown preset \"${presetName}\". Available presets: ${available}`,\n )\n }\n return {\n baseURL: preset.baseURL,\n compatible: preset.compatible ?? 'openai',\n }\n }\n\n /**\n * Resolve config set configuration from explicit configs, presets, or environment variables.\n */\n function resolveConfig(configSet: string): ResolvedConfig {\n // Explicit configs take precedence over env vars\n if (options.configs?.[configSet]) {\n const config = options.configs[configSet]\n\n // Code-based configs also support presets\n if (config.preset) {\n const preset = resolvePreset(config.preset)\n return {\n baseURL: config.baseURL ?? preset.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? preset.compatible,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n if (!config.baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing baseURL in config for \"${configSet}\"`\n + ` (or set preset to use a built-in preset)`,\n )\n }\n\n return {\n baseURL: config.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? 'openai',\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n const prefix = configSet.toUpperCase()\n const env = (key: string): string | undefined => process.env[`${prefix}${separator}${key}`]\n\n const apiKey = env('API_KEY')\n if (!apiKey) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}API_KEY`,\n )\n }\n\n // Parse headers from env var (JSON format)\n const headersRaw = env('HEADERS')\n let headers: Record<string, string> | undefined\n if (headersRaw) {\n try {\n headers = JSON.parse(headersRaw)\n }\n catch {\n throw new Error(\n `[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`,\n )\n }\n }\n\n // Check for preset\n const presetName = env('PRESET')\n if (presetName) {\n const preset = resolvePreset(presetName)\n return {\n baseURL: env('BASE_URL') ?? preset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? preset.compatible,\n ...(headers && { headers }),\n }\n }\n\n // Without a preset, BASE_URL is required\n const baseURL = env('BASE_URL')\n if (!baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL`\n + ` (or set ${prefix}${separator}PRESET to use a preset)`,\n )\n }\n\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai',\n ...(headers && { headers }),\n }\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(config: ResolvedConfig): ProviderV3 {\n const { baseURL, apiKey, compatible, headers } = config\n\n // Merge headers: defaults.headers as base, config-set headers override matching keys\n const mergedHeaders = (defaultHeaders || headers)\n ? { ...defaultHeaders, ...headers }\n : undefined\n\n const baseOpts = {\n baseURL,\n apiKey,\n ...(mergedHeaders && { headers: mergedHeaders }),\n ...(defaultFetch && { fetch: defaultFetch }),\n }\n\n switch (compatible) {\n case 'openai':\n return factories.createOpenAI(baseOpts)\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n default:\n return factories.createOpenAICompatible({ name: compatible, ...baseOpts })\n }\n }\n\n /**\n * Get or create a cached provider for the given config set.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = configSet.toUpperCase()\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n const provider = createUnderlying(config)\n cache.set(key, provider)\n return provider\n }\n\n /**\n * Parse a model ID. The first `/` separates the config set name from the actual model ID.\n */\n function parseModelId(modelId: string): { configSet: string, model: string } {\n const slashIndex = modelId.indexOf('/')\n if (slashIndex === -1) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid model ID \"${modelId}\". `\n + `Expected format: \"{configSet}/{modelId}\", e.g. \"zhipu/glm-4\"`,\n )\n }\n return {\n configSet: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n }\n\n return {\n specificationVersion: 'v3' as const,\n\n languageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).languageModel(model)\n },\n\n embeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).embeddingModel(model)\n },\n\n imageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).imageModel(model)\n },\n\n textEmbeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.textEmbeddingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n }\n return provider.textEmbeddingModel(model)\n },\n\n transcriptionModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.transcriptionModel) {\n throw new NoSuchModelError({ modelId, modelType: 'transcriptionModel' })\n }\n return provider.transcriptionModel(model)\n },\n\n speechModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.speechModel) {\n throw new NoSuchModelError({ modelId, modelType: 'speechModel' })\n }\n return provider.speechModel(model)\n },\n\n rerankingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.rerankingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'rerankingModel' })\n }\n return provider.rerankingModel(model)\n },\n }\n}\n\n/**\n * Create a dynamic, environment-variable-driven AI SDK provider.\n *\n * Automatically resolves provider configurations from env var naming conventions,\n * with built-in preset support for quick setup.\n *\n * Env var convention (using config set `ZHIPU` with default separator `_` as example):\n * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)\n * - `ZHIPU_BASE_URL` — API base URL\n * - `ZHIPU_API_KEY` — API key (required)\n * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)\n * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)\n *\n * @example\n * ```ts\n * import { createProviderRegistry } from 'ai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const registry = createProviderRegistry({\n * env: envProvider(),\n * })\n *\n * // Use a preset (only API_KEY is required)\n * // DEEPSEEK_PRESET=deepseek\n * // DEEPSEEK_API_KEY=sk-xxx\n * const model = registry.languageModel('env:deepseek/deepseek-chat')\n *\n * // Specify all parameters manually\n * // MYAPI_BASE_URL=https://api.example.com/v1\n * // MYAPI_API_KEY=xxx\n * const model2 = registry.languageModel('env:myapi/some-model')\n * ```\n */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n return createEnvProvider(defaultFactories, options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["process","NoSuchModelError"],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n}\n\n/**\n * Create 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-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, 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-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, 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-compatible',\n ...(headers && { headers }),\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 '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\", \"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,11 @@ interface ConfigSetEntry {
21
21
  /**
22
22
  * Compatibility mode
23
23
  *
24
- * - `'openai'` — uses `createOpenAI` (default)
24
+ * - `'openai'` — uses `createOpenAI`
25
25
  * - `'anthropic'` — uses `createAnthropic`
26
- * - any other string — uses `createOpenAICompatible` with that string as the provider name
26
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
27
27
  */
28
- compatible?: string;
28
+ compatible?: 'openai' | 'anthropic' | 'openai-compatible';
29
29
  /**
30
30
  * Custom HTTP headers appended to API requests.
31
31
  *
@@ -43,9 +43,13 @@ interface PresetConfig {
43
43
  /** API base URL */
44
44
  baseURL: string;
45
45
  /**
46
- * Compatibility mode (defaults to `'openai'`)
46
+ * Compatibility mode
47
+ *
48
+ * - `'openai'` — uses `createOpenAI`
49
+ * - `'anthropic'` — uses `createAnthropic`
50
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
47
51
  */
48
- compatible?: string;
52
+ compatible?: 'openai' | 'anthropic' | 'openai-compatible';
49
53
  }
50
54
  /**
51
55
  * Global defaults for `envProvider()`.
@@ -142,7 +146,7 @@ interface EnvProviderOptions {
142
146
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
143
147
  * - `ZHIPU_BASE_URL` — API base URL
144
148
  * - `ZHIPU_API_KEY` — API key (required)
145
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
149
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
146
150
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
147
151
  *
148
152
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EAyBe;EAvBf,OAAA;;;;AAqCF;;;;EA7BE,UAAA;EAsCe;;;;;;EA/Bf,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA0Df;EAxDA,OAAA;EA4EU;;;EAxEV,UAAA;AAAA;;;;;ACwQF;UDhQiB,mBAAA;;;;;;;;;EASf,KAAA,UAAe,UAAA,CAAW,KAAA;;AEtD5B;;;;;;;;;;;;EFqEE,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;AAlDb;;;;;;;;;;;;;;;;;;;ACkOA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD/S/D;;;;cEEa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EAyBe;EAvBf,OAAA;;;;AAyCF;;;;EAjCE,UAAA;EA0Ce;;;;;;EAnCf,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA8Df;EA5DA,OAAA;EAgFU;;;;;;;EAxEV,UAAA;AAAA;AC0QF;;;;;AAAA,UDlQiB,mBAAA;ECkQ8C;;;;;;ACnT/D;;EF0DE,KAAA,UAAe,UAAA,CAAW,KAAA;EE1DC;;;;;;;;;;;;;EFyE3B,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;AAlDb;;;;;;;;;;;;;;;;;;;ACoOA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;ADrT/D;;;;cEEa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
package/dist/index.d.mts CHANGED
@@ -21,11 +21,11 @@ interface ConfigSetEntry {
21
21
  /**
22
22
  * Compatibility mode
23
23
  *
24
- * - `'openai'` — uses `createOpenAI` (default)
24
+ * - `'openai'` — uses `createOpenAI`
25
25
  * - `'anthropic'` — uses `createAnthropic`
26
- * - any other string — uses `createOpenAICompatible` with that string as the provider name
26
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
27
27
  */
28
- compatible?: string;
28
+ compatible?: 'openai' | 'anthropic' | 'openai-compatible';
29
29
  /**
30
30
  * Custom HTTP headers appended to API requests.
31
31
  *
@@ -43,9 +43,13 @@ interface PresetConfig {
43
43
  /** API base URL */
44
44
  baseURL: string;
45
45
  /**
46
- * Compatibility mode (defaults to `'openai'`)
46
+ * Compatibility mode
47
+ *
48
+ * - `'openai'` — uses `createOpenAI`
49
+ * - `'anthropic'` — uses `createAnthropic`
50
+ * - `'openai-compatible'` — uses `createOpenAICompatible` with the config set name as the provider name (default)
47
51
  */
48
- compatible?: string;
52
+ compatible?: 'openai' | 'anthropic' | 'openai-compatible';
49
53
  }
50
54
  /**
51
55
  * Global defaults for `envProvider()`.
@@ -142,7 +146,7 @@ interface EnvProviderOptions {
142
146
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
143
147
  * - `ZHIPU_BASE_URL` — API base URL
144
148
  * - `ZHIPU_API_KEY` — API key (required)
145
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
149
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
146
150
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
147
151
  *
148
152
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EAyBe;EAvBf,OAAA;;;;AAqCF;;;;EA7BE,UAAA;EAsCe;;;;;;EA/Bf,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA0Df;EAxDA,OAAA;EA4EU;;;EAxEV,UAAA;AAAA;;;;;ACwQF;UDhQiB,mBAAA;;;;;;;;;EASf,KAAA,UAAe,UAAA,CAAW,KAAA;;AEtD5B;;;;;;;;;;;;EFqEE,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;AAlDb;;;;;;;;;;;;;;;;;;;ACkOA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;AD/S/D;;;;cEEa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/env-provider.ts","../src/presets.ts"],"mappings":";;;;;;AAMA;;;UAAiB,cAAA;EAEf;EAAA,MAAA;EAQA;;;;;EAFA,MAAA;EAyBe;EAvBf,OAAA;;;;AAyCF;;;;EAjCE,UAAA;EA0Ce;;;;;;EAnCf,OAAA,GAAU,MAAA;AAAA;;;;;;UAQK,YAAA;EA8Df;EA5DA,OAAA;EAgFU;;;;;;;EAxEV,UAAA;AAAA;AC0QF;;;;;AAAA,UDlQiB,mBAAA;ECkQ8C;;;;;;ACnT/D;;EF0DE,KAAA,UAAe,UAAA,CAAW,KAAA;EE1DC;;;;;;;;;;;;;EFyE3B,OAAA,GAAU,MAAA;AAAA;;;;UAMK,kBAAA;;;;;;;;;;;;;;EAcf,SAAA;;;;;;;;;;;;;;;;;;;EAoBA,OAAA,GAAU,MAAA,SAAe,cAAA;;;;;;;;;;;;;;;EAgBzB,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;AAlDb;;;;;;;;;;;;;;;;;;;ACoOA;;;;;;iBAAgB,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,UAAA;;;;;ADrT/D;;;;cEEa,cAAA,EAAgB,MAAA,SAAe,YAAA"}
package/dist/index.mjs CHANGED
@@ -66,43 +66,43 @@ const builtinPresets = {
66
66
  },
67
67
  deepseek: {
68
68
  baseURL: "https://api.deepseek.com",
69
- compatible: "openai"
69
+ compatible: "openai-compatible"
70
70
  },
71
71
  zhipu: {
72
72
  baseURL: "https://open.bigmodel.cn/api/paas/v4",
73
- compatible: "openai"
73
+ compatible: "openai-compatible"
74
74
  },
75
75
  groq: {
76
76
  baseURL: "https://api.groq.com/openai/v1",
77
- compatible: "openai"
77
+ compatible: "openai-compatible"
78
78
  },
79
79
  together: {
80
80
  baseURL: "https://api.together.xyz/v1",
81
- compatible: "openai"
81
+ compatible: "openai-compatible"
82
82
  },
83
83
  fireworks: {
84
84
  baseURL: "https://api.fireworks.ai/inference/v1",
85
- compatible: "openai"
85
+ compatible: "openai-compatible"
86
86
  },
87
87
  mistral: {
88
88
  baseURL: "https://api.mistral.ai/v1",
89
- compatible: "openai"
89
+ compatible: "openai-compatible"
90
90
  },
91
91
  moonshot: {
92
92
  baseURL: "https://api.moonshot.cn/v1",
93
- compatible: "openai"
93
+ compatible: "openai-compatible"
94
94
  },
95
95
  perplexity: {
96
96
  baseURL: "https://api.perplexity.ai",
97
- compatible: "openai"
97
+ compatible: "openai-compatible"
98
98
  },
99
99
  openrouter: {
100
100
  baseURL: "https://openrouter.ai/api/v1",
101
- compatible: "openai"
101
+ compatible: "openai-compatible"
102
102
  },
103
103
  siliconflow: {
104
104
  baseURL: "https://api.siliconflow.cn/v1",
105
- compatible: "openai"
105
+ compatible: "openai-compatible"
106
106
  }
107
107
  };
108
108
 
@@ -138,7 +138,7 @@ function createEnvProvider(factories, options = {}) {
138
138
  }
139
139
  return {
140
140
  baseURL: preset.baseURL,
141
- compatible: preset.compatible ?? "openai"
141
+ compatible: preset.compatible ?? "openai-compatible"
142
142
  };
143
143
  }
144
144
  /**
@@ -160,7 +160,7 @@ function createEnvProvider(factories, options = {}) {
160
160
  return {
161
161
  baseURL: config.baseURL,
162
162
  apiKey: config.apiKey,
163
- compatible: config.compatible ?? "openai",
163
+ compatible: config.compatible ?? "openai-compatible",
164
164
  ...config.headers && { headers: config.headers }
165
165
  };
166
166
  }
@@ -190,14 +190,14 @@ function createEnvProvider(factories, options = {}) {
190
190
  return {
191
191
  baseURL,
192
192
  apiKey,
193
- compatible: env("COMPATIBLE") ?? "openai",
193
+ compatible: env("COMPATIBLE") ?? "openai-compatible",
194
194
  ...headers && { headers }
195
195
  };
196
196
  }
197
197
  /**
198
198
  * Create the underlying provider based on the compatibility mode.
199
199
  */
200
- function createUnderlying(config) {
200
+ function createUnderlying(configSet, config) {
201
201
  const { baseURL, apiKey, compatible, headers } = config;
202
202
  const mergedHeaders = defaultHeaders || headers ? {
203
203
  ...defaultHeaders,
@@ -212,10 +212,11 @@ function createEnvProvider(factories, options = {}) {
212
212
  switch (compatible) {
213
213
  case "openai": return factories.createOpenAI(baseOpts);
214
214
  case "anthropic": return factories.createAnthropic(baseOpts);
215
- default: return factories.createOpenAICompatible({
216
- name: compatible,
215
+ case "openai-compatible": return factories.createOpenAICompatible({
216
+ name: configSet,
217
217
  ...baseOpts
218
218
  });
219
+ default: throw new Error(`[ai-sdk-provider-env] Unknown compatible mode "${compatible}". Supported values: "openai", "anthropic", "openai-compatible". Set COMPATIBLE=openai-compatible (or omit it) to use the OpenAI-compatible provider.`);
219
220
  }
220
221
  }
221
222
  /**
@@ -225,7 +226,7 @@ function createEnvProvider(factories, options = {}) {
225
226
  const key = configSet.toUpperCase();
226
227
  const cached = cache.get(key);
227
228
  if (cached) return cached;
228
- const provider = createUnderlying(resolveConfig(configSet));
229
+ const provider = createUnderlying(configSet, resolveConfig(configSet));
229
230
  cache.set(key, provider);
230
231
  return provider;
231
232
  }
@@ -302,7 +303,7 @@ function createEnvProvider(factories, options = {}) {
302
303
  * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)
303
304
  * - `ZHIPU_BASE_URL` — API base URL
304
305
  * - `ZHIPU_API_KEY` — API key (required)
305
- * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)
306
+ * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai-compatible'`)
306
307
  * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)
307
308
  *
308
309
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { ProviderV3 } from '@ai-sdk/provider'\n\nexport interface ProviderOpts {\n baseURL: string\n apiKey: string\n headers?: Record<string, string>\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * Create an OpenAI provider.\n *\n * Dynamically requires `@ai-sdk/openai`, so it only needs to be installed when actually used.\n */\nexport function createOpenAIProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAI } = require('@ai-sdk/openai')\n return createOpenAI(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai compatibility mode requires @ai-sdk/openai. '\n + 'Run: npm install @ai-sdk/openai',\n )\n }\n}\n\n/**\n * Create an Anthropic provider.\n *\n * Dynamically requires `@ai-sdk/anthropic`, so it only needs to be installed when actually used.\n */\nexport function createAnthropicProvider(opts: ProviderOpts): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createAnthropic } = require('@ai-sdk/anthropic')\n return createAnthropic(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] anthropic compatibility mode requires @ai-sdk/anthropic. '\n + 'Run: npm install @ai-sdk/anthropic',\n )\n }\n}\n\n/**\n * Create an OpenAI Compatible provider.\n *\n * Dynamically requires `@ai-sdk/openai-compatible`, so it only needs to be installed when actually used.\n */\nexport function createOpenAICompatibleProvider(opts: ProviderOpts & { name: string }): ProviderV3 {\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { createOpenAICompatible } = require('@ai-sdk/openai-compatible')\n return createOpenAICompatible(opts)\n }\n catch {\n throw new Error(\n '[ai-sdk-provider-env] openai-compatible mode requires @ai-sdk/openai-compatible. '\n + 'Run: npm install @ai-sdk/openai-compatible',\n )\n }\n}\n","import type { PresetConfig } from './types'\n\n/**\n * Built-in preset configurations for common providers.\n *\n * When using a preset, only `{PREFIX}__PRESET` and `{PREFIX}__API_KEY`\n * are required; `BASE_URL` and `COMPATIBLE` are provided by the preset.\n */\nexport const builtinPresets: Record<string, PresetConfig> = {\n // OpenAI\n openai: {\n baseURL: 'https://api.openai.com/v1',\n compatible: 'openai',\n },\n\n // Anthropic\n anthropic: {\n baseURL: 'https://api.anthropic.com',\n compatible: 'anthropic',\n },\n\n // DeepSeek\n deepseek: {\n baseURL: 'https://api.deepseek.com',\n compatible: 'openai',\n },\n\n // Zhipu AI (GLM series)\n zhipu: {\n baseURL: 'https://open.bigmodel.cn/api/paas/v4',\n compatible: 'openai',\n },\n\n // Groq\n groq: {\n baseURL: 'https://api.groq.com/openai/v1',\n compatible: 'openai',\n },\n\n // Together AI\n together: {\n baseURL: 'https://api.together.xyz/v1',\n compatible: 'openai',\n },\n\n // Fireworks AI\n fireworks: {\n baseURL: 'https://api.fireworks.ai/inference/v1',\n compatible: 'openai',\n },\n\n // Mistral AI\n mistral: {\n baseURL: 'https://api.mistral.ai/v1',\n compatible: 'openai',\n },\n\n // Moonshot AI\n moonshot: {\n baseURL: 'https://api.moonshot.cn/v1',\n compatible: 'openai',\n },\n\n // Perplexity\n perplexity: {\n baseURL: 'https://api.perplexity.ai',\n compatible: 'openai',\n },\n\n // OpenRouter\n openrouter: {\n baseURL: 'https://openrouter.ai/api/v1',\n compatible: 'openai',\n },\n\n // SiliconFlow\n siliconflow: {\n baseURL: 'https://api.siliconflow.cn/v1',\n compatible: 'openai',\n },\n}\n","import type { ProviderV3 } from '@ai-sdk/provider'\nimport type { ProviderOpts } from './factories'\nimport type { EnvProviderOptions } from './types'\nimport process from 'node:process'\nimport { NoSuchModelError } from '@ai-sdk/provider'\nimport { createAnthropicProvider, createOpenAICompatibleProvider, createOpenAIProvider } from './factories'\nimport { builtinPresets } from './presets'\n\n/**\n * Interface for provider factory functions, used for dependency injection.\n *\n * In production, `defaultFactories` delegates to the real SDK implementations.\n * In tests, fake factories can be injected to avoid module mocking.\n */\nexport interface ProviderFactories {\n createOpenAI: (opts: ProviderOpts) => ProviderV3\n createAnthropic: (opts: ProviderOpts) => ProviderV3\n createOpenAICompatible: (opts: ProviderOpts & { name: string }) => ProviderV3\n}\n\n/**\n * Default factories that delegate to the real implementations in `factories.ts`.\n */\nconst defaultFactories: ProviderFactories = {\n createOpenAI: createOpenAIProvider,\n createAnthropic: createAnthropicProvider,\n createOpenAICompatible: createOpenAICompatibleProvider,\n}\n\n/**\n * Internally resolved configuration with all required fields determined.\n */\ninterface ResolvedConfig {\n baseURL: string\n apiKey: string\n compatible: string\n headers?: Record<string, string>\n}\n\n/**\n * Testable core implementation that accepts injected provider factories.\n *\n * In tests, call this function directly with fake factories\n * to avoid module mocking entirely.\n */\nexport function createEnvProvider(\n factories: ProviderFactories,\n options: EnvProviderOptions = {},\n): ProviderV3 {\n const separator = options.separator ?? '_'\n const defaultFetch = options.defaults?.fetch\n const defaultHeaders = options.defaults?.headers\n\n // Cache created providers to avoid redundant initialization\n const cache = new Map<string, ProviderV3>()\n\n /**\n * Resolve baseURL and compatible from a preset name.\n */\n function resolvePreset(presetName: string): { baseURL: string, compatible: string } {\n const preset = builtinPresets[presetName]\n if (!preset) {\n const available = Object.keys(builtinPresets).join(', ')\n throw new Error(\n `[ai-sdk-provider-env] Unknown preset \"${presetName}\". Available presets: ${available}`,\n )\n }\n return {\n baseURL: preset.baseURL,\n compatible: preset.compatible ?? 'openai',\n }\n }\n\n /**\n * Resolve config set configuration from explicit configs, presets, or environment variables.\n */\n function resolveConfig(configSet: string): ResolvedConfig {\n // Explicit configs take precedence over env vars\n if (options.configs?.[configSet]) {\n const config = options.configs[configSet]\n\n // Code-based configs also support presets\n if (config.preset) {\n const preset = resolvePreset(config.preset)\n return {\n baseURL: config.baseURL ?? preset.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? preset.compatible,\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n if (!config.baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing baseURL in config for \"${configSet}\"`\n + ` (or set preset to use a built-in preset)`,\n )\n }\n\n return {\n baseURL: config.baseURL,\n apiKey: config.apiKey,\n compatible: config.compatible ?? 'openai',\n ...(config.headers && { headers: config.headers }),\n }\n }\n\n const prefix = configSet.toUpperCase()\n const env = (key: string): string | undefined => process.env[`${prefix}${separator}${key}`]\n\n const apiKey = env('API_KEY')\n if (!apiKey) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}API_KEY`,\n )\n }\n\n // Parse headers from env var (JSON format)\n const headersRaw = env('HEADERS')\n let headers: Record<string, string> | undefined\n if (headersRaw) {\n try {\n headers = JSON.parse(headersRaw)\n }\n catch {\n throw new Error(\n `[ai-sdk-provider-env] Invalid JSON in ${prefix}${separator}HEADERS: ${headersRaw}`,\n )\n }\n }\n\n // Check for preset\n const presetName = env('PRESET')\n if (presetName) {\n const preset = resolvePreset(presetName)\n return {\n baseURL: env('BASE_URL') ?? preset.baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? preset.compatible,\n ...(headers && { headers }),\n }\n }\n\n // Without a preset, BASE_URL is required\n const baseURL = env('BASE_URL')\n if (!baseURL) {\n throw new Error(\n `[ai-sdk-provider-env] Missing env var ${prefix}${separator}BASE_URL`\n + ` (or set ${prefix}${separator}PRESET to use a preset)`,\n )\n }\n\n return {\n baseURL,\n apiKey,\n compatible: env('COMPATIBLE') ?? 'openai',\n ...(headers && { headers }),\n }\n }\n\n /**\n * Create the underlying provider based on the compatibility mode.\n */\n function createUnderlying(config: ResolvedConfig): ProviderV3 {\n const { baseURL, apiKey, compatible, headers } = config\n\n // Merge headers: defaults.headers as base, config-set headers override matching keys\n const mergedHeaders = (defaultHeaders || headers)\n ? { ...defaultHeaders, ...headers }\n : undefined\n\n const baseOpts = {\n baseURL,\n apiKey,\n ...(mergedHeaders && { headers: mergedHeaders }),\n ...(defaultFetch && { fetch: defaultFetch }),\n }\n\n switch (compatible) {\n case 'openai':\n return factories.createOpenAI(baseOpts)\n case 'anthropic':\n return factories.createAnthropic(baseOpts)\n default:\n return factories.createOpenAICompatible({ name: compatible, ...baseOpts })\n }\n }\n\n /**\n * Get or create a cached provider for the given config set.\n */\n function getProvider(configSet: string): ProviderV3 {\n const key = configSet.toUpperCase()\n const cached = cache.get(key)\n if (cached)\n return cached\n\n const config = resolveConfig(configSet)\n const provider = createUnderlying(config)\n cache.set(key, provider)\n return provider\n }\n\n /**\n * Parse a model ID. The first `/` separates the config set name from the actual model ID.\n */\n function parseModelId(modelId: string): { configSet: string, model: string } {\n const slashIndex = modelId.indexOf('/')\n if (slashIndex === -1) {\n throw new Error(\n `[ai-sdk-provider-env] Invalid model ID \"${modelId}\". `\n + `Expected format: \"{configSet}/{modelId}\", e.g. \"zhipu/glm-4\"`,\n )\n }\n return {\n configSet: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n }\n\n return {\n specificationVersion: 'v3' as const,\n\n languageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).languageModel(model)\n },\n\n embeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).embeddingModel(model)\n },\n\n imageModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n return getProvider(configSet).imageModel(model)\n },\n\n textEmbeddingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.textEmbeddingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' })\n }\n return provider.textEmbeddingModel(model)\n },\n\n transcriptionModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.transcriptionModel) {\n throw new NoSuchModelError({ modelId, modelType: 'transcriptionModel' })\n }\n return provider.transcriptionModel(model)\n },\n\n speechModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.speechModel) {\n throw new NoSuchModelError({ modelId, modelType: 'speechModel' })\n }\n return provider.speechModel(model)\n },\n\n rerankingModel(modelId: string) {\n const { configSet, model } = parseModelId(modelId)\n const provider = getProvider(configSet)\n if (!provider.rerankingModel) {\n throw new NoSuchModelError({ modelId, modelType: 'rerankingModel' })\n }\n return provider.rerankingModel(model)\n },\n }\n}\n\n/**\n * Create a dynamic, environment-variable-driven AI SDK provider.\n *\n * Automatically resolves provider configurations from env var naming conventions,\n * with built-in preset support for quick setup.\n *\n * Env var convention (using config set `ZHIPU` with default separator `_` as example):\n * - `ZHIPU_PRESET` — use a built-in preset (BASE_URL and COMPATIBLE become optional)\n * - `ZHIPU_BASE_URL` — API base URL\n * - `ZHIPU_API_KEY` — API key (required)\n * - `ZHIPU_COMPATIBLE` — compatibility mode (defaults to `'openai'`)\n * - `ZHIPU_HEADERS` — custom HTTP headers (JSON format)\n *\n * @example\n * ```ts\n * import { createProviderRegistry } from 'ai'\n * import { envProvider } from 'ai-sdk-provider-env'\n *\n * const registry = createProviderRegistry({\n * env: envProvider(),\n * })\n *\n * // Use a preset (only API_KEY is required)\n * // DEEPSEEK_PRESET=deepseek\n * // DEEPSEEK_API_KEY=sk-xxx\n * const model = registry.languageModel('env:deepseek/deepseek-chat')\n *\n * // Specify all parameters manually\n * // MYAPI_BASE_URL=https://api.example.com/v1\n * // MYAPI_API_KEY=xxx\n * const model2 = registry.languageModel('env:myapi/some-model')\n * ```\n */\nexport function envProvider(options: EnvProviderOptions = {}): ProviderV3 {\n return createEnvProvider(defaultFactories, options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/factories.ts","../src/presets.ts","../src/env-provider.ts"],"sourcesContent":["import type { 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-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, 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-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, 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-compatible',\n ...(headers && { headers }),\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 '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\", \"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.0.2",
4
+ "version": "0.1.1",
5
5
  "description": "A dynamic, environment-variable-driven provider for Vercel AI SDK — resolves provider configurations from env var conventions at runtime",
6
6
  "author": "maou-shonen",
7
7
  "license": "MIT",