@lobehub/chat 1.69.0 → 1.69.2
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/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/models.json +9 -0
- package/locales/bg-BG/models.json +9 -0
- package/locales/de-DE/models.json +9 -0
- package/locales/en-US/models.json +9 -0
- package/locales/es-ES/models.json +9 -0
- package/locales/fa-IR/models.json +9 -0
- package/locales/fr-FR/models.json +9 -0
- package/locales/it-IT/models.json +9 -0
- package/locales/ja-JP/models.json +9 -0
- package/locales/ko-KR/models.json +9 -0
- package/locales/nl-NL/models.json +9 -0
- package/locales/pl-PL/models.json +9 -0
- package/locales/pt-BR/models.json +9 -0
- package/locales/ru-RU/models.json +9 -0
- package/locales/tr-TR/models.json +9 -0
- package/locales/vi-VN/models.json +9 -0
- package/locales/zh-CN/models.json +9 -0
- package/locales/zh-TW/models.json +9 -0
- package/package.json +2 -2
- package/src/config/aiModels/google.ts +0 -1
- package/src/config/aiModels/groq.ts +14 -0
- package/src/config/aiModels/novita.ts +36 -0
- package/src/config/aiModels/siliconcloud.ts +18 -2
- package/src/libs/agent-runtime/UniformRuntime/index.ts +114 -0
- package/src/libs/agent-runtime/anthropic/handleAnthropicError.ts +15 -0
- package/src/libs/agent-runtime/anthropic/index.test.ts +10 -1
- package/src/libs/agent-runtime/anthropic/index.ts +58 -40
- package/src/libs/agent-runtime/azureai/index.ts +7 -1
- package/src/libs/agent-runtime/github/index.ts +20 -25
- package/src/libs/agent-runtime/index.ts +2 -0
- package/src/libs/agent-runtime/openai/index.ts +2 -22
- package/src/libs/agent-runtime/types/type.ts +1 -1
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +11 -7
- package/src/libs/agent-runtime/utils/openaiHelpers.ts +22 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.69.
|
3
|
+
"version": "1.69.2",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -133,7 +133,7 @@
|
|
133
133
|
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
134
134
|
"@lobehub/icons": "^1.73.1",
|
135
135
|
"@lobehub/tts": "^1.28.0",
|
136
|
-
"@lobehub/ui": "^1.165.
|
136
|
+
"@lobehub/ui": "^1.165.5",
|
137
137
|
"@neondatabase/serverless": "^0.10.4",
|
138
138
|
"@next/third-parties": "^15.2.0",
|
139
139
|
"@react-spring/web": "^9.7.5",
|
@@ -118,7 +118,6 @@ const googleChatModels: AIChatModelCard[] = [
|
|
118
118
|
description:
|
119
119
|
'Gemini 2.0 Flash Thinking Exp 是 Google 的实验性多模态推理AI模型,能对复杂问题进行推理,拥有新的思维能力。',
|
120
120
|
displayName: 'Gemini 2.0 Flash Thinking Experimental',
|
121
|
-
enabled: true,
|
122
121
|
id: 'gemini-2.0-flash-thinking-exp',
|
123
122
|
maxOutput: 65_536,
|
124
123
|
pricing: {
|
@@ -4,6 +4,20 @@ import { AIChatModelCard } from '@/types/aiModel';
|
|
4
4
|
// https://console.groq.com/docs/models
|
5
5
|
|
6
6
|
const groqChatModels: AIChatModelCard[] = [
|
7
|
+
{
|
8
|
+
abilities: {
|
9
|
+
functionCall: true,
|
10
|
+
reasoning: true,
|
11
|
+
},
|
12
|
+
contextWindowTokens: 131_072,
|
13
|
+
displayName: 'Qwen QwQ 32B',
|
14
|
+
id: 'qwen-qwq-32b',
|
15
|
+
pricing: {
|
16
|
+
input: 0.29,
|
17
|
+
output: 0.39,
|
18
|
+
},
|
19
|
+
type: 'chat',
|
20
|
+
},
|
7
21
|
{
|
8
22
|
abilities: {
|
9
23
|
functionCall: true,
|
@@ -150,6 +150,29 @@ const novitaChatModels: AIChatModelCard[] = [
|
|
150
150
|
},
|
151
151
|
type: 'chat',
|
152
152
|
},
|
153
|
+
{
|
154
|
+
contextWindowTokens: 64_000,
|
155
|
+
displayName: 'Deepseek V3 Turbo',
|
156
|
+
id: 'deepseek/deepseek-v3-turbo',
|
157
|
+
pricing: {
|
158
|
+
input: 0.4,
|
159
|
+
output: 1.3,
|
160
|
+
},
|
161
|
+
type: 'chat',
|
162
|
+
},
|
163
|
+
{
|
164
|
+
abilities: {
|
165
|
+
reasoning: true,
|
166
|
+
},
|
167
|
+
contextWindowTokens: 64_000,
|
168
|
+
displayName: 'Deepseek R1 Turbo',
|
169
|
+
id: 'deepseek/deepseek-r1-turbo',
|
170
|
+
pricing: {
|
171
|
+
input: 0.7,
|
172
|
+
output: 2.5,
|
173
|
+
},
|
174
|
+
type: 'chat',
|
175
|
+
},
|
153
176
|
{
|
154
177
|
abilities: {
|
155
178
|
reasoning: true,
|
@@ -378,6 +401,19 @@ const novitaChatModels: AIChatModelCard[] = [
|
|
378
401
|
},
|
379
402
|
type: 'chat',
|
380
403
|
},
|
404
|
+
{
|
405
|
+
abilities: {
|
406
|
+
reasoning: true,
|
407
|
+
},
|
408
|
+
contextWindowTokens: 32_768,
|
409
|
+
displayName: 'QwQ 32B',
|
410
|
+
id: 'qwen/qwq-32b',
|
411
|
+
pricing: {
|
412
|
+
input: 0.18,
|
413
|
+
output: 0.2,
|
414
|
+
},
|
415
|
+
type: 'chat',
|
416
|
+
},
|
381
417
|
];
|
382
418
|
|
383
419
|
export const allModels = [...novitaChatModels];
|
@@ -226,9 +226,25 @@ const siliconcloudChatModels: AIChatModelCard[] = [
|
|
226
226
|
},
|
227
227
|
contextWindowTokens: 32_768,
|
228
228
|
description:
|
229
|
-
'QwQ-32B-
|
230
|
-
displayName: 'QwQ 32B
|
229
|
+
'QwQ 是 Qwen 系列的推理模型。与传统的指令调优模型相比,QwQ 具备思考和推理能力,能够在下游任务中实现显著增强的性能,尤其是在解决困难问题方面。QwQ-32B 是中型推理模型,能够在与最先进的推理模型(如 DeepSeek-R1、o1-mini)的对比中取得有竞争力的性能。该模型采用 RoPE、SwiGLU、RMSNorm 和 Attention QKV bias 等技术,具有 64 层网络结构和 40 个 Q 注意力头(GQA 架构中 KV 为 8 个)。',
|
230
|
+
displayName: 'QwQ 32B',
|
231
231
|
enabled: true,
|
232
|
+
id: 'Qwen/QwQ-32B',
|
233
|
+
pricing: {
|
234
|
+
currency: 'CNY',
|
235
|
+
input: 1,
|
236
|
+
output: 4,
|
237
|
+
},
|
238
|
+
type: 'chat',
|
239
|
+
},
|
240
|
+
{
|
241
|
+
abilities: {
|
242
|
+
reasoning: true,
|
243
|
+
},
|
244
|
+
contextWindowTokens: 32_768,
|
245
|
+
description:
|
246
|
+
'QwQ-32B-Preview 是 Qwen 最新的实验性研究模型,专注于提升AI推理能力。通过探索语言混合、递归推理等复杂机制,主要优势包括强大的推理分析能力、数学和编程能力。与此同时,也存在语言切换问题、推理循环、安全性考虑、其他能力方面的差异。',
|
247
|
+
displayName: 'QwQ 32B Preview',
|
232
248
|
id: 'Qwen/QwQ-32B-Preview',
|
233
249
|
pricing: {
|
234
250
|
currency: 'CNY',
|
@@ -0,0 +1,114 @@
|
|
1
|
+
import { LobeRuntimeAI } from '../BaseAI';
|
2
|
+
import { LobeOpenAI } from '../openai';
|
3
|
+
import { providerRuntimeMap } from '../runtimeMap';
|
4
|
+
import {
|
5
|
+
ChatCompetitionOptions,
|
6
|
+
type ChatCompletionErrorPayload,
|
7
|
+
ChatStreamPayload,
|
8
|
+
EmbeddingsOptions,
|
9
|
+
EmbeddingsPayload,
|
10
|
+
TextToImagePayload,
|
11
|
+
TextToSpeechPayload,
|
12
|
+
} from '../types';
|
13
|
+
|
14
|
+
export interface RuntimeItem {
|
15
|
+
id: string;
|
16
|
+
models?: string[];
|
17
|
+
runtime: LobeRuntimeAI;
|
18
|
+
}
|
19
|
+
|
20
|
+
interface ProviderInitParams extends Record<string, any> {
|
21
|
+
accessKeyId?: string;
|
22
|
+
accessKeySecret?: string;
|
23
|
+
apiKey?: string;
|
24
|
+
apiVersion?: string;
|
25
|
+
baseURL?: string;
|
26
|
+
baseURLOrAccountID?: string;
|
27
|
+
dangerouslyAllowBrowser?: boolean;
|
28
|
+
region?: string;
|
29
|
+
sessionToken?: string;
|
30
|
+
}
|
31
|
+
|
32
|
+
interface ProviderInstance {
|
33
|
+
apiType: keyof typeof providerRuntimeMap;
|
34
|
+
models?: string[];
|
35
|
+
params: ProviderInitParams;
|
36
|
+
runtime?: typeof LobeOpenAI;
|
37
|
+
}
|
38
|
+
|
39
|
+
interface UniformRuntimeOptions {
|
40
|
+
chat?: {
|
41
|
+
handleError?: (error: any) => Omit<ChatCompletionErrorPayload, 'provider'> | undefined;
|
42
|
+
};
|
43
|
+
}
|
44
|
+
|
45
|
+
class UniformRuntime {
|
46
|
+
private _runtimes: RuntimeItem[];
|
47
|
+
private _options: UniformRuntimeOptions;
|
48
|
+
|
49
|
+
constructor(id: string, providers: ProviderInstance[], options: UniformRuntimeOptions) {
|
50
|
+
if (providers.length === 0) {
|
51
|
+
throw new Error('empty providers');
|
52
|
+
}
|
53
|
+
|
54
|
+
this._runtimes = providers.map((options) => {
|
55
|
+
const providerAI = options.runtime ?? providerRuntimeMap[options.apiType] ?? LobeOpenAI;
|
56
|
+
const runtime: LobeRuntimeAI = new providerAI({ ...options.params, id });
|
57
|
+
|
58
|
+
return { id: options.apiType, models: options.models, runtime };
|
59
|
+
});
|
60
|
+
|
61
|
+
this._options = options;
|
62
|
+
}
|
63
|
+
|
64
|
+
// 检查下是否能匹配到特定模型,否则默认使用第一个 runtime
|
65
|
+
getRuntimeByModel(model: string) {
|
66
|
+
const runtimeItem =
|
67
|
+
this._runtimes.find((runtime) => runtime.models && runtime.models.includes(model)) ||
|
68
|
+
this._runtimes[0];
|
69
|
+
|
70
|
+
return runtimeItem.runtime;
|
71
|
+
}
|
72
|
+
|
73
|
+
async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) {
|
74
|
+
try {
|
75
|
+
const runtime = this.getRuntimeByModel(payload.model);
|
76
|
+
|
77
|
+
return await runtime.chat(payload, options);
|
78
|
+
} catch (e) {
|
79
|
+
if (this._options.chat?.handleError) {
|
80
|
+
const error = this._options.chat.handleError(e);
|
81
|
+
|
82
|
+
if (error) {
|
83
|
+
throw error;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
throw e;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
async textToImage(payload: TextToImagePayload) {
|
92
|
+
const runtime = this.getRuntimeByModel(payload.model);
|
93
|
+
|
94
|
+
return runtime.textToImage?.(payload);
|
95
|
+
}
|
96
|
+
|
97
|
+
async models() {
|
98
|
+
return this._runtimes[0].runtime.models?.();
|
99
|
+
}
|
100
|
+
|
101
|
+
async embeddings(payload: EmbeddingsPayload, options?: EmbeddingsOptions) {
|
102
|
+
const runtime = this.getRuntimeByModel(payload.model);
|
103
|
+
|
104
|
+
return runtime.embeddings?.(payload, options);
|
105
|
+
}
|
106
|
+
|
107
|
+
async textToSpeech(payload: TextToSpeechPayload, options?: EmbeddingsOptions) {
|
108
|
+
const runtime = this.getRuntimeByModel(payload.model);
|
109
|
+
|
110
|
+
return runtime.textToSpeech?.(payload, options);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
export default UniformRuntime;
|
@@ -0,0 +1,15 @@
|
|
1
|
+
export const handleAnthropicError = (error: any) => {
|
2
|
+
let errorResult: any = error;
|
3
|
+
|
4
|
+
if (error.error) {
|
5
|
+
errorResult = error.error;
|
6
|
+
|
7
|
+
if ('error' in errorResult) {
|
8
|
+
errorResult = errorResult.error;
|
9
|
+
}
|
10
|
+
} else {
|
11
|
+
errorResult = { headers: error.headers, stack: error.stack, status: error.status };
|
12
|
+
}
|
13
|
+
|
14
|
+
return { errorResult };
|
15
|
+
};
|
@@ -44,6 +44,15 @@ describe('LobeAnthropicAI', () => {
|
|
44
44
|
expect(instance).toBeInstanceOf(LobeAnthropicAI);
|
45
45
|
expect(instance.baseURL).toBe('https://api.anthropic.proxy');
|
46
46
|
});
|
47
|
+
|
48
|
+
it('should correctly initialize with different id', async () => {
|
49
|
+
const instance = new LobeAnthropicAI({
|
50
|
+
apiKey: 'test_api_key',
|
51
|
+
id: 'abc',
|
52
|
+
});
|
53
|
+
expect(instance).toBeInstanceOf(LobeAnthropicAI);
|
54
|
+
expect(instance['id']).toBe('abc');
|
55
|
+
});
|
47
56
|
});
|
48
57
|
|
49
58
|
describe('chat', () => {
|
@@ -347,7 +356,7 @@ describe('LobeAnthropicAI', () => {
|
|
347
356
|
// Assert
|
348
357
|
expect(e).toEqual({
|
349
358
|
endpoint: 'https://api.anthropic.com',
|
350
|
-
error: apiError,
|
359
|
+
error: apiError.error.error,
|
351
360
|
errorType: bizErrorType,
|
352
361
|
provider,
|
353
362
|
});
|
@@ -2,18 +2,23 @@
|
|
2
2
|
import '@anthropic-ai/sdk/shims/web';
|
3
3
|
import Anthropic from '@anthropic-ai/sdk';
|
4
4
|
import { ClientOptions } from 'openai';
|
5
|
+
import type { ChatModelCard } from '@/types/llm';
|
5
6
|
|
6
7
|
import { LobeRuntimeAI } from '../BaseAI';
|
7
8
|
import { AgentRuntimeErrorType } from '../error';
|
8
|
-
import {
|
9
|
+
import {
|
10
|
+
ChatCompetitionOptions,
|
11
|
+
type ChatCompletionErrorPayload,
|
12
|
+
ChatStreamPayload,
|
13
|
+
ModelProvider,
|
14
|
+
} from '../types';
|
9
15
|
import { AgentRuntimeError } from '../utils/createError';
|
10
16
|
import { debugStream } from '../utils/debugStream';
|
11
17
|
import { desensitizeUrl } from '../utils/desensitizeUrl';
|
12
18
|
import { buildAnthropicMessages, buildAnthropicTools } from '../utils/anthropicHelpers';
|
13
19
|
import { StreamingResponse } from '../utils/response';
|
14
20
|
import { AnthropicStream } from '../utils/streams';
|
15
|
-
|
16
|
-
import type { ChatModelCard } from '@/types/llm';
|
21
|
+
import { handleAnthropicError } from './handleAnthropicError';
|
17
22
|
|
18
23
|
export interface AnthropicModelCard {
|
19
24
|
display_name: string;
|
@@ -22,18 +27,24 @@ export interface AnthropicModelCard {
|
|
22
27
|
|
23
28
|
const DEFAULT_BASE_URL = 'https://api.anthropic.com';
|
24
29
|
|
30
|
+
interface AnthropicAIParams extends ClientOptions {
|
31
|
+
id?: string;
|
32
|
+
}
|
33
|
+
|
25
34
|
export class LobeAnthropicAI implements LobeRuntimeAI {
|
26
35
|
private client: Anthropic;
|
27
36
|
|
28
37
|
baseURL: string;
|
29
38
|
apiKey?: string;
|
39
|
+
private id: string;
|
30
40
|
|
31
|
-
constructor({ apiKey, baseURL = DEFAULT_BASE_URL, ...res }:
|
41
|
+
constructor({ apiKey, baseURL = DEFAULT_BASE_URL, id, ...res }: AnthropicAIParams = {}) {
|
32
42
|
if (!apiKey) throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey);
|
33
43
|
|
34
44
|
this.client = new Anthropic({ apiKey, baseURL, ...res });
|
35
45
|
this.baseURL = this.client.baseURL;
|
36
46
|
this.apiKey = apiKey;
|
47
|
+
this.id = id || ModelProvider.Anthropic;
|
37
48
|
}
|
38
49
|
|
39
50
|
async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) {
|
@@ -57,42 +68,7 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
|
|
57
68
|
headers: options?.headers,
|
58
69
|
});
|
59
70
|
} catch (error) {
|
60
|
-
|
61
|
-
|
62
|
-
if (this.baseURL !== DEFAULT_BASE_URL) {
|
63
|
-
desensitizedEndpoint = desensitizeUrl(this.baseURL);
|
64
|
-
}
|
65
|
-
|
66
|
-
if ('status' in (error as any)) {
|
67
|
-
switch ((error as Response).status) {
|
68
|
-
case 401: {
|
69
|
-
throw AgentRuntimeError.chat({
|
70
|
-
endpoint: desensitizedEndpoint,
|
71
|
-
error: error as any,
|
72
|
-
errorType: AgentRuntimeErrorType.InvalidProviderAPIKey,
|
73
|
-
provider: ModelProvider.Anthropic,
|
74
|
-
});
|
75
|
-
}
|
76
|
-
|
77
|
-
case 403: {
|
78
|
-
throw AgentRuntimeError.chat({
|
79
|
-
endpoint: desensitizedEndpoint,
|
80
|
-
error: error as any,
|
81
|
-
errorType: AgentRuntimeErrorType.LocationNotSupportError,
|
82
|
-
provider: ModelProvider.Anthropic,
|
83
|
-
});
|
84
|
-
}
|
85
|
-
default: {
|
86
|
-
break;
|
87
|
-
}
|
88
|
-
}
|
89
|
-
}
|
90
|
-
throw AgentRuntimeError.chat({
|
91
|
-
endpoint: desensitizedEndpoint,
|
92
|
-
error: error as any,
|
93
|
-
errorType: AgentRuntimeErrorType.ProviderBizError,
|
94
|
-
provider: ModelProvider.Anthropic,
|
95
|
-
});
|
71
|
+
throw this.handleError(error);
|
96
72
|
}
|
97
73
|
}
|
98
74
|
|
@@ -191,6 +167,48 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
|
|
191
167
|
})
|
192
168
|
.filter(Boolean) as ChatModelCard[];
|
193
169
|
}
|
170
|
+
|
171
|
+
private handleError(error: any): ChatCompletionErrorPayload {
|
172
|
+
let desensitizedEndpoint = this.baseURL;
|
173
|
+
|
174
|
+
if (this.baseURL !== DEFAULT_BASE_URL) {
|
175
|
+
desensitizedEndpoint = desensitizeUrl(this.baseURL);
|
176
|
+
}
|
177
|
+
|
178
|
+
if ('status' in (error as any)) {
|
179
|
+
switch ((error as Response).status) {
|
180
|
+
case 401: {
|
181
|
+
throw AgentRuntimeError.chat({
|
182
|
+
endpoint: desensitizedEndpoint,
|
183
|
+
error: error as any,
|
184
|
+
errorType: AgentRuntimeErrorType.InvalidProviderAPIKey,
|
185
|
+
provider: this.id,
|
186
|
+
});
|
187
|
+
}
|
188
|
+
|
189
|
+
case 403: {
|
190
|
+
throw AgentRuntimeError.chat({
|
191
|
+
endpoint: desensitizedEndpoint,
|
192
|
+
error: error as any,
|
193
|
+
errorType: AgentRuntimeErrorType.LocationNotSupportError,
|
194
|
+
provider: this.id,
|
195
|
+
});
|
196
|
+
}
|
197
|
+
default: {
|
198
|
+
break;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
const { errorResult } = handleAnthropicError(error);
|
204
|
+
|
205
|
+
throw AgentRuntimeError.chat({
|
206
|
+
endpoint: desensitizedEndpoint,
|
207
|
+
error: errorResult,
|
208
|
+
errorType: AgentRuntimeErrorType.ProviderBizError,
|
209
|
+
provider: this.id,
|
210
|
+
});
|
211
|
+
}
|
194
212
|
}
|
195
213
|
|
196
214
|
export default LobeAnthropicAI;
|
@@ -13,10 +13,16 @@ import { transformResponseToStream } from '../utils/openaiCompatibleFactory';
|
|
13
13
|
import { StreamingResponse } from '../utils/response';
|
14
14
|
import { OpenAIStream, createSSEDataExtractor } from '../utils/streams';
|
15
15
|
|
16
|
+
interface AzureAIParams {
|
17
|
+
apiKey?: string;
|
18
|
+
apiVersion?: string;
|
19
|
+
baseURL?: string;
|
20
|
+
}
|
21
|
+
|
16
22
|
export class LobeAzureAI implements LobeRuntimeAI {
|
17
23
|
client: ModelClient;
|
18
24
|
|
19
|
-
constructor(params?:
|
25
|
+
constructor(params?: AzureAIParams) {
|
20
26
|
if (!params?.apiKey || !params?.baseURL)
|
21
27
|
throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey);
|
22
28
|
|
@@ -1,9 +1,9 @@
|
|
1
|
+
import type { ChatModelCard } from '@/types/llm';
|
2
|
+
|
1
3
|
import { AgentRuntimeErrorType } from '../error';
|
2
|
-
import { pruneReasoningPayload } from '../openai';
|
3
4
|
import { ModelProvider } from '../types';
|
4
5
|
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
|
5
|
-
|
6
|
-
import type { ChatModelCard } from '@/types/llm';
|
6
|
+
import { pruneReasoningPayload } from '../utils/openaiHelpers';
|
7
7
|
|
8
8
|
export interface GithubModelCard {
|
9
9
|
description: string;
|
@@ -39,27 +39,20 @@ export const LobeGithubAI = LobeOpenAICompatibleFactory({
|
|
39
39
|
models: async ({ client }) => {
|
40
40
|
const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
|
41
41
|
|
42
|
-
const functionCallKeywords = [
|
43
|
-
'function',
|
44
|
-
'tool',
|
45
|
-
];
|
42
|
+
const functionCallKeywords = ['function', 'tool'];
|
46
43
|
|
47
|
-
const visionKeywords = [
|
48
|
-
'vision',
|
49
|
-
];
|
44
|
+
const visionKeywords = ['vision'];
|
50
45
|
|
51
|
-
const reasoningKeywords = [
|
52
|
-
'deepseek-r1',
|
53
|
-
'o1',
|
54
|
-
'o3',
|
55
|
-
];
|
46
|
+
const reasoningKeywords = ['deepseek-r1', 'o1', 'o3'];
|
56
47
|
|
57
48
|
const modelsPage = (await client.models.list()) as any;
|
58
49
|
const modelList: GithubModelCard[] = modelsPage.body;
|
59
50
|
|
60
51
|
return modelList
|
61
52
|
.map((model) => {
|
62
|
-
const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
|
53
|
+
const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
|
54
|
+
(m) => model.name.toLowerCase() === m.id.toLowerCase(),
|
55
|
+
);
|
63
56
|
|
64
57
|
return {
|
65
58
|
contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
|
@@ -67,18 +60,20 @@ export const LobeGithubAI = LobeOpenAICompatibleFactory({
|
|
67
60
|
displayName: model.friendly_name,
|
68
61
|
enabled: knownModel?.enabled || false,
|
69
62
|
functionCall:
|
70
|
-
functionCallKeywords.some(keyword =>
|
71
|
-
|
72
|
-
||
|
63
|
+
functionCallKeywords.some((keyword) =>
|
64
|
+
model.description.toLowerCase().includes(keyword),
|
65
|
+
) ||
|
66
|
+
knownModel?.abilities?.functionCall ||
|
67
|
+
false,
|
73
68
|
id: model.name,
|
74
69
|
reasoning:
|
75
|
-
reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword))
|
76
|
-
|
77
|
-
|
70
|
+
reasoningKeywords.some((keyword) => model.name.toLowerCase().includes(keyword)) ||
|
71
|
+
knownModel?.abilities?.reasoning ||
|
72
|
+
false,
|
78
73
|
vision:
|
79
|
-
visionKeywords.some(keyword => model.description.toLowerCase().includes(keyword))
|
80
|
-
|
81
|
-
|
74
|
+
visionKeywords.some((keyword) => model.description.toLowerCase().includes(keyword)) ||
|
75
|
+
knownModel?.abilities?.vision ||
|
76
|
+
false,
|
82
77
|
};
|
83
78
|
})
|
84
79
|
.filter(Boolean) as ChatModelCard[];
|
@@ -19,6 +19,8 @@ export { LobeQwenAI } from './qwen';
|
|
19
19
|
export { LobeTogetherAI } from './togetherai';
|
20
20
|
export * from './types';
|
21
21
|
export { AgentRuntimeError } from './utils/createError';
|
22
|
+
export { LobeOpenAICompatibleFactory } from './utils/openaiCompatibleFactory';
|
23
|
+
export { pruneReasoningPayload } from './utils/openaiHelpers';
|
22
24
|
export { LobeVolcengineAI } from './volcengine';
|
23
25
|
export { LobeZeroOneAI } from './zeroone';
|
24
26
|
export { LobeZhipuAI } from './zhipu';
|
@@ -1,33 +1,13 @@
|
|
1
|
-
import { disableStreamModels, systemToUserModels } from '@/const/models';
|
2
1
|
import type { ChatModelCard } from '@/types/llm';
|
3
2
|
|
4
|
-
import {
|
3
|
+
import { ModelProvider } from '../types';
|
5
4
|
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
|
5
|
+
import { pruneReasoningPayload } from '../utils/openaiHelpers';
|
6
6
|
|
7
7
|
export interface OpenAIModelCard {
|
8
8
|
id: string;
|
9
9
|
}
|
10
10
|
|
11
|
-
export const pruneReasoningPayload = (payload: ChatStreamPayload) => {
|
12
|
-
return {
|
13
|
-
...payload,
|
14
|
-
frequency_penalty: 0,
|
15
|
-
messages: payload.messages.map((message: OpenAIChatMessage) => ({
|
16
|
-
...message,
|
17
|
-
role:
|
18
|
-
message.role === 'system'
|
19
|
-
? systemToUserModels.has(payload.model)
|
20
|
-
? 'user'
|
21
|
-
: 'developer'
|
22
|
-
: message.role,
|
23
|
-
})),
|
24
|
-
presence_penalty: 0,
|
25
|
-
stream: !disableStreamModels.has(payload.model),
|
26
|
-
temperature: 1,
|
27
|
-
top_p: 1,
|
28
|
-
};
|
29
|
-
};
|
30
|
-
|
31
11
|
export const LobeOpenAI = LobeOpenAICompatibleFactory({
|
32
12
|
baseURL: 'https://api.openai.com/v1',
|
33
13
|
chatCompletion: {
|
@@ -168,6 +168,8 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
168
168
|
return class LobeOpenAICompatibleAI implements LobeRuntimeAI {
|
169
169
|
client!: OpenAI;
|
170
170
|
|
171
|
+
private id: string;
|
172
|
+
|
171
173
|
baseURL!: string;
|
172
174
|
protected _options: ConstructorOptions<T>;
|
173
175
|
|
@@ -192,6 +194,8 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
192
194
|
}
|
193
195
|
|
194
196
|
this.baseURL = baseURL || this.client.baseURL;
|
197
|
+
|
198
|
+
this.id = options.id || provider;
|
195
199
|
}
|
196
200
|
|
197
201
|
async chat({ responseMode, ...payload }: ChatStreamPayload, options?: ChatCompetitionOptions) {
|
@@ -210,7 +214,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
210
214
|
const streamOptions: OpenAIStreamOptions = {
|
211
215
|
bizErrorTypeTransformer: chatCompletion?.handleStreamBizErrorType,
|
212
216
|
callbacks: options?.callback,
|
213
|
-
provider,
|
217
|
+
provider: this.id,
|
214
218
|
};
|
215
219
|
|
216
220
|
if (customClient?.createChatCompletionStream) {
|
@@ -368,7 +372,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
368
372
|
if (errorResult)
|
369
373
|
return AgentRuntimeError.chat({
|
370
374
|
...errorResult,
|
371
|
-
provider,
|
375
|
+
provider: this.id,
|
372
376
|
} as ChatCompletionErrorPayload);
|
373
377
|
}
|
374
378
|
|
@@ -379,7 +383,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
379
383
|
endpoint: desensitizedEndpoint,
|
380
384
|
error: error as any,
|
381
385
|
errorType: ErrorType.invalidAPIKey,
|
382
|
-
provider:
|
386
|
+
provider: this.id as ModelProvider,
|
383
387
|
});
|
384
388
|
}
|
385
389
|
|
@@ -397,7 +401,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
397
401
|
endpoint: desensitizedEndpoint,
|
398
402
|
error: errorResult,
|
399
403
|
errorType: AgentRuntimeErrorType.InsufficientQuota,
|
400
|
-
provider:
|
404
|
+
provider: this.id as ModelProvider,
|
401
405
|
});
|
402
406
|
}
|
403
407
|
|
@@ -406,7 +410,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
406
410
|
endpoint: desensitizedEndpoint,
|
407
411
|
error: errorResult,
|
408
412
|
errorType: AgentRuntimeErrorType.ModelNotFound,
|
409
|
-
provider:
|
413
|
+
provider: this.id as ModelProvider,
|
410
414
|
});
|
411
415
|
}
|
412
416
|
|
@@ -417,7 +421,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
417
421
|
endpoint: desensitizedEndpoint,
|
418
422
|
error: errorResult,
|
419
423
|
errorType: AgentRuntimeErrorType.ExceededContextWindow,
|
420
|
-
provider:
|
424
|
+
provider: this.id as ModelProvider,
|
421
425
|
});
|
422
426
|
}
|
423
427
|
}
|
@@ -426,7 +430,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
426
430
|
endpoint: desensitizedEndpoint,
|
427
431
|
error: errorResult,
|
428
432
|
errorType: RuntimeError || ErrorType.bizError,
|
429
|
-
provider:
|
433
|
+
provider: this.id as ModelProvider,
|
430
434
|
});
|
431
435
|
}
|
432
436
|
};
|