provider-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/package.json +19 -0
- package/src/core/persistent-config.js +107 -0
- package/src/index.js +16 -0
- package/src/providers/anthropic-adapter.js +368 -0
- package/src/providers/azure-adapter.js +323 -0
- package/src/providers/bedrock-adapter.js +388 -0
- package/src/providers/cohere-adapter.js +319 -0
- package/src/providers/gemini-adapter.js +282 -0
- package/src/providers/local-provider.js +185 -0
- package/src/providers/openai-compatible.js +840 -0
- package/src/providers/provider-error-adapter.js +100 -0
- package/src/providers/provider-manager.js +321 -0
- package/src/providers/provider-registry.js +316 -0
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Compatible Provider
|
|
3
|
+
*
|
|
4
|
+
* 统一适配器 - 支持 90% 的 AI 服务商
|
|
5
|
+
*
|
|
6
|
+
* 兼容的服务商:
|
|
7
|
+
* - OpenAI, DeepSeek, 智谱GLM, 通义千问, SiliconFlow
|
|
8
|
+
* - OpenRouter, Groq, xAI, Moonshot, Ollama, LM Studio
|
|
9
|
+
* - 以及所有支持 OpenAI API 格式的服务商
|
|
10
|
+
*
|
|
11
|
+
* 使用方式:
|
|
12
|
+
* const provider = new OpenAICompatibleProvider({
|
|
13
|
+
* id: 'deepseek',
|
|
14
|
+
* name: 'DeepSeek',
|
|
15
|
+
* baseUrl: 'https://api.deepseek.com/v1',
|
|
16
|
+
* apiKey: 'sk-xxx'
|
|
17
|
+
* });
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export class OpenAICompatibleProvider {
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.id = config.id || 'openai-compatible';
|
|
23
|
+
this.name = config.name || config.id || 'Unknown';
|
|
24
|
+
this.nameCn = config.nameCn || this.name;
|
|
25
|
+
this.baseUrl = config.baseUrl || 'https://api.openai.com/v1';
|
|
26
|
+
this.apiKey = config.apiKey || null;
|
|
27
|
+
this.defaultModel = config.defaultModel || null;
|
|
28
|
+
this.models = config.models || [];
|
|
29
|
+
this.connected = false;
|
|
30
|
+
this.description = config.description || '';
|
|
31
|
+
|
|
32
|
+
// 可选配置
|
|
33
|
+
this.timeout = config.timeout || 60000;
|
|
34
|
+
this.headers = config.headers || {};
|
|
35
|
+
this.skipAuth = config.skipAuth || false; // Ollama 等本地服务不需要 auth
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 连接/验证
|
|
40
|
+
*/
|
|
41
|
+
async connect(apiKey) {
|
|
42
|
+
if (apiKey) this.apiKey = apiKey;
|
|
43
|
+
|
|
44
|
+
if (!this.skipAuth && !this.apiKey) {
|
|
45
|
+
throw new Error(`API Key required for ${this.name}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 尝试获取模型列表来验证连接
|
|
49
|
+
try {
|
|
50
|
+
await this.fetchModels();
|
|
51
|
+
this.connected = true;
|
|
52
|
+
return true;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// 如果获取模型失败,但 API Key 存在,也认为连接成功
|
|
55
|
+
if (this.apiKey || this.skipAuth) {
|
|
56
|
+
this.connected = true;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
throw e;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 断开连接
|
|
65
|
+
*/
|
|
66
|
+
disconnect() {
|
|
67
|
+
this.connected = false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 发送聊天消息
|
|
72
|
+
*/
|
|
73
|
+
async chat(model, messages, options = {}) {
|
|
74
|
+
if (!this.connected && !this.skipAuth) {
|
|
75
|
+
throw new Error(`Provider ${this.name} not connected`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
79
|
+
|
|
80
|
+
const body = {
|
|
81
|
+
model: model || this.defaultModel,
|
|
82
|
+
messages,
|
|
83
|
+
stream: options.stream || false,
|
|
84
|
+
...options.extra
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// 支持 Function Calling
|
|
88
|
+
if (options.tools && options.tools.length > 0) {
|
|
89
|
+
body.tools = options.tools;
|
|
90
|
+
if (options.tool_choice) {
|
|
91
|
+
body.tool_choice = options.tool_choice;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const headers = {
|
|
96
|
+
'Content-Type': 'application/json',
|
|
97
|
+
...this.getAuthHeader(),
|
|
98
|
+
...this.headers
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const response = await fetch(url, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers,
|
|
104
|
+
body: JSON.stringify(body),
|
|
105
|
+
signal: this.timeout ? AbortSignal.timeout(this.timeout) : undefined
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
const error = await response.json().catch(() => ({}));
|
|
110
|
+
throw new Error(error.error?.message || `${this.name} API error: ${response.status}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const data = await response.json();
|
|
114
|
+
const choice = data.choices?.[0];
|
|
115
|
+
|
|
116
|
+
// 处理 Function Calling 响应
|
|
117
|
+
const result = {
|
|
118
|
+
content: choice?.message?.content || '',
|
|
119
|
+
model: data.model,
|
|
120
|
+
usage: data.usage,
|
|
121
|
+
raw: data
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// 如果有工具调用,解析并返回
|
|
125
|
+
if (choice?.message?.tool_calls && choice.message.tool_calls.length > 0) {
|
|
126
|
+
result.toolCalls = choice.message.tool_calls.map(tc => ({
|
|
127
|
+
id: tc.id,
|
|
128
|
+
name: tc.function.name,
|
|
129
|
+
arguments: tc.function.arguments
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 流式聊天
|
|
138
|
+
*/
|
|
139
|
+
async *chatStream(model, messages, options = {}) {
|
|
140
|
+
if (!this.connected && !this.skipAuth) {
|
|
141
|
+
throw new Error(`Provider ${this.name} not connected`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
145
|
+
|
|
146
|
+
const body = {
|
|
147
|
+
model: model || this.defaultModel,
|
|
148
|
+
messages,
|
|
149
|
+
stream: true,
|
|
150
|
+
...options.extra
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// 支持 Function Calling (部分模型支持流式 FC)
|
|
154
|
+
if (options.tools && options.tools.length > 0) {
|
|
155
|
+
body.tools = options.tools;
|
|
156
|
+
if (options.tool_choice) {
|
|
157
|
+
body.tool_choice = options.tool_choice;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const headers = {
|
|
162
|
+
'Content-Type': 'application/json',
|
|
163
|
+
...this.getAuthHeader(),
|
|
164
|
+
...this.headers
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const response = await fetch(url, {
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers,
|
|
170
|
+
body: JSON.stringify(body),
|
|
171
|
+
signal: this.timeout ? AbortSignal.timeout(this.timeout) : undefined
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
const error = await response.json().catch(() => ({}));
|
|
176
|
+
throw new Error(error.error?.message || `${this.name} API error: ${response.status}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const reader = response.body.getReader();
|
|
180
|
+
const decoder = new TextDecoder();
|
|
181
|
+
let buffer = '';
|
|
182
|
+
|
|
183
|
+
// 用于累积工具调用
|
|
184
|
+
const toolCallChunks = new Map();
|
|
185
|
+
|
|
186
|
+
while (true) {
|
|
187
|
+
const { done, value } = await reader.read();
|
|
188
|
+
if (done) break;
|
|
189
|
+
|
|
190
|
+
buffer += decoder.decode(value, { stream: true });
|
|
191
|
+
const lines = buffer.split('\n');
|
|
192
|
+
buffer = lines.pop() || '';
|
|
193
|
+
|
|
194
|
+
for (const line of lines) {
|
|
195
|
+
if (line.startsWith('data: ')) {
|
|
196
|
+
const data = line.slice(6);
|
|
197
|
+
if (data === '[DONE]') {
|
|
198
|
+
// 处理累积的工具调用
|
|
199
|
+
if (toolCallChunks.size > 0) {
|
|
200
|
+
const toolCalls = Array.from(toolCallChunks.values()).map(tc => ({
|
|
201
|
+
id: tc.id,
|
|
202
|
+
name: tc.name,
|
|
203
|
+
arguments: tc.arguments
|
|
204
|
+
}));
|
|
205
|
+
yield { type: 'tool_calls', toolCalls, done: false };
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const json = JSON.parse(data);
|
|
212
|
+
const delta = json.choices?.[0]?.delta;
|
|
213
|
+
|
|
214
|
+
// 处理内容
|
|
215
|
+
if (delta?.content) {
|
|
216
|
+
yield { type: 'content', content: delta.content, done: false };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 处理流式工具调用
|
|
220
|
+
if (delta?.tool_calls) {
|
|
221
|
+
for (const tc of delta.tool_calls) {
|
|
222
|
+
const idx = tc.index || 0;
|
|
223
|
+
if (!toolCallChunks.has(idx)) {
|
|
224
|
+
toolCallChunks.set(idx, { id: '', name: '', arguments: '' });
|
|
225
|
+
}
|
|
226
|
+
const chunk = toolCallChunks.get(idx);
|
|
227
|
+
if (tc.id) chunk.id = tc.id;
|
|
228
|
+
if (tc.function?.name) chunk.name = tc.function.name;
|
|
229
|
+
if (tc.function?.arguments) chunk.arguments += tc.function.arguments;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} catch (e) {
|
|
233
|
+
// 忽略解析错误
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
yield { done: true };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 获取模型列表
|
|
244
|
+
*/
|
|
245
|
+
async fetchModels() {
|
|
246
|
+
const url = `${this.baseUrl}/models`;
|
|
247
|
+
|
|
248
|
+
const headers = {
|
|
249
|
+
'Content-Type': 'application/json',
|
|
250
|
+
...this.getAuthHeader(),
|
|
251
|
+
...this.headers
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const response = await fetch(url, {
|
|
255
|
+
method: 'GET',
|
|
256
|
+
headers,
|
|
257
|
+
signal: AbortSignal.timeout(10000)
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
throw new Error(`Failed to fetch models: ${response.status}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const data = await response.json();
|
|
265
|
+
|
|
266
|
+
// 解析模型列表
|
|
267
|
+
this.models = (data.data || data.models || [])
|
|
268
|
+
.map(m => typeof m === 'string' ? m : (m.id || m.name))
|
|
269
|
+
.filter(Boolean)
|
|
270
|
+
.sort();
|
|
271
|
+
|
|
272
|
+
return this.models;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 获取模型列表(本地缓存)
|
|
277
|
+
*/
|
|
278
|
+
getModels() {
|
|
279
|
+
return this.models;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 获取认证头
|
|
284
|
+
*/
|
|
285
|
+
getAuthHeader() {
|
|
286
|
+
if (this.skipAuth || !this.apiKey) {
|
|
287
|
+
return {};
|
|
288
|
+
}
|
|
289
|
+
return { 'Authorization': `Bearer ${this.apiKey}` };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Embedding API
|
|
294
|
+
*/
|
|
295
|
+
async embeddings(input, model = 'text-embedding-3-small') {
|
|
296
|
+
if (!this.connected && !this.skipAuth) {
|
|
297
|
+
throw new Error(`Provider ${this.name} not connected`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const url = `${this.baseUrl}/embeddings`;
|
|
301
|
+
|
|
302
|
+
const body = {
|
|
303
|
+
model,
|
|
304
|
+
input: Array.isArray(input) ? input : [input]
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const headers = {
|
|
308
|
+
'Content-Type': 'application/json',
|
|
309
|
+
...this.getAuthHeader(),
|
|
310
|
+
...this.headers
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const response = await fetch(url, {
|
|
314
|
+
method: 'POST',
|
|
315
|
+
headers,
|
|
316
|
+
body: JSON.stringify(body)
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (!response.ok) {
|
|
320
|
+
const error = await response.json().catch(() => ({}));
|
|
321
|
+
throw new Error(error.error?.message || `Embedding API error: ${response.status}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const data = await response.json();
|
|
325
|
+
return data.data.map(d => d.embedding);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 获取状态
|
|
330
|
+
*/
|
|
331
|
+
getStatus() {
|
|
332
|
+
return {
|
|
333
|
+
id: this.id,
|
|
334
|
+
name: this.name,
|
|
335
|
+
nameCn: this.nameCn,
|
|
336
|
+
baseUrl: this.baseUrl,
|
|
337
|
+
connected: this.connected,
|
|
338
|
+
modelCount: this.models.length,
|
|
339
|
+
defaultModel: this.defaultModel,
|
|
340
|
+
hasApiKey: !!this.apiKey
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* 预设 Provider 配置
|
|
347
|
+
* 只需要 baseUrl,其他用默认值
|
|
348
|
+
*/
|
|
349
|
+
export const PRESET_PROVIDERS = {
|
|
350
|
+
// ═══════════════════════════════════════════════════════════
|
|
351
|
+
// 国际主流服务商
|
|
352
|
+
// ═══════════════════════════════════════════════════════════
|
|
353
|
+
openai: {
|
|
354
|
+
name: 'OpenAI',
|
|
355
|
+
nameCn: 'OpenAI',
|
|
356
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
357
|
+
defaultModel: 'gpt-4o',
|
|
358
|
+
description: 'GPT-4, GPT-3.5, DALL-E, Whisper'
|
|
359
|
+
},
|
|
360
|
+
anthropic: {
|
|
361
|
+
name: 'Claude',
|
|
362
|
+
nameCn: 'Anthropic Claude',
|
|
363
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
364
|
+
defaultModel: 'claude-sonnet-4-20250514',
|
|
365
|
+
description: 'Claude 3.5/4 系列',
|
|
366
|
+
special: true
|
|
367
|
+
},
|
|
368
|
+
google: {
|
|
369
|
+
name: 'Gemini',
|
|
370
|
+
nameCn: 'Google Gemini',
|
|
371
|
+
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
372
|
+
defaultModel: 'gemini-2.0-flash',
|
|
373
|
+
description: 'Gemini 2.0/1.5 Pro/Flash'
|
|
374
|
+
},
|
|
375
|
+
deepseek: {
|
|
376
|
+
name: 'DeepSeek',
|
|
377
|
+
nameCn: 'DeepSeek',
|
|
378
|
+
baseUrl: 'https://api.deepseek.com/v1',
|
|
379
|
+
defaultModel: 'deepseek-chat',
|
|
380
|
+
description: 'DeepSeek-V3, DeepSeek-Reasoner'
|
|
381
|
+
},
|
|
382
|
+
openrouter: {
|
|
383
|
+
name: 'OpenRouter',
|
|
384
|
+
nameCn: 'OpenRouter',
|
|
385
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
386
|
+
defaultModel: 'openrouter/auto',
|
|
387
|
+
description: '200+ 模型聚合平台'
|
|
388
|
+
},
|
|
389
|
+
groq: {
|
|
390
|
+
name: 'Groq',
|
|
391
|
+
nameCn: 'Groq',
|
|
392
|
+
baseUrl: 'https://api.groq.com/openai/v1',
|
|
393
|
+
defaultModel: 'llama-3.3-70b-versatile',
|
|
394
|
+
description: '极速推理,LPU芯片'
|
|
395
|
+
},
|
|
396
|
+
xai: {
|
|
397
|
+
name: 'xAI',
|
|
398
|
+
nameCn: 'xAI (Grok)',
|
|
399
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
400
|
+
defaultModel: 'grok-2-latest',
|
|
401
|
+
description: 'Elon Musk 的 AI 公司'
|
|
402
|
+
},
|
|
403
|
+
mistral: {
|
|
404
|
+
name: 'Mistral',
|
|
405
|
+
nameCn: 'Mistral AI',
|
|
406
|
+
baseUrl: 'https://api.mistral.ai/v1',
|
|
407
|
+
defaultModel: 'mistral-large-latest',
|
|
408
|
+
description: '欧洲开源模型领导者'
|
|
409
|
+
},
|
|
410
|
+
cohere: {
|
|
411
|
+
name: 'Cohere',
|
|
412
|
+
nameCn: 'Cohere',
|
|
413
|
+
baseUrl: 'https://api.cohere.ai/v1',
|
|
414
|
+
defaultModel: 'command-r-plus',
|
|
415
|
+
description: '企业级 AI,擅长 RAG'
|
|
416
|
+
},
|
|
417
|
+
replicate: {
|
|
418
|
+
name: 'Replicate',
|
|
419
|
+
nameCn: 'Replicate',
|
|
420
|
+
baseUrl: 'https://api.replicate.com/v1',
|
|
421
|
+
defaultModel: 'meta/llama-2-70b-chat',
|
|
422
|
+
description: '云端运行开源模型'
|
|
423
|
+
},
|
|
424
|
+
together: {
|
|
425
|
+
name: 'Together',
|
|
426
|
+
nameCn: 'Together AI',
|
|
427
|
+
baseUrl: 'https://api.together.xyz/v1',
|
|
428
|
+
defaultModel: 'meta-llama/Llama-3-70b-chat-hf',
|
|
429
|
+
description: '开源模型云服务'
|
|
430
|
+
},
|
|
431
|
+
perplexity: {
|
|
432
|
+
name: 'Perplexity',
|
|
433
|
+
nameCn: 'Perplexity',
|
|
434
|
+
baseUrl: 'https://api.perplexity.ai',
|
|
435
|
+
defaultModel: 'llama-3.1-sonar-small-128k-online',
|
|
436
|
+
description: 'AI 搜索引擎'
|
|
437
|
+
},
|
|
438
|
+
fireworks: {
|
|
439
|
+
name: 'Fireworks',
|
|
440
|
+
nameCn: 'Fireworks AI',
|
|
441
|
+
baseUrl: 'https://api.fireworks.ai/inference/v1',
|
|
442
|
+
defaultModel: 'accounts/fireworks/models/llama-v3-70b',
|
|
443
|
+
description: '高速推理平台'
|
|
444
|
+
},
|
|
445
|
+
anyscale: {
|
|
446
|
+
name: 'Anyscale',
|
|
447
|
+
nameCn: 'Anyscale',
|
|
448
|
+
baseUrl: 'https://api.endpoints.anyscale.com/v1',
|
|
449
|
+
defaultModel: 'meta-llama/Llama-2-70b-chat-hf',
|
|
450
|
+
description: 'Ray 团队出品'
|
|
451
|
+
},
|
|
452
|
+
octoai: {
|
|
453
|
+
name: 'OctoAI',
|
|
454
|
+
nameCn: 'OctoAI',
|
|
455
|
+
baseUrl: 'https://text.octoai.run/v1',
|
|
456
|
+
defaultModel: 'meta-llama-3-70b-instruct',
|
|
457
|
+
description: '高效模型服务'
|
|
458
|
+
},
|
|
459
|
+
lepton: {
|
|
460
|
+
name: 'Lepton',
|
|
461
|
+
nameCn: 'Lepton AI',
|
|
462
|
+
baseUrl: 'https://api.lepton.ai/v1',
|
|
463
|
+
defaultModel: 'llama3-70b',
|
|
464
|
+
description: '一键部署 AI 应用'
|
|
465
|
+
},
|
|
466
|
+
predibase: {
|
|
467
|
+
name: 'Predibase',
|
|
468
|
+
nameCn: 'Predibase',
|
|
469
|
+
baseUrl: 'https://api.predibase.com/v1',
|
|
470
|
+
defaultModel: 'llama-2-70b',
|
|
471
|
+
description: '微调和部署平台'
|
|
472
|
+
},
|
|
473
|
+
nomic: {
|
|
474
|
+
name: 'Nomic',
|
|
475
|
+
nameCn: 'Nomic AI',
|
|
476
|
+
baseUrl: 'https://api-atlas.nomic.ai/v1',
|
|
477
|
+
defaultModel: 'nomic-embed-text-v1',
|
|
478
|
+
description: '向量嵌入专家'
|
|
479
|
+
},
|
|
480
|
+
voyage: {
|
|
481
|
+
name: 'Voyage',
|
|
482
|
+
nameCn: 'Voyage AI',
|
|
483
|
+
baseUrl: 'https://api.voyageai.com/v1',
|
|
484
|
+
defaultModel: 'voyage-large-2',
|
|
485
|
+
description: '高质量嵌入模型'
|
|
486
|
+
},
|
|
487
|
+
alephalpha: {
|
|
488
|
+
name: 'AlephAlpha',
|
|
489
|
+
nameCn: 'Aleph Alpha',
|
|
490
|
+
baseUrl: 'https://api.aleph-alpha.com/v1',
|
|
491
|
+
defaultModel: 'luminous-supreme',
|
|
492
|
+
description: '欧洲企业 AI'
|
|
493
|
+
},
|
|
494
|
+
ai21: {
|
|
495
|
+
name: 'AI21',
|
|
496
|
+
nameCn: 'AI21 Labs',
|
|
497
|
+
baseUrl: 'https://api.ai21.com/v1',
|
|
498
|
+
defaultModel: 'jamba-1-5-large',
|
|
499
|
+
description: 'Jamba 混合架构模型'
|
|
500
|
+
},
|
|
501
|
+
inflection: {
|
|
502
|
+
name: 'Inflection',
|
|
503
|
+
nameCn: 'Inflection AI',
|
|
504
|
+
baseUrl: 'https://api.inflection.ai/v1',
|
|
505
|
+
defaultModel: 'inflection-3-pi',
|
|
506
|
+
description: 'Pi 助手'
|
|
507
|
+
},
|
|
508
|
+
reka: {
|
|
509
|
+
name: 'Reka',
|
|
510
|
+
nameCn: 'Reka AI',
|
|
511
|
+
baseUrl: 'https://api.reka.ai/v1',
|
|
512
|
+
defaultModel: 'reka-core',
|
|
513
|
+
description: '多模态模型'
|
|
514
|
+
},
|
|
515
|
+
databricks: {
|
|
516
|
+
name: 'Databricks',
|
|
517
|
+
nameCn: 'Databricks',
|
|
518
|
+
baseUrl: 'https://models.databricks.com/v1',
|
|
519
|
+
defaultModel: 'databricks-dbrx-instruct',
|
|
520
|
+
description: '企业数据 AI'
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
// ═══════════════════════════════════════════════════════════
|
|
524
|
+
// 云服务商 AI
|
|
525
|
+
// ═══════════════════════════════════════════════════════════
|
|
526
|
+
azure: {
|
|
527
|
+
name: 'Azure',
|
|
528
|
+
nameCn: 'Azure OpenAI',
|
|
529
|
+
baseUrl: 'https://YOUR_RESOURCE.openai.azure.com/openai/deployments',
|
|
530
|
+
defaultModel: 'gpt-4o',
|
|
531
|
+
description: '微软 Azure 托管 OpenAI',
|
|
532
|
+
special: true
|
|
533
|
+
},
|
|
534
|
+
aws_bedrock: {
|
|
535
|
+
name: 'Bedrock',
|
|
536
|
+
nameCn: 'AWS Bedrock',
|
|
537
|
+
baseUrl: 'https://bedrock-runtime.us-east-1.amazonaws.com/v1',
|
|
538
|
+
defaultModel: 'anthropic.claude-3-sonnet',
|
|
539
|
+
description: 'AWS 托管模型服务',
|
|
540
|
+
special: true
|
|
541
|
+
},
|
|
542
|
+
vertex: {
|
|
543
|
+
name: 'Vertex',
|
|
544
|
+
nameCn: 'Google Vertex AI',
|
|
545
|
+
baseUrl: 'https://us-central1-aiplatform.googleapis.com/v1',
|
|
546
|
+
defaultModel: 'gemini-pro',
|
|
547
|
+
description: 'GCP 托管模型服务',
|
|
548
|
+
special: true
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
// ═══════════════════════════════════════════════════════════
|
|
552
|
+
// 国内服务商
|
|
553
|
+
// ═══════════════════════════════════════════════════════════
|
|
554
|
+
zhipu: {
|
|
555
|
+
name: 'GLM',
|
|
556
|
+
nameCn: '智谱GLM',
|
|
557
|
+
baseUrl: 'https://open.bigmodel.cn/api/paas/v4',
|
|
558
|
+
defaultModel: 'glm-4',
|
|
559
|
+
description: '清华系,GLM-4 系列'
|
|
560
|
+
},
|
|
561
|
+
alibaba: {
|
|
562
|
+
name: 'Qwen',
|
|
563
|
+
nameCn: '通义千问',
|
|
564
|
+
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
565
|
+
defaultModel: 'qwen-turbo',
|
|
566
|
+
description: '阿里云,Qwen 系列'
|
|
567
|
+
},
|
|
568
|
+
baidu: {
|
|
569
|
+
name: 'Qianfan',
|
|
570
|
+
nameCn: '百度千帆',
|
|
571
|
+
baseUrl: 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop',
|
|
572
|
+
defaultModel: 'ernie-4.0-8k',
|
|
573
|
+
description: '文心一言系列',
|
|
574
|
+
special: true
|
|
575
|
+
},
|
|
576
|
+
moonshot: {
|
|
577
|
+
name: 'Moonshot',
|
|
578
|
+
nameCn: 'Moonshot (Kimi)',
|
|
579
|
+
baseUrl: 'https://api.moonshot.cn/v1',
|
|
580
|
+
defaultModel: 'moonshot-v1-8k',
|
|
581
|
+
description: '长文本专家'
|
|
582
|
+
},
|
|
583
|
+
minimax: {
|
|
584
|
+
name: 'MiniMax',
|
|
585
|
+
nameCn: 'MiniMax',
|
|
586
|
+
baseUrl: 'https://api.minimax.chat/v1',
|
|
587
|
+
defaultModel: 'abab6.5s-chat',
|
|
588
|
+
description: '海螺AI'
|
|
589
|
+
},
|
|
590
|
+
siliconflow: {
|
|
591
|
+
name: 'SiliconFlow',
|
|
592
|
+
nameCn: '硅基流动',
|
|
593
|
+
baseUrl: 'https://api.siliconflow.cn/v1',
|
|
594
|
+
defaultModel: 'Qwen/Qwen2.5-72B-Instruct',
|
|
595
|
+
description: '国内模型聚合平台'
|
|
596
|
+
},
|
|
597
|
+
volcengine: {
|
|
598
|
+
name: 'VolcEngine',
|
|
599
|
+
nameCn: '火山引擎',
|
|
600
|
+
baseUrl: 'https://ark.cn-beijing.volces.com/api/v3',
|
|
601
|
+
defaultModel: 'doubao-pro-4k',
|
|
602
|
+
description: '字节跳动,豆包系列'
|
|
603
|
+
},
|
|
604
|
+
spark: {
|
|
605
|
+
name: 'Spark',
|
|
606
|
+
nameCn: '讯飞星火',
|
|
607
|
+
baseUrl: 'https://spark-api-open.xf-yun.com/v1',
|
|
608
|
+
defaultModel: 'generalv3.5',
|
|
609
|
+
description: '科大讯飞'
|
|
610
|
+
},
|
|
611
|
+
baichuan: {
|
|
612
|
+
name: 'Baichuan',
|
|
613
|
+
nameCn: '百川智能',
|
|
614
|
+
baseUrl: 'https://api.baichuan-ai.com/v1',
|
|
615
|
+
defaultModel: 'Baichuan4',
|
|
616
|
+
description: '前搜狗王小川'
|
|
617
|
+
},
|
|
618
|
+
yi: {
|
|
619
|
+
name: 'Yi',
|
|
620
|
+
nameCn: '零一万物',
|
|
621
|
+
baseUrl: 'https://api.lingyiwanwu.com/v1',
|
|
622
|
+
defaultModel: 'yi-large',
|
|
623
|
+
description: '李开复创立'
|
|
624
|
+
},
|
|
625
|
+
stepfun: {
|
|
626
|
+
name: 'StepFun',
|
|
627
|
+
nameCn: '阶跃星辰',
|
|
628
|
+
baseUrl: 'https://api.stepfun.com/v1',
|
|
629
|
+
defaultModel: 'step-1-8k',
|
|
630
|
+
description: '上海阶跃'
|
|
631
|
+
},
|
|
632
|
+
lingji: {
|
|
633
|
+
name: 'Lingji',
|
|
634
|
+
nameCn: '无问芯穹',
|
|
635
|
+
baseUrl: 'https://inference-model.lingji.ai/v1',
|
|
636
|
+
defaultModel: 'qwen1.5-110b-chat',
|
|
637
|
+
description: '清华系'
|
|
638
|
+
},
|
|
639
|
+
iflow: {
|
|
640
|
+
name: 'iFlow',
|
|
641
|
+
nameCn: '心流',
|
|
642
|
+
baseUrl: 'https://api.xinliu.ai/v1',
|
|
643
|
+
defaultModel: 'xinliu-7b-chat',
|
|
644
|
+
description: '中文优化模型'
|
|
645
|
+
},
|
|
646
|
+
bailian: {
|
|
647
|
+
name: 'Bailian',
|
|
648
|
+
nameCn: '阿里百炼',
|
|
649
|
+
baseUrl: 'https://bailian.cn-beijing.aliyuncs.com/v1',
|
|
650
|
+
defaultModel: 'qwen-plus',
|
|
651
|
+
description: '阿里云百炼平台'
|
|
652
|
+
},
|
|
653
|
+
tencent: {
|
|
654
|
+
name: 'Tencent',
|
|
655
|
+
nameCn: '腾讯混元',
|
|
656
|
+
baseUrl: 'https://hunyuan.tencentcloudapi.com/v1',
|
|
657
|
+
defaultModel: 'hunyuan-lite',
|
|
658
|
+
description: '腾讯混元大模型'
|
|
659
|
+
},
|
|
660
|
+
360: {
|
|
661
|
+
name: '360Zhinao',
|
|
662
|
+
nameCn: '360智脑',
|
|
663
|
+
baseUrl: 'https://api.360.cn/v1',
|
|
664
|
+
defaultModel: '360gpt-pro',
|
|
665
|
+
description: '360 安全 AI'
|
|
666
|
+
},
|
|
667
|
+
langboat: {
|
|
668
|
+
name: 'Langboat',
|
|
669
|
+
nameCn: '澜舟科技',
|
|
670
|
+
baseUrl: 'https://api.langboat.com/v1',
|
|
671
|
+
defaultModel: 'mengzi-gpt',
|
|
672
|
+
description: '孟子模型'
|
|
673
|
+
},
|
|
674
|
+
sensetime: {
|
|
675
|
+
name: 'SenseTime',
|
|
676
|
+
nameCn: '商汤日日新',
|
|
677
|
+
baseUrl: 'https://api.sensenova.cn/v1',
|
|
678
|
+
defaultModel: 'nova-ptc-xl-v1',
|
|
679
|
+
description: '商汤科技'
|
|
680
|
+
},
|
|
681
|
+
unisound: {
|
|
682
|
+
name: 'Unisound',
|
|
683
|
+
nameCn: '云知声',
|
|
684
|
+
baseUrl: 'https://api.unisound.com/v1',
|
|
685
|
+
defaultModel: '山海大模型',
|
|
686
|
+
description: '语音 AI 专家'
|
|
687
|
+
},
|
|
688
|
+
teleai: {
|
|
689
|
+
name: 'TeleAI',
|
|
690
|
+
nameCn: '电信星辰',
|
|
691
|
+
baseUrl: 'https://api.teleai.cn/v1',
|
|
692
|
+
defaultModel: 'telechat-12b',
|
|
693
|
+
description: '中国电信'
|
|
694
|
+
},
|
|
695
|
+
mita: {
|
|
696
|
+
name: 'Mita',
|
|
697
|
+
nameCn: '秘塔AI',
|
|
698
|
+
baseUrl: 'https://api.metaso.cn/v1',
|
|
699
|
+
defaultModel: 'metaso-search',
|
|
700
|
+
description: 'AI 搜索'
|
|
701
|
+
},
|
|
702
|
+
|
|
703
|
+
// ═══════════════════════════════════════════════════════════
|
|
704
|
+
// 本地服务
|
|
705
|
+
// ═══════════════════════════════════════════════════════════
|
|
706
|
+
ollama: {
|
|
707
|
+
name: 'Ollama',
|
|
708
|
+
nameCn: 'Ollama',
|
|
709
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
710
|
+
defaultModel: 'llama3',
|
|
711
|
+
skipAuth: true,
|
|
712
|
+
description: '本地运行,无需 API Key'
|
|
713
|
+
},
|
|
714
|
+
lmstudio: {
|
|
715
|
+
name: 'LM Studio',
|
|
716
|
+
nameCn: 'LM Studio',
|
|
717
|
+
baseUrl: 'http://localhost:1234/v1',
|
|
718
|
+
defaultModel: 'local-model',
|
|
719
|
+
skipAuth: true,
|
|
720
|
+
description: '本地运行,可视化界面'
|
|
721
|
+
},
|
|
722
|
+
vllm: {
|
|
723
|
+
name: 'vLLM',
|
|
724
|
+
nameCn: 'vLLM',
|
|
725
|
+
baseUrl: 'http://localhost:8000/v1',
|
|
726
|
+
defaultModel: 'meta-llama/Llama-2-70b-hf',
|
|
727
|
+
skipAuth: true,
|
|
728
|
+
description: '高性能推理引擎'
|
|
729
|
+
},
|
|
730
|
+
localai: {
|
|
731
|
+
name: 'LocalAI',
|
|
732
|
+
nameCn: 'LocalAI',
|
|
733
|
+
baseUrl: 'http://localhost:8080/v1',
|
|
734
|
+
defaultModel: 'gpt-3.5-turbo',
|
|
735
|
+
skipAuth: true,
|
|
736
|
+
description: 'OpenAI 兼容本地服务'
|
|
737
|
+
},
|
|
738
|
+
textgen: {
|
|
739
|
+
name: 'TextGen',
|
|
740
|
+
nameCn: 'Text Generation WebUI',
|
|
741
|
+
baseUrl: 'http://localhost:5000/v1',
|
|
742
|
+
defaultModel: 'model',
|
|
743
|
+
skipAuth: true,
|
|
744
|
+
description: 'Oobabooga WebUI'
|
|
745
|
+
},
|
|
746
|
+
|
|
747
|
+
// ═══════════════════════════════════════════════════════════
|
|
748
|
+
// 其他聚合/特殊平台
|
|
749
|
+
// ═══════════════════════════════════════════════════════════
|
|
750
|
+
huggingface: {
|
|
751
|
+
name: 'HuggingFace',
|
|
752
|
+
nameCn: 'HuggingFace',
|
|
753
|
+
baseUrl: 'https://api-inference.huggingface.co/models',
|
|
754
|
+
defaultModel: 'meta-llama/Llama-3.2-3B-Instruct',
|
|
755
|
+
description: '开源模型中心'
|
|
756
|
+
},
|
|
757
|
+
monsterapi: {
|
|
758
|
+
name: 'MonsterAPI',
|
|
759
|
+
nameCn: 'MonsterAPI',
|
|
760
|
+
baseUrl: 'https://api.monsterapi.ai/v1',
|
|
761
|
+
defaultModel: 'meta-llama/Llama-2-70b-chat-hf',
|
|
762
|
+
description: '低成本 AI API'
|
|
763
|
+
},
|
|
764
|
+
glidian: {
|
|
765
|
+
name: 'Glidian',
|
|
766
|
+
nameCn: 'Glidian',
|
|
767
|
+
baseUrl: 'https://api.glidian.com/v1',
|
|
768
|
+
defaultModel: 'gpt-4',
|
|
769
|
+
description: 'AI API 网关'
|
|
770
|
+
},
|
|
771
|
+
inferless: {
|
|
772
|
+
name: 'Inferless',
|
|
773
|
+
nameCn: 'Inferless',
|
|
774
|
+
baseUrl: 'https://api.inferless.com/v1',
|
|
775
|
+
defaultModel: 'llama-2-70b',
|
|
776
|
+
description: '无服务器推理'
|
|
777
|
+
},
|
|
778
|
+
cerebras: {
|
|
779
|
+
name: 'Cerebras',
|
|
780
|
+
nameCn: 'Cerebras AI',
|
|
781
|
+
baseUrl: 'https://api.cerebras.ai/v1',
|
|
782
|
+
defaultModel: 'llama3.1-70b',
|
|
783
|
+
description: '极速推理芯片'
|
|
784
|
+
},
|
|
785
|
+
sambanova: {
|
|
786
|
+
name: 'SambaNova',
|
|
787
|
+
nameCn: 'SambaNova',
|
|
788
|
+
baseUrl: 'https://api.sambanova.ai/v1',
|
|
789
|
+
defaultModel: 'Meta-Llama-3.1-70B-Instruct',
|
|
790
|
+
description: '企业 AI 平台'
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* 创建 Provider 实例
|
|
796
|
+
*/
|
|
797
|
+
export function createProvider(providerId, apiKey = null, overrides = {}) {
|
|
798
|
+
const preset = PRESET_PROVIDERS[providerId];
|
|
799
|
+
|
|
800
|
+
if (!preset) {
|
|
801
|
+
// 未知 provider,尝试用通用配置
|
|
802
|
+
return new OpenAICompatibleProvider({
|
|
803
|
+
id: providerId,
|
|
804
|
+
name: providerId,
|
|
805
|
+
baseUrl: overrides.baseUrl || `https://api.${providerId}.com/v1`,
|
|
806
|
+
apiKey,
|
|
807
|
+
...overrides
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// 特殊 provider 需要单独处理
|
|
812
|
+
if (preset.special) {
|
|
813
|
+
// 将在单独文件中处理
|
|
814
|
+
console.log(`[Provider] ${providerId} requires special adapter`);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return new OpenAICompatibleProvider({
|
|
818
|
+
id: providerId,
|
|
819
|
+
...preset,
|
|
820
|
+
apiKey,
|
|
821
|
+
...overrides
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* 获取所有预设 Provider
|
|
827
|
+
*/
|
|
828
|
+
export function listPresetProviders() {
|
|
829
|
+
return Object.entries(PRESET_PROVIDERS).map(([id, config]) => ({
|
|
830
|
+
id,
|
|
831
|
+
name: config.name,
|
|
832
|
+
nameCn: config.nameCn,
|
|
833
|
+
defaultModel: config.defaultModel,
|
|
834
|
+
description: config.description || '',
|
|
835
|
+
skipAuth: config.skipAuth || false,
|
|
836
|
+
special: config.special || false
|
|
837
|
+
}));
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
export default OpenAICompatibleProvider;
|