@lobehub/chat 1.109.0 → 1.109.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/CHANGELOG.md +34 -0
- package/changelog/v1.json +12 -0
- package/locales/ar/models.json +6 -0
- package/locales/ar/providers.json +3 -0
- package/locales/bg-BG/models.json +6 -0
- package/locales/bg-BG/providers.json +3 -0
- package/locales/de-DE/models.json +6 -0
- package/locales/de-DE/providers.json +3 -0
- package/locales/en-US/models.json +6 -0
- package/locales/en-US/providers.json +3 -0
- package/locales/es-ES/models.json +6 -0
- package/locales/es-ES/providers.json +3 -0
- package/locales/fa-IR/models.json +6 -0
- package/locales/fa-IR/providers.json +3 -0
- package/locales/fr-FR/models.json +6 -0
- package/locales/fr-FR/providers.json +3 -0
- package/locales/it-IT/models.json +6 -0
- package/locales/it-IT/providers.json +3 -0
- package/locales/ja-JP/models.json +6 -0
- package/locales/ja-JP/providers.json +3 -0
- package/locales/ko-KR/models.json +6 -0
- package/locales/ko-KR/providers.json +3 -0
- package/locales/nl-NL/models.json +6 -0
- package/locales/nl-NL/providers.json +3 -0
- package/locales/pl-PL/models.json +6 -0
- package/locales/pl-PL/providers.json +3 -0
- package/locales/pt-BR/models.json +6 -0
- package/locales/pt-BR/providers.json +3 -0
- package/locales/ru-RU/models.json +6 -0
- package/locales/ru-RU/providers.json +3 -0
- package/locales/tr-TR/models.json +6 -0
- package/locales/tr-TR/providers.json +3 -0
- package/locales/vi-VN/models.json +6 -0
- package/locales/vi-VN/providers.json +3 -0
- package/locales/zh-CN/models.json +6 -0
- package/locales/zh-CN/providers.json +3 -0
- package/locales/zh-TW/models.json +6 -0
- package/locales/zh-TW/providers.json +3 -0
- package/package.json +1 -1
- package/src/config/aiModels/aihubmix.ts +465 -30
- package/src/config/aiModels/anthropic.ts +27 -1
- package/src/config/aiModels/groq.ts +40 -4
- package/src/config/aiModels/qwen.ts +24 -2
- package/src/libs/model-runtime/anthropic/index.ts +15 -2
- package/src/libs/model-runtime/utils/modelParse.ts +2 -2
- package/src/libs/model-runtime/utils/streams/ollama.test.ts +97 -51
- package/src/libs/model-runtime/utils/streams/ollama.ts +4 -0
@@ -5,7 +5,7 @@ import { AIChatModelCard } from '@/types/aiModel';
|
|
5
5
|
|
6
6
|
const groqChatModels: AIChatModelCard[] = [
|
7
7
|
{
|
8
|
-
contextWindowTokens:
|
8
|
+
contextWindowTokens: 8192,
|
9
9
|
description:
|
10
10
|
'Compound-beta 是一个复合 AI 系统,由 GroqCloud 中已经支持的多个开放可用的模型提供支持,可以智能地、有选择地使用工具来回答用户查询。',
|
11
11
|
displayName: 'Compound Beta',
|
@@ -15,7 +15,7 @@ const groqChatModels: AIChatModelCard[] = [
|
|
15
15
|
type: 'chat',
|
16
16
|
},
|
17
17
|
{
|
18
|
-
contextWindowTokens:
|
18
|
+
contextWindowTokens: 8192,
|
19
19
|
description:
|
20
20
|
'Compound-beta-mini 是一个复合 AI 系统,由 GroqCloud 中已经支持的公开可用模型提供支持,可以智能地、有选择地使用工具来回答用户查询。',
|
21
21
|
displayName: 'Compound Beta Mini',
|
@@ -23,6 +23,42 @@ const groqChatModels: AIChatModelCard[] = [
|
|
23
23
|
maxOutput: 8192,
|
24
24
|
type: 'chat',
|
25
25
|
},
|
26
|
+
{
|
27
|
+
abilities: {
|
28
|
+
functionCall: true,
|
29
|
+
reasoning: true,
|
30
|
+
},
|
31
|
+
contextWindowTokens: 131_072,
|
32
|
+
description:
|
33
|
+
'OpenAI GPT-OSS 120B 是一款拥有 1200 亿参数的顶尖语言模型,内置浏览器搜索和代码执行功能,并具备推理能力。',
|
34
|
+
displayName: 'GPT OSS 120B',
|
35
|
+
id: 'openai/gpt-oss-120b',
|
36
|
+
maxOutput: 32_768,
|
37
|
+
pricing: {
|
38
|
+
input: 0.15,
|
39
|
+
output: 0.75,
|
40
|
+
},
|
41
|
+
releasedAt: '2025-08-06',
|
42
|
+
type: 'chat',
|
43
|
+
},
|
44
|
+
{
|
45
|
+
abilities: {
|
46
|
+
functionCall: true,
|
47
|
+
reasoning: true,
|
48
|
+
},
|
49
|
+
contextWindowTokens: 131_072,
|
50
|
+
description:
|
51
|
+
'OpenAI GPT-OSS 20B 是一款拥有 200 亿参数的顶尖语言模型,内置浏览器搜索和代码执行功能,并具备推理能力。',
|
52
|
+
displayName: 'GPT OSS 20B',
|
53
|
+
id: 'openai/gpt-oss-20b',
|
54
|
+
maxOutput: 32_768,
|
55
|
+
pricing: {
|
56
|
+
input: 0.1,
|
57
|
+
output: 0.5,
|
58
|
+
},
|
59
|
+
releasedAt: '2025-08-06',
|
60
|
+
type: 'chat',
|
61
|
+
},
|
26
62
|
{
|
27
63
|
abilities: {
|
28
64
|
functionCall: true,
|
@@ -75,7 +111,7 @@ const groqChatModels: AIChatModelCard[] = [
|
|
75
111
|
contextWindowTokens: 131_072,
|
76
112
|
displayName: 'Qwen3 32B',
|
77
113
|
id: 'qwen/qwen3-32b',
|
78
|
-
maxOutput:
|
114
|
+
maxOutput: 131_072,
|
79
115
|
pricing: {
|
80
116
|
input: 0.29,
|
81
117
|
output: 0.59,
|
@@ -132,7 +168,7 @@ const groqChatModels: AIChatModelCard[] = [
|
|
132
168
|
abilities: {
|
133
169
|
functionCall: true,
|
134
170
|
},
|
135
|
-
contextWindowTokens:
|
171
|
+
contextWindowTokens: 32_768,
|
136
172
|
description:
|
137
173
|
'Meta Llama 3.3 多语言大语言模型 ( LLM ) 是 70B(文本输入/文本输出)中的预训练和指令调整生成模型。 Llama 3.3 指令调整的纯文本模型针对多语言对话用例进行了优化,并且在常见行业基准上优于许多可用的开源和封闭式聊天模型。',
|
138
174
|
displayName: 'Llama 3.3 70B Versatile',
|
@@ -34,7 +34,7 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
34
34
|
deploymentName: 'qwen3-coder-plus',
|
35
35
|
},
|
36
36
|
contextWindowTokens: 1_048_576,
|
37
|
-
description: '通义千问代码模型。最新的 Qwen3-Coder
|
37
|
+
description: '通义千问代码模型。最新的 Qwen3-Coder 系列模型是基于 Qwen3 的代码生成模型,具有强大的Coding Agent能力,擅长工具调用和环境交互,能够实现自主编程,代码能力卓越的同时兼具通用能力。',
|
38
38
|
displayName: 'Qwen3 Coder Plus',
|
39
39
|
id: 'qwen3-coder-plus',
|
40
40
|
maxOutput: 65_536,
|
@@ -45,7 +45,29 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
45
45
|
input: 6,
|
46
46
|
output: 24,
|
47
47
|
},
|
48
|
-
releasedAt: '2025-07-
|
48
|
+
releasedAt: '2025-07-22',
|
49
|
+
type: 'chat',
|
50
|
+
},
|
51
|
+
{
|
52
|
+
abilities: {
|
53
|
+
functionCall: true,
|
54
|
+
},
|
55
|
+
config: {
|
56
|
+
deploymentName: 'qwen3-coder-flash',
|
57
|
+
},
|
58
|
+
contextWindowTokens: 1_048_576,
|
59
|
+
description: '通义千问代码模型。最新的 Qwen3-Coder 系列模型是基于 Qwen3 的代码生成模型,具有强大的Coding Agent能力,擅长工具调用和环境交互,能够实现自主编程,代码能力卓越的同时兼具通用能力。',
|
60
|
+
displayName: 'Qwen3 Coder Flash',
|
61
|
+
id: 'qwen3-coder-flash',
|
62
|
+
maxOutput: 65_536,
|
63
|
+
organization: 'Qwen',
|
64
|
+
pricing: {
|
65
|
+
cachedInput: 0.6, // tokens 32K ~ 128K
|
66
|
+
currency: 'CNY',
|
67
|
+
input: 1.5,
|
68
|
+
output: 6,
|
69
|
+
},
|
70
|
+
releasedAt: '2025-07-28',
|
49
71
|
type: 'chat',
|
50
72
|
},
|
51
73
|
{
|
@@ -27,6 +27,9 @@ type anthropicTools = Anthropic.Tool | Anthropic.WebSearchTool20250305;
|
|
27
27
|
|
28
28
|
const modelsWithSmallContextWindow = new Set(['claude-3-opus-20240229', 'claude-3-haiku-20240307']);
|
29
29
|
|
30
|
+
// Opus 4.1 models that don't allow both temperature and top_p parameters
|
31
|
+
const opus41Models = new Set(['claude-opus-4-1', 'claude-opus-4-1-20250805']);
|
32
|
+
|
30
33
|
const DEFAULT_BASE_URL = 'https://api.anthropic.com';
|
31
34
|
|
32
35
|
interface AnthropicAIParams extends ClientOptions {
|
@@ -189,6 +192,10 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
|
|
189
192
|
} satisfies Anthropic.MessageCreateParams;
|
190
193
|
}
|
191
194
|
|
195
|
+
// For Opus 4.1 models, we can only set either temperature OR top_p, not both
|
196
|
+
const isOpus41Model = opus41Models.has(model);
|
197
|
+
const shouldSetTemperature = payload.temperature !== undefined;
|
198
|
+
|
192
199
|
return {
|
193
200
|
// claude 3 series model hax max output token of 4096, 3.x series has 8192
|
194
201
|
// https://docs.anthropic.com/en/docs/about-claude/models/all-models#:~:text=200K-,Max%20output,-Normal%3A
|
@@ -196,9 +203,15 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
|
|
196
203
|
messages: postMessages,
|
197
204
|
model,
|
198
205
|
system: systemPrompts,
|
199
|
-
|
206
|
+
// For Opus 4.1 models: prefer temperature over top_p if both are provided
|
207
|
+
temperature: isOpus41Model
|
208
|
+
? (shouldSetTemperature ? temperature / 2 : undefined)
|
209
|
+
: (payload.temperature !== undefined ? temperature / 2 : undefined),
|
200
210
|
tools: postTools,
|
201
|
-
top_p
|
211
|
+
// For Opus 4.1 models: only set top_p if temperature is not set
|
212
|
+
top_p: isOpus41Model
|
213
|
+
? (shouldSetTemperature ? undefined : top_p)
|
214
|
+
: top_p,
|
202
215
|
} satisfies Anthropic.MessageCreateParams;
|
203
216
|
}
|
204
217
|
|
@@ -30,8 +30,8 @@ export const MODEL_LIST_CONFIGS = {
|
|
30
30
|
},
|
31
31
|
openai: {
|
32
32
|
excludeKeywords: ['audio'],
|
33
|
-
functionCallKeywords: ['4o', '4.1', 'o3', 'o4'],
|
34
|
-
reasoningKeywords: ['o1', 'o3', 'o4'],
|
33
|
+
functionCallKeywords: ['4o', '4.1', 'o3', 'o4', 'oss'],
|
34
|
+
reasoningKeywords: ['o1', 'o3', 'o4', 'oss'],
|
35
35
|
visionKeywords: ['4o', '4.1', 'o4'],
|
36
36
|
},
|
37
37
|
qwen: {
|
@@ -7,63 +7,109 @@ import { OllamaStream } from './ollama';
|
|
7
7
|
|
8
8
|
describe('OllamaStream', () => {
|
9
9
|
describe('should transform Ollama stream to protocol stream', () => {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
const messages = [
|
14
|
-
'<think>',
|
15
|
-
'这是一个思考过程',
|
16
|
-
',需要仔细分析问题。',
|
17
|
-
'</think>',
|
18
|
-
'根据分析,我的答案是:',
|
19
|
-
'这是最终答案。',
|
20
|
-
];
|
10
|
+
describe('reasoning', () => {
|
11
|
+
it('reasoning with thinking tag', async () => {
|
12
|
+
vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('2');
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
14
|
+
const messages = [
|
15
|
+
'<think>',
|
16
|
+
'这是一个思考过程',
|
17
|
+
',需要仔细分析问题。',
|
18
|
+
'</think>',
|
19
|
+
'根据分析,我的答案是:',
|
20
|
+
'这是最终答案。',
|
21
|
+
];
|
22
|
+
|
23
|
+
const mockOllamaStream = new ReadableStream<ChatResponse>({
|
24
|
+
start(controller) {
|
25
|
+
messages.forEach((content) => {
|
26
|
+
controller.enqueue({ message: { content }, done: false } as ChatResponse);
|
27
|
+
});
|
28
|
+
controller.enqueue({ message: { content: '' }, done: true } as ChatResponse);
|
29
|
+
controller.close();
|
30
|
+
},
|
31
|
+
});
|
32
|
+
|
33
|
+
const protocolStream = OllamaStream(mockOllamaStream);
|
34
|
+
|
35
|
+
const decoder = new TextDecoder();
|
36
|
+
const chunks = [];
|
37
|
+
|
38
|
+
// @ts-ignore
|
39
|
+
for await (const chunk of protocolStream) {
|
40
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
41
|
+
}
|
42
|
+
|
43
|
+
expect(chunks).toEqual(
|
44
|
+
[
|
45
|
+
'id: chat_2',
|
46
|
+
'event: reasoning',
|
47
|
+
`data: ""\n`,
|
48
|
+
'id: chat_2',
|
49
|
+
'event: reasoning',
|
50
|
+
`data: "这是一个思考过程"\n`,
|
51
|
+
'id: chat_2',
|
52
|
+
'event: reasoning',
|
53
|
+
`data: ",需要仔细分析问题。"\n`,
|
54
|
+
'id: chat_2',
|
55
|
+
'event: text',
|
56
|
+
`data: ""\n`,
|
57
|
+
'id: chat_2',
|
58
|
+
'event: text',
|
59
|
+
`data: "根据分析,我的答案是:"\n`,
|
60
|
+
'id: chat_2',
|
61
|
+
'event: text',
|
62
|
+
`data: "这是最终答案。"\n`,
|
63
|
+
'id: chat_2',
|
64
|
+
'event: stop',
|
65
|
+
`data: "finished"\n`,
|
66
|
+
].map((line) => `${line}\n`),
|
67
|
+
);
|
30
68
|
});
|
31
69
|
|
32
|
-
|
70
|
+
it('thinking field', async () => {
|
71
|
+
vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('1');
|
33
72
|
|
34
|
-
|
35
|
-
|
73
|
+
const mockOllamaStream = new ReadableStream<ChatResponse>({
|
74
|
+
start(controller) {
|
75
|
+
controller.enqueue({ message: { thinking: 'Hello' }, done: false } as ChatResponse);
|
76
|
+
controller.enqueue({ message: { thinking: ' world!' }, done: false } as ChatResponse);
|
77
|
+
controller.enqueue({ message: { thinking: '' }, done: true } as ChatResponse);
|
36
78
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
}
|
79
|
+
controller.close();
|
80
|
+
},
|
81
|
+
});
|
41
82
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
'
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
83
|
+
const onStartMock = vi.fn();
|
84
|
+
const onTextMock = vi.fn();
|
85
|
+
const onCompletionMock = vi.fn();
|
86
|
+
|
87
|
+
const protocolStream = OllamaStream(mockOllamaStream, {
|
88
|
+
onStart: onStartMock,
|
89
|
+
onText: onTextMock,
|
90
|
+
onCompletion: onCompletionMock,
|
91
|
+
});
|
92
|
+
|
93
|
+
const decoder = new TextDecoder();
|
94
|
+
const chunks = [];
|
95
|
+
|
96
|
+
// @ts-ignore
|
97
|
+
for await (const chunk of protocolStream) {
|
98
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
99
|
+
}
|
100
|
+
|
101
|
+
expect(chunks).toEqual([
|
102
|
+
'id: chat_1\n',
|
103
|
+
'event: reasoning\n',
|
104
|
+
`data: "Hello"\n\n`,
|
105
|
+
'id: chat_1\n',
|
106
|
+
'event: reasoning\n',
|
107
|
+
`data: " world!"\n\n`,
|
108
|
+
'id: chat_1\n',
|
109
|
+
'event: stop\n',
|
110
|
+
`data: "finished"\n\n`,
|
111
|
+
]);
|
112
|
+
});
|
67
113
|
});
|
68
114
|
|
69
115
|
it('text', async () => {
|
@@ -17,6 +17,10 @@ const transformOllamaStream = (chunk: ChatResponse, stack: StreamContext): Strea
|
|
17
17
|
return { data: 'finished', id: stack.id, type: 'stop' };
|
18
18
|
}
|
19
19
|
|
20
|
+
if (chunk.message.thinking) {
|
21
|
+
return { data: chunk.message.thinking, id: stack.id, type: 'reasoning' };
|
22
|
+
}
|
23
|
+
|
20
24
|
if (chunk.message.tool_calls && chunk.message.tool_calls.length > 0) {
|
21
25
|
return {
|
22
26
|
data: chunk.message.tool_calls.map((value, index) => ({
|