@nebutra/ai-providers 1.0.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/dist/index.js ADDED
@@ -0,0 +1,783 @@
1
+ import { fetchModels } from '@tokenlens/fetch';
2
+
3
+ // src/catalog.ts
4
+ var PROVIDER_MAP = {
5
+ openai: "OPENAI",
6
+ anthropic: "ANTHROPIC",
7
+ google: "GOOGLE",
8
+ "google-vertex": "GOOGLE",
9
+ "google-vertex-anthropic": "ANTHROPIC",
10
+ "amazon-bedrock": "ANTHROPIC",
11
+ // most bedrock usage is claude
12
+ siliconflow: "SILICONFLOW",
13
+ "siliconflow-cn": "SILICONFLOW",
14
+ // SiliconFlow ecosystem (GLM / Qwen / DeepSeek / MiniMax / …)
15
+ deepseek: "SILICONFLOW",
16
+ // deepseek is siliconflow-compatible
17
+ alibaba: "SILICONFLOW",
18
+ "alibaba-token-plan": "SILICONFLOW",
19
+ qwen: "SILICONFLOW",
20
+ moonshot: "SILICONFLOW",
21
+ zhipu: "SILICONFLOW",
22
+ minimax: "SILICONFLOW"
23
+ };
24
+ function mapProvider(modelsDevProviderId) {
25
+ return PROVIDER_MAP[modelsDevProviderId] ?? "CUSTOM";
26
+ }
27
+ function classifyModality(model) {
28
+ const out = model.modalities?.output ?? [];
29
+ if (out.includes("video")) return "video";
30
+ if (out.includes("image")) return "image";
31
+ if (out.includes("audio")) return "audio";
32
+ if (/embedding/i.test(model.id) || out.includes("embedding")) return "embedding";
33
+ return "text";
34
+ }
35
+ function toModelInfo(rawProvider, model) {
36
+ const visionIn = model.modalities?.input?.includes("image") ?? false;
37
+ return {
38
+ id: model.id,
39
+ name: model.name ?? model.id,
40
+ provider: mapProvider(rawProvider),
41
+ rawProvider,
42
+ modality: classifyModality(model),
43
+ ...model.limit?.context !== void 0 ? { contextWindow: model.limit.context } : {},
44
+ ...model.limit?.output !== void 0 ? { maxOutput: model.limit.output } : {},
45
+ pricing: {
46
+ ...model.cost?.input !== void 0 ? { inputPerMTok: model.cost.input } : {},
47
+ ...model.cost?.output !== void 0 ? { outputPerMTok: model.cost.output } : {}
48
+ },
49
+ capabilities: {
50
+ reasoning: model.reasoning ?? false,
51
+ toolCall: model.tool_call ?? false,
52
+ vision: visionIn
53
+ }
54
+ };
55
+ }
56
+ function dedupKey(modelId) {
57
+ return modelId.toLowerCase().replace(/^.*\//, "").replace(/[._:\-\s]/g, "");
58
+ }
59
+ var TTL_MS = (() => {
60
+ const n = Number.parseInt(process.env.MODEL_CATALOG_TTL_MS ?? "21600000", 10);
61
+ return Number.isFinite(n) && n > 0 ? n : 216e5;
62
+ })();
63
+ var cache = null;
64
+ var inflight = null;
65
+ function buildIndex(catalog) {
66
+ const index = /* @__PURE__ */ new Map();
67
+ for (const [providerId, providerInfo] of Object.entries(catalog)) {
68
+ for (const model of Object.values(providerInfo.models)) {
69
+ if (!index.has(model.id)) index.set(model.id, toModelInfo(providerId, model));
70
+ }
71
+ }
72
+ return index;
73
+ }
74
+ function buildLogical(catalog) {
75
+ const byKey = /* @__PURE__ */ new Map();
76
+ for (const [providerId, providerInfo] of Object.entries(catalog)) {
77
+ for (const model of Object.values(providerInfo.models)) {
78
+ const key = dedupKey(model.id);
79
+ if (!key) continue;
80
+ const bucket = mapProvider(providerId);
81
+ const offering = {
82
+ id: model.id,
83
+ rawProvider: providerId,
84
+ bucket,
85
+ pricing: {
86
+ ...model.cost?.input !== void 0 ? { inputPerMTok: model.cost.input } : {},
87
+ ...model.cost?.output !== void 0 ? { outputPerMTok: model.cost.output } : {}
88
+ },
89
+ ...model.limit?.context !== void 0 ? { contextWindow: model.limit.context } : {}
90
+ };
91
+ const existing = byKey.get(key);
92
+ if (existing) {
93
+ existing.offerings.push(offering);
94
+ if (!existing.buckets.includes(bucket)) existing.buckets.push(bucket);
95
+ existing.capabilities.reasoning ||= model.reasoning ?? false;
96
+ existing.capabilities.toolCall ||= model.tool_call ?? false;
97
+ existing.capabilities.vision ||= model.modalities?.input?.includes("image") ?? false;
98
+ } else {
99
+ byKey.set(key, {
100
+ key,
101
+ name: model.name ?? model.id,
102
+ modality: classifyModality(model),
103
+ capabilities: {
104
+ reasoning: model.reasoning ?? false,
105
+ toolCall: model.tool_call ?? false,
106
+ vision: model.modalities?.input?.includes("image") ?? false
107
+ },
108
+ buckets: [bucket],
109
+ offerings: [offering]
110
+ });
111
+ }
112
+ }
113
+ }
114
+ return [...byKey.values()];
115
+ }
116
+ async function loadCache() {
117
+ const now = Date.now();
118
+ if (cache && now - cache.fetchedAt < TTL_MS) return cache;
119
+ if (inflight) return inflight;
120
+ inflight = (async () => {
121
+ try {
122
+ const catalog = await fetchModels();
123
+ cache = { index: buildIndex(catalog), logical: buildLogical(catalog), fetchedAt: Date.now() };
124
+ return cache;
125
+ } catch {
126
+ if (cache) return cache;
127
+ cache = { index: /* @__PURE__ */ new Map(), logical: [], fetchedAt: Date.now() };
128
+ return cache;
129
+ } finally {
130
+ inflight = null;
131
+ }
132
+ })();
133
+ return inflight;
134
+ }
135
+ async function loadIndex() {
136
+ return (await loadCache()).index;
137
+ }
138
+ async function listModels() {
139
+ return [...(await loadIndex()).values()];
140
+ }
141
+ async function listModelsByModality(modality) {
142
+ const { logical } = await loadCache();
143
+ return logical.filter((m) => m.modality === modality).sort((a, b) => b.offerings.length - a.offerings.length);
144
+ }
145
+ async function listModalities() {
146
+ const { logical } = await loadCache();
147
+ const counts = {
148
+ text: 0,
149
+ image: 0,
150
+ video: 0,
151
+ audio: 0,
152
+ embedding: 0
153
+ };
154
+ for (const m of logical) counts[m.modality]++;
155
+ return counts;
156
+ }
157
+ async function getModelInfo(modelId) {
158
+ return (await loadIndex()).get(modelId) ?? null;
159
+ }
160
+ async function providerForModel(modelId) {
161
+ const info = await getModelInfo(modelId);
162
+ if (info) return info.provider;
163
+ return providerFromIdHeuristic(modelId);
164
+ }
165
+ function providerFromIdHeuristic(modelId) {
166
+ const m = modelId.toLowerCase().replace(/^[^/]+\//, "");
167
+ if (/^(gpt-|o1|o3|o4|chatgpt|text-|davinci|babbage)/.test(m)) return "OPENAI";
168
+ if (m.startsWith("claude")) return "ANTHROPIC";
169
+ if (m.startsWith("gemini")) return "GOOGLE";
170
+ if (/deepseek|qwen|glm|yi-|internlm|siliconflow|moonshot|kimi/.test(m)) return "SILICONFLOW";
171
+ return null;
172
+ }
173
+ var TIER_RULES = {
174
+ reasoning: {
175
+ include: /^anthropic\/claude-opus-/,
176
+ exclude: /-(fast|mini|nano|image|codex)/,
177
+ fallback: "anthropic/claude-opus-4.8"
178
+ },
179
+ flagship: {
180
+ include: /^anthropic\/claude-sonnet-/,
181
+ exclude: /-(fast|mini|nano|image|codex)/,
182
+ fallback: "anthropic/claude-sonnet-4.6"
183
+ },
184
+ fast: {
185
+ include: /^anthropic\/claude-haiku-/,
186
+ exclude: /-(image|codex)/,
187
+ fallback: "anthropic/claude-haiku-4.5"
188
+ },
189
+ "openai-flagship": {
190
+ include: /^openai\/gpt-5/,
191
+ // Prefer full flagship over mini/nano/chat/image/codex variants
192
+ exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\.\d+-(mini|nano|pro)/,
193
+ // models.dev current OpenAI flagship (audited 2026-07); live resolver may pick newer
194
+ fallback: "openai/gpt-5.5"
195
+ },
196
+ "google-flagship": {
197
+ include: /^google\/gemini-.*pro/,
198
+ exclude: /-(image|tts|customtools)/,
199
+ fallback: "google/gemini-3.1-pro-preview"
200
+ },
201
+ "google-fast": {
202
+ include: /^google\/gemini-.*flash/,
203
+ exclude: /-(image|tts|lite)/,
204
+ // models.dev: gemini-3.5-flash / 3.6-flash class
205
+ fallback: "google/gemini-3.5-flash"
206
+ }
207
+ };
208
+ var FRONTIER_FALLBACK = Object.fromEntries(
209
+ Object.entries(TIER_RULES).map(([tier, rule]) => [tier, rule.fallback])
210
+ );
211
+ var OPENROUTER_MODELS_URL = process.env.OPENROUTER_MODELS_URL ?? "https://openrouter.ai/api/v1/models";
212
+ var routableCache = null;
213
+ var routableInflight = null;
214
+ async function fetchRoutableIds() {
215
+ const now = Date.now();
216
+ if (routableCache && now - routableCache.fetchedAt < TTL_MS) return routableCache.ids;
217
+ if (routableInflight) return routableInflight;
218
+ routableInflight = (async () => {
219
+ try {
220
+ const res = await fetch(OPENROUTER_MODELS_URL);
221
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
222
+ const json = await res.json();
223
+ const ids = new Set(
224
+ (json.data ?? []).map((m) => m.id).filter((x) => typeof x === "string")
225
+ );
226
+ routableCache = { ids, fetchedAt: Date.now() };
227
+ return ids;
228
+ } catch {
229
+ return routableCache?.ids ?? /* @__PURE__ */ new Set();
230
+ } finally {
231
+ routableInflight = null;
232
+ }
233
+ })();
234
+ return routableInflight;
235
+ }
236
+ var collapseId = (s) => s.toLowerCase().replace(/[._-]/g, "");
237
+ var bareId = (id) => {
238
+ const slash = id.indexOf("/");
239
+ return slash >= 0 ? id.slice(slash + 1) : id;
240
+ };
241
+ var versionOf = (id) => {
242
+ const m = bareId(id).match(/(\d+(?:\.\d+)?)/);
243
+ const raw = m?.[1];
244
+ return raw ? Number.parseFloat(raw) : -1;
245
+ };
246
+ async function resolveFrontierModel(tier) {
247
+ const rule = TIER_RULES[tier];
248
+ try {
249
+ const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);
250
+ if (routable.size === 0) return rule.fallback;
251
+ const metaNorm = new Set([...metaIndex.keys()].map(collapseId));
252
+ const candidates = [...routable].filter(
253
+ (id) => rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id)))
254
+ );
255
+ if (candidates.length === 0) return rule.fallback;
256
+ candidates.sort((a, b) => versionOf(b) - versionOf(a));
257
+ return candidates[0] ?? rule.fallback;
258
+ } catch {
259
+ return rule.fallback;
260
+ }
261
+ }
262
+ var EFFORT_TIER = {
263
+ low: "fast",
264
+ medium: "flagship",
265
+ high: "flagship",
266
+ xhigh: "reasoning"
267
+ };
268
+ function pickByCapabilities(models, caps) {
269
+ const filtered = caps ? models.filter(
270
+ (m) => (!caps.vision || m.capabilities.vision) && (!caps.toolCall || m.capabilities.toolCall) && (!caps.reasoning || m.capabilities.reasoning)
271
+ ) : models;
272
+ return filtered[0] ?? null;
273
+ }
274
+ async function resolveModelSpec(spec, fallback) {
275
+ try {
276
+ if (spec.id) return spec.id;
277
+ if (spec.modality && spec.modality !== "text") {
278
+ const match = pickByCapabilities(
279
+ await listModelsByModality(spec.modality),
280
+ spec.capabilities
281
+ );
282
+ if (match) return match.offerings[0]?.id ?? match.key;
283
+ return fallback;
284
+ }
285
+ if (spec.reasoningEffort) return resolveFrontierModel(EFFORT_TIER[spec.reasoningEffort]);
286
+ if (spec.capabilities) {
287
+ const match = pickByCapabilities(await listModelsByModality("text"), spec.capabilities);
288
+ if (match) return match.offerings[0]?.id ?? match.key;
289
+ }
290
+ return fallback;
291
+ } catch {
292
+ return fallback;
293
+ }
294
+ }
295
+ function providerBaseUrl(provider) {
296
+ switch (provider) {
297
+ case "OPENAI":
298
+ return process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1";
299
+ case "ANTHROPIC":
300
+ return process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com/v1";
301
+ case "GOOGLE":
302
+ return process.env.GOOGLE_BASE_URL ?? "https://generativelanguage.googleapis.com/v1beta/openai";
303
+ case "SILICONFLOW":
304
+ return process.env.SILICONFLOW_BASE_URL ?? "https://api.siliconflow.cn/v1";
305
+ default:
306
+ return null;
307
+ }
308
+ }
309
+
310
+ // src/meta.ts
311
+ var PROVIDERS = [
312
+ // ─── 直接实验室 ───────────────────────────────────────────────
313
+ {
314
+ id: "anthropic",
315
+ name: "Anthropic",
316
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
317
+ status: "opencode",
318
+ docs: "https://docs.anthropic.com",
319
+ envVarPrefix: "ANTHROPIC",
320
+ requiredEnvVars: ["ANTHROPIC_API_KEY"]
321
+ },
322
+ {
323
+ id: "openai",
324
+ name: "OpenAI",
325
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
326
+ status: "opencode",
327
+ docs: "https://platform.openai.com/docs",
328
+ envVarPrefix: "OPENAI",
329
+ requiredEnvVars: ["OPENAI_API_KEY"]
330
+ },
331
+ {
332
+ id: "deepseek",
333
+ name: "DeepSeek",
334
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
335
+ status: "opencode",
336
+ docs: "https://platform.deepseek.com/api-docs",
337
+ envVarPrefix: "DEEPSEEK",
338
+ requiredEnvVars: ["DEEPSEEK_API_KEY"]
339
+ },
340
+ {
341
+ id: "xai",
342
+ name: "xAI (Grok)",
343
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
344
+ status: "opencode",
345
+ docs: "https://docs.x.ai/api",
346
+ envVarPrefix: "XAI",
347
+ requiredEnvVars: ["XAI_API_KEY"]
348
+ },
349
+ {
350
+ id: "moonshot",
351
+ name: "Moonshot (Kimi)",
352
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
353
+ status: "opencode",
354
+ docs: "https://platform.moonshot.cn/docs",
355
+ envVarPrefix: "MOONSHOT",
356
+ requiredEnvVars: ["MOONSHOT_API_KEY", "MOONSHOT_BASE_URL"]
357
+ },
358
+ {
359
+ id: "google",
360
+ name: "Google Gemini",
361
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
362
+ status: "ai-sdk",
363
+ docs: "https://ai.google.dev/gemini-api/docs",
364
+ envVarPrefix: "GOOGLE_GENERATIVE_AI",
365
+ requiredEnvVars: ["GOOGLE_GENERATIVE_AI_API_KEY"]
366
+ },
367
+ {
368
+ id: "mistral",
369
+ name: "Mistral AI",
370
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
371
+ status: "ai-sdk",
372
+ docs: "https://docs.mistral.ai",
373
+ envVarPrefix: "MISTRAL",
374
+ requiredEnvVars: ["MISTRAL_API_KEY"]
375
+ },
376
+ {
377
+ id: "cohere",
378
+ name: "Cohere",
379
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
380
+ status: "ai-sdk",
381
+ docs: "https://docs.cohere.com",
382
+ envVarPrefix: "COHERE",
383
+ requiredEnvVars: ["COHERE_API_KEY"]
384
+ },
385
+ {
386
+ id: "perplexity",
387
+ name: "Perplexity",
388
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
389
+ status: "cn-compatible",
390
+ docs: "https://docs.perplexity.ai",
391
+ baseURL: "https://api.perplexity.ai",
392
+ envVarPrefix: "PERPLEXITY",
393
+ requiredEnvVars: ["PERPLEXITY_API_KEY"]
394
+ },
395
+ {
396
+ id: "ai21",
397
+ name: "AI21 Labs",
398
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
399
+ status: "cn-compatible",
400
+ docs: "https://docs.ai21.com",
401
+ baseURL: "https://api.ai21.com/studio/v1",
402
+ envVarPrefix: "AI21",
403
+ requiredEnvVars: ["AI21_API_KEY"]
404
+ },
405
+ {
406
+ id: "upstage",
407
+ name: "Upstage (Solar)",
408
+ category: "\u76F4\u63A5\u5B9E\u9A8C\u5BA4",
409
+ status: "cn-compatible",
410
+ docs: "https://developers.upstage.ai/docs",
411
+ baseURL: "https://api.upstage.ai/v1/solar",
412
+ envVarPrefix: "UPSTAGE",
413
+ requiredEnvVars: ["UPSTAGE_API_KEY"]
414
+ },
415
+ // ─── 国内平台 ───────────────────────────────────────────────
416
+ {
417
+ id: "siliconflow",
418
+ name: "\u7845\u57FA\u6D41\u52A8 SiliconFlow",
419
+ category: "\u56FD\u5185\u5E73\u53F0",
420
+ status: "cn-compatible",
421
+ docs: "https://docs.siliconflow.cn",
422
+ baseURL: "https://api.siliconflow.cn/v1",
423
+ envVarPrefix: "SILICONFLOW",
424
+ requiredEnvVars: ["SILICONFLOW_API_KEY"]
425
+ },
426
+ {
427
+ id: "volcengine-ark",
428
+ name: "\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",
429
+ category: "\u56FD\u5185\u5E73\u53F0",
430
+ status: "cn-compatible",
431
+ docs: "https://www.volcengine.com/docs/82379",
432
+ baseURL: "https://ark.cn-beijing.volces.com/api/v3",
433
+ envVarPrefix: "ARK",
434
+ requiredEnvVars: ["ARK_API_KEY"]
435
+ },
436
+ {
437
+ id: "bailian",
438
+ name: "\u767E\u70BC Bailian (\u901A\u4E49)",
439
+ category: "\u56FD\u5185\u5E73\u53F0",
440
+ status: "cn-compatible",
441
+ docs: "https://help.aliyun.com/zh/model-studio",
442
+ baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
443
+ envVarPrefix: "DASHSCOPE",
444
+ requiredEnvVars: ["DASHSCOPE_API_KEY"]
445
+ },
446
+ {
447
+ id: "zhipu",
448
+ name: "\u667A\u8C31 AI Zhipu",
449
+ category: "\u56FD\u5185\u5E73\u53F0",
450
+ status: "ai-sdk",
451
+ docs: "https://docs.bigmodel.cn",
452
+ baseURL: "https://open.bigmodel.cn/api/paas/v4",
453
+ envVarPrefix: "ZHIPU",
454
+ requiredEnvVars: ["ZHIPU_API_KEY"]
455
+ },
456
+ {
457
+ id: "baichuan",
458
+ name: "\u767E\u5DDD\u667A\u80FD Baichuan",
459
+ category: "\u56FD\u5185\u5E73\u53F0",
460
+ status: "cn-compatible",
461
+ docs: "https://platform.baichuan-ai.com/docs",
462
+ baseURL: "https://api.baichuan-ai.com/v1",
463
+ envVarPrefix: "BAICHUAN",
464
+ requiredEnvVars: ["BAICHUAN_API_KEY"]
465
+ },
466
+ {
467
+ id: "minimax",
468
+ name: "MiniMax (ABAB)",
469
+ category: "\u56FD\u5185\u5E73\u53F0",
470
+ status: "cn-compatible",
471
+ docs: "https://platform.minimaxi.com/document/guides",
472
+ baseURL: "https://api.minimax.chat/v1",
473
+ envVarPrefix: "MINIMAX",
474
+ requiredEnvVars: ["MINIMAX_API_KEY", "MINIMAX_GROUP_ID"]
475
+ },
476
+ {
477
+ id: "stepfun",
478
+ name: "\u9636\u8DC3\u661F\u8FB0 StepFun",
479
+ category: "\u56FD\u5185\u5E73\u53F0",
480
+ status: "cn-compatible",
481
+ docs: "https://platform.stepfun.com/docs",
482
+ baseURL: "https://api.stepfun.com/v1",
483
+ envVarPrefix: "STEPFUN",
484
+ requiredEnvVars: ["STEPFUN_API_KEY"]
485
+ },
486
+ {
487
+ id: "sensetime",
488
+ name: "\u5546\u6C64 SenseNova",
489
+ category: "\u56FD\u5185\u5E73\u53F0",
490
+ status: "cn-compatible",
491
+ docs: "https://platform.sensenova.cn/docs",
492
+ // Token Plan OpenAI-compatible surface (NOT enterprise api.sensenova.cn)
493
+ // Ref: https://github.com/OpenSenseNova/SenseNova6.7/blob/main/API_CN.md
494
+ baseURL: "https://token.sensenova.cn/v1",
495
+ envVarPrefix: "SENSENOVA",
496
+ requiredEnvVars: ["SENSENOVA_API_KEY"]
497
+ },
498
+ {
499
+ id: "tencent",
500
+ name: "\u817E\u8BAF\u6DF7\u5143 Hunyuan",
501
+ category: "\u56FD\u5185\u5E73\u53F0",
502
+ status: "cn-compatible",
503
+ docs: "https://cloud.tencent.com/document/product/1729",
504
+ baseURL: "https://api.hunyuan.cloud.tencent.com/v1",
505
+ envVarPrefix: "HUNYUAN",
506
+ requiredEnvVars: ["HUNYUAN_API_KEY"]
507
+ },
508
+ {
509
+ id: "lingyi",
510
+ name: "\u96F6\u4E00\u4E07\u7269 Yi",
511
+ category: "\u56FD\u5185\u5E73\u53F0",
512
+ status: "cn-compatible",
513
+ docs: "https://platform.lingyiwanwu.com/docs",
514
+ baseURL: "https://api.lingyiwanwu.com/v1",
515
+ envVarPrefix: "YI",
516
+ requiredEnvVars: ["YI_API_KEY"]
517
+ },
518
+ // ─── 统一网关 ───────────────────────────────────────────────
519
+ {
520
+ id: "openrouter",
521
+ name: "OpenRouter",
522
+ category: "\u7EDF\u4E00\u7F51\u5173",
523
+ status: "opencode",
524
+ docs: "https://openrouter.ai/docs",
525
+ envVarPrefix: "OPENROUTER",
526
+ requiredEnvVars: ["OPENROUTER_API_KEY"]
527
+ },
528
+ {
529
+ id: "vercel-gateway",
530
+ name: "Vercel AI Gateway",
531
+ category: "\u7EDF\u4E00\u7F51\u5173",
532
+ status: "opencode",
533
+ docs: "https://vercel.com/docs/ai-gateway",
534
+ envVarPrefix: "AI_GATEWAY",
535
+ requiredEnvVars: ["AI_GATEWAY_API_KEY"]
536
+ },
537
+ {
538
+ id: "litellm",
539
+ name: "LiteLLM Proxy",
540
+ category: "\u7EDF\u4E00\u7F51\u5173",
541
+ status: "cn-compatible",
542
+ docs: "https://docs.litellm.ai",
543
+ baseURL: "http://localhost:4000/v1",
544
+ envVarPrefix: "LITELLM",
545
+ requiredEnvVars: ["LITELLM_API_KEY"]
546
+ },
547
+ {
548
+ id: "portkey",
549
+ name: "Portkey",
550
+ category: "\u7EDF\u4E00\u7F51\u5173",
551
+ status: "cn-compatible",
552
+ docs: "https://portkey.ai/docs",
553
+ baseURL: "https://api.portkey.ai/v1",
554
+ envVarPrefix: "PORTKEY",
555
+ requiredEnvVars: ["PORTKEY_API_KEY"]
556
+ },
557
+ {
558
+ id: "aws-bedrock",
559
+ name: "AWS Bedrock",
560
+ category: "\u4E91\u5E73\u53F0",
561
+ status: "ai-sdk",
562
+ docs: "https://aws.amazon.com/bedrock/",
563
+ envVarPrefix: "AWS_BEDROCK",
564
+ requiredEnvVars: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"]
565
+ },
566
+ {
567
+ id: "azure-openai",
568
+ name: "Azure OpenAI",
569
+ category: "\u4E91\u5E73\u53F0",
570
+ status: "ai-sdk",
571
+ docs: "https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",
572
+ envVarPrefix: "AZURE_OPENAI",
573
+ requiredEnvVars: ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"]
574
+ },
575
+ {
576
+ id: "gcp-vertex",
577
+ name: "GCP Vertex AI",
578
+ category: "\u4E91\u5E73\u53F0",
579
+ status: "ai-sdk",
580
+ docs: "https://cloud.google.com/vertex-ai",
581
+ envVarPrefix: "GOOGLE_VERTEX",
582
+ requiredEnvVars: ["GOOGLE_APPLICATION_CREDENTIALS"]
583
+ },
584
+ // ─── 推理加速 ───────────────────────────────────────────────
585
+ {
586
+ id: "groq",
587
+ name: "Groq",
588
+ category: "\u63A8\u7406\u52A0\u901F",
589
+ status: "opencode",
590
+ docs: "https://console.groq.com/docs",
591
+ envVarPrefix: "GROQ",
592
+ requiredEnvVars: ["GROQ_API_KEY"]
593
+ },
594
+ {
595
+ id: "fireworks",
596
+ name: "Fireworks AI",
597
+ category: "\u63A8\u7406\u52A0\u901F",
598
+ status: "opencode",
599
+ docs: "https://docs.fireworks.ai",
600
+ envVarPrefix: "FIREWORKS",
601
+ requiredEnvVars: ["FIREWORKS_API_KEY"]
602
+ },
603
+ {
604
+ id: "together",
605
+ name: "Together AI",
606
+ category: "\u63A8\u7406\u52A0\u901F",
607
+ status: "opencode",
608
+ docs: "https://docs.together.ai",
609
+ envVarPrefix: "TOGETHER",
610
+ requiredEnvVars: ["TOGETHER_API_KEY"]
611
+ },
612
+ {
613
+ id: "huggingface",
614
+ name: "Hugging Face",
615
+ category: "\u63A8\u7406\u52A0\u901F",
616
+ status: "opencode",
617
+ docs: "https://huggingface.co/docs/inference-providers",
618
+ envVarPrefix: "HF",
619
+ requiredEnvVars: ["HF_API_KEY"]
620
+ },
621
+ {
622
+ id: "replicate",
623
+ name: "Replicate",
624
+ category: "\u63A8\u7406\u52A0\u901F",
625
+ status: "cn-compatible",
626
+ docs: "https://replicate.com/docs",
627
+ envVarPrefix: "REPLICATE",
628
+ requiredEnvVars: ["REPLICATE_API_TOKEN"]
629
+ },
630
+ {
631
+ id: "lepton",
632
+ name: "Lepton AI",
633
+ category: "\u63A8\u7406\u52A0\u901F",
634
+ status: "cn-compatible",
635
+ docs: "https://www.lepton.ai/docs",
636
+ baseURL: "https://api.lepton.ai/v1",
637
+ envVarPrefix: "LEPTON",
638
+ requiredEnvVars: ["LEPTON_API_KEY"]
639
+ },
640
+ {
641
+ id: "anyscale",
642
+ name: "Anyscale Endpoints",
643
+ category: "\u63A8\u7406\u52A0\u901F",
644
+ status: "cn-compatible",
645
+ docs: "https://docs.endpoints.anyscale.com/",
646
+ baseURL: "https://api.endpoints.anyscale.com/v1",
647
+ envVarPrefix: "ANYSCALE",
648
+ requiredEnvVars: ["ANYSCALE_API_KEY"]
649
+ },
650
+ {
651
+ id: "octoai",
652
+ name: "OctoAI",
653
+ category: "\u63A8\u7406\u52A0\u901F",
654
+ status: "cn-compatible",
655
+ docs: "https://octoai.cloud/docs",
656
+ baseURL: "https://text.octoai.run/v1",
657
+ envVarPrefix: "OCTOAI",
658
+ requiredEnvVars: ["OCTOAI_TOKEN"]
659
+ },
660
+ {
661
+ id: "deepinfra",
662
+ name: "DeepInfra",
663
+ category: "\u63A8\u7406\u52A0\u901F",
664
+ status: "cn-compatible",
665
+ docs: "https://deepinfra.com/docs",
666
+ baseURL: "https://api.deepinfra.com/v1/openai",
667
+ envVarPrefix: "DEEPINFRA",
668
+ requiredEnvVars: ["DEEPINFRA_API_KEY"]
669
+ },
670
+ {
671
+ id: "novita",
672
+ name: "Novita AI",
673
+ category: "\u63A8\u7406\u52A0\u901F",
674
+ status: "cn-compatible",
675
+ docs: "https://novita.ai/docs",
676
+ baseURL: "https://api.novita.ai/v3/openai",
677
+ envVarPrefix: "NOVITA",
678
+ requiredEnvVars: ["NOVITA_API_KEY"]
679
+ },
680
+ // ─── 多模态 ───────────────────────────────────────────────
681
+ {
682
+ id: "black-forest-labs",
683
+ name: "Black Forest Labs (FLUX)",
684
+ category: "\u591A\u6A21\u6001",
685
+ status: "ai-sdk",
686
+ docs: "https://docs.bfl.ml",
687
+ envVarPrefix: "BFL",
688
+ requiredEnvVars: ["BFL_API_KEY"]
689
+ },
690
+ {
691
+ id: "elevenlabs",
692
+ name: "ElevenLabs (TTS)",
693
+ category: "\u591A\u6A21\u6001",
694
+ status: "ai-sdk",
695
+ docs: "https://elevenlabs.io/docs",
696
+ envVarPrefix: "ELEVENLABS",
697
+ requiredEnvVars: ["ELEVENLABS_API_KEY"]
698
+ },
699
+ {
700
+ id: "runway",
701
+ name: "Runway (Video)",
702
+ category: "\u591A\u6A21\u6001",
703
+ status: "pending",
704
+ docs: "https://runwayml.com",
705
+ envVarPrefix: "RUNWAY",
706
+ requiredEnvVars: ["RUNWAY_API_KEY"]
707
+ },
708
+ {
709
+ id: "luma",
710
+ name: "Luma (Video)",
711
+ category: "\u591A\u6A21\u6001",
712
+ status: "pending",
713
+ docs: "https://lumalabs.ai",
714
+ envVarPrefix: "LUMA",
715
+ requiredEnvVars: ["LUMA_API_KEY"]
716
+ },
717
+ {
718
+ id: "midjourney",
719
+ name: "Midjourney",
720
+ category: "\u591A\u6A21\u6001",
721
+ status: "pending",
722
+ docs: "https://docs.midjourney.com",
723
+ envVarPrefix: "MIDJOURNEY",
724
+ requiredEnvVars: []
725
+ },
726
+ // ─── 本地部署 ───────────────────────────────────────────────
727
+ {
728
+ id: "ollama",
729
+ name: "Ollama (\u672C\u5730)",
730
+ category: "\u672C\u5730\u90E8\u7F72",
731
+ status: "opencode",
732
+ docs: "https://github.com/ollama/ollama",
733
+ baseURL: "http://localhost:11434/v1",
734
+ envVarPrefix: "OLLAMA",
735
+ requiredEnvVars: ["OLLAMA_BASE_URL"]
736
+ },
737
+ {
738
+ id: "lmstudio",
739
+ name: "LM Studio",
740
+ category: "\u672C\u5730\u90E8\u7F72",
741
+ status: "cn-compatible",
742
+ docs: "https://lmstudio.ai/docs",
743
+ baseURL: "http://localhost:1234/v1",
744
+ envVarPrefix: "LMSTUDIO",
745
+ requiredEnvVars: []
746
+ },
747
+ {
748
+ id: "vllm",
749
+ name: "vLLM",
750
+ category: "\u672C\u5730\u90E8\u7F72",
751
+ status: "cn-compatible",
752
+ docs: "https://docs.vllm.ai",
753
+ baseURL: "http://localhost:8000/v1",
754
+ envVarPrefix: "VLLM",
755
+ requiredEnvVars: []
756
+ },
757
+ {
758
+ id: "tgi",
759
+ name: "HF TGI",
760
+ category: "\u672C\u5730\u90E8\u7F72",
761
+ status: "cn-compatible",
762
+ docs: "https://huggingface.co/docs/text-generation-inference",
763
+ baseURL: "http://localhost:8080/v1",
764
+ envVarPrefix: "TGI",
765
+ requiredEnvVars: []
766
+ }
767
+ ];
768
+ var PROVIDERS_BY_CATEGORY = PROVIDERS.reduce(
769
+ (acc, p) => {
770
+ if (!acc[p.category]) acc[p.category] = [];
771
+ acc[p.category].push(p);
772
+ return acc;
773
+ },
774
+ {}
775
+ );
776
+ function getProvider(id) {
777
+ return PROVIDERS.find((p) => p.id === id);
778
+ }
779
+ var isCNCompatible = (id) => getProvider(id)?.status === "cn-compatible";
780
+
781
+ export { FRONTIER_FALLBACK, PROVIDERS, PROVIDERS_BY_CATEGORY, PROVIDER_MAP, getModelInfo, getProvider, isCNCompatible, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
782
+ //# sourceMappingURL=index.js.map
783
+ //# sourceMappingURL=index.js.map