@lobehub/chat 1.47.22 → 1.48.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/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +4 -0
- package/locales/ar/components.json +1 -0
- package/locales/ar/models.json +6 -0
- package/locales/bg-BG/chat.json +4 -0
- package/locales/bg-BG/components.json +1 -0
- package/locales/bg-BG/models.json +6 -0
- package/locales/de-DE/chat.json +4 -0
- package/locales/de-DE/components.json +1 -0
- package/locales/de-DE/models.json +6 -0
- package/locales/en-US/chat.json +4 -0
- package/locales/en-US/components.json +1 -0
- package/locales/en-US/models.json +6 -0
- package/locales/es-ES/chat.json +4 -0
- package/locales/es-ES/components.json +1 -0
- package/locales/es-ES/models.json +6 -0
- package/locales/fa-IR/chat.json +4 -0
- package/locales/fa-IR/components.json +1 -0
- package/locales/fa-IR/models.json +6 -0
- package/locales/fr-FR/chat.json +4 -0
- package/locales/fr-FR/components.json +1 -0
- package/locales/fr-FR/models.json +6 -0
- package/locales/it-IT/chat.json +4 -0
- package/locales/it-IT/components.json +1 -0
- package/locales/it-IT/models.json +6 -0
- package/locales/ja-JP/chat.json +4 -0
- package/locales/ja-JP/components.json +1 -0
- package/locales/ja-JP/models.json +6 -0
- package/locales/ko-KR/chat.json +4 -0
- package/locales/ko-KR/components.json +1 -0
- package/locales/ko-KR/models.json +6 -0
- package/locales/nl-NL/chat.json +4 -0
- package/locales/nl-NL/components.json +1 -0
- package/locales/nl-NL/models.json +6 -0
- package/locales/pl-PL/chat.json +4 -0
- package/locales/pl-PL/components.json +1 -0
- package/locales/pl-PL/models.json +6 -0
- package/locales/pt-BR/chat.json +4 -0
- package/locales/pt-BR/components.json +1 -0
- package/locales/pt-BR/models.json +6 -0
- package/locales/ru-RU/chat.json +4 -0
- package/locales/ru-RU/components.json +1 -0
- package/locales/ru-RU/models.json +6 -0
- package/locales/tr-TR/chat.json +4 -0
- package/locales/tr-TR/components.json +1 -0
- package/locales/tr-TR/models.json +6 -0
- package/locales/vi-VN/chat.json +4 -0
- package/locales/vi-VN/components.json +1 -0
- package/locales/vi-VN/models.json +6 -0
- package/locales/zh-CN/chat.json +4 -0
- package/locales/zh-CN/components.json +1 -0
- package/locales/zh-CN/modelProvider.json +2 -2
- package/locales/zh-CN/models.json +7 -1
- package/locales/zh-TW/chat.json +4 -0
- package/locales/zh-TW/components.json +1 -0
- package/locales/zh-TW/models.json +6 -0
- package/package.json +1 -1
- package/src/components/ModelSelect/index.tsx +16 -1
- package/src/config/aiModels/deepseek.ts +3 -0
- package/src/config/aiModels/hunyuan.ts +132 -12
- package/src/config/aiModels/qwen.ts +19 -2
- package/src/config/modelProviders/hunyuan.ts +2 -0
- package/src/database/client/migrations.json +13 -2
- package/src/database/migrations/0014_add_message_reasoning.sql +1 -0
- package/src/database/migrations/meta/0014_snapshot.json +3961 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/schemas/message.ts +2 -3
- package/src/database/server/models/__tests__/message.test.ts +5 -4
- package/src/database/server/models/message.ts +35 -13
- package/src/database/server/models/topic.ts +3 -2
- package/src/features/Conversation/Messages/Assistant/Reasoning/index.tsx +123 -0
- package/src/features/Conversation/Messages/Assistant/index.tsx +8 -1
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/index.ts +2 -2
- package/src/libs/agent-runtime/deepseek/index.ts +1 -1
- package/src/libs/agent-runtime/google/index.ts +7 -5
- package/src/libs/agent-runtime/hunyuan/index.ts +24 -0
- package/src/libs/agent-runtime/qwen/index.ts +8 -3
- package/src/libs/agent-runtime/stepfun/index.ts +7 -1
- package/src/libs/agent-runtime/utils/streams/openai.test.ts +203 -0
- package/src/libs/agent-runtime/utils/streams/openai.ts +8 -1
- package/src/libs/agent-runtime/utils/streams/protocol.ts +1 -1
- package/src/locales/default/chat.ts +4 -0
- package/src/locales/default/components.ts +1 -0
- package/src/server/routers/lambda/message.ts +4 -2
- package/src/services/message/client.test.ts +1 -1
- package/src/services/message/type.ts +1 -1
- package/src/store/chat/selectors.ts +1 -0
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +60 -14
- package/src/store/chat/slices/aiChat/initialState.ts +5 -0
- package/src/store/chat/slices/aiChat/selectors.ts +9 -0
- package/src/store/chat/slices/message/action.ts +4 -1
- package/src/types/aiModel.ts +5 -14
- package/src/types/message/base.ts +59 -0
- package/src/types/message/chat.ts +136 -0
- package/src/types/message/index.ts +2 -135
- package/src/utils/fetch/__tests__/fetchSSE.test.ts +34 -0
- package/src/utils/fetch/fetchSSE.ts +38 -3
@@ -0,0 +1,136 @@
|
|
1
|
+
import { IPluginErrorType } from '@lobehub/chat-plugin-sdk';
|
2
|
+
|
3
|
+
import { ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
|
4
|
+
import { ErrorType } from '@/types/fetch';
|
5
|
+
import { MessageRoleType, ModelReasoning } from '@/types/message/base';
|
6
|
+
import { ChatPluginPayload, ChatToolPayload } from '@/types/message/tools';
|
7
|
+
import { Translate } from '@/types/message/translate';
|
8
|
+
import { MetaData } from '@/types/meta';
|
9
|
+
import { MessageSemanticSearchChunk } from '@/types/rag';
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 聊天消息错误对象
|
13
|
+
*/
|
14
|
+
export interface ChatMessageError {
|
15
|
+
body?: any;
|
16
|
+
message: string;
|
17
|
+
type: ErrorType | IPluginErrorType | ILobeAgentRuntimeErrorType;
|
18
|
+
}
|
19
|
+
|
20
|
+
export interface ChatTranslate extends Translate {
|
21
|
+
content?: string;
|
22
|
+
}
|
23
|
+
|
24
|
+
export interface ChatTTS {
|
25
|
+
contentMd5?: string;
|
26
|
+
file?: string;
|
27
|
+
voice?: string;
|
28
|
+
}
|
29
|
+
|
30
|
+
export interface ChatFileItem {
|
31
|
+
fileType: string;
|
32
|
+
id: string;
|
33
|
+
name: string;
|
34
|
+
size: number;
|
35
|
+
url: string;
|
36
|
+
}
|
37
|
+
|
38
|
+
export interface ChatImageItem {
|
39
|
+
alt: string;
|
40
|
+
id: string;
|
41
|
+
url: string;
|
42
|
+
}
|
43
|
+
|
44
|
+
export interface ChatFileChunk {
|
45
|
+
fileId: string;
|
46
|
+
fileType: string;
|
47
|
+
fileUrl: string;
|
48
|
+
filename: string;
|
49
|
+
id: string;
|
50
|
+
similarity?: number;
|
51
|
+
text: string;
|
52
|
+
}
|
53
|
+
|
54
|
+
export interface ChatMessageExtra {
|
55
|
+
fromModel?: string;
|
56
|
+
fromProvider?: string;
|
57
|
+
// 翻译
|
58
|
+
translate?: ChatTranslate | false | null;
|
59
|
+
// TTS
|
60
|
+
tts?: ChatTTS;
|
61
|
+
}
|
62
|
+
|
63
|
+
export interface ChatMessage {
|
64
|
+
chunksList?: ChatFileChunk[];
|
65
|
+
content: string;
|
66
|
+
createdAt: number;
|
67
|
+
error?: ChatMessageError | null;
|
68
|
+
// 扩展字段
|
69
|
+
extra?: ChatMessageExtra;
|
70
|
+
|
71
|
+
fileList?: ChatFileItem[];
|
72
|
+
/**
|
73
|
+
* this is a deprecated field, only use in client db
|
74
|
+
* and should be remove after migrate to pglite
|
75
|
+
* this field is replaced by fileList and imageList
|
76
|
+
* @deprecated
|
77
|
+
*/
|
78
|
+
files?: string[];
|
79
|
+
id: string;
|
80
|
+
imageList?: ChatImageItem[];
|
81
|
+
meta: MetaData;
|
82
|
+
|
83
|
+
/**
|
84
|
+
* observation id
|
85
|
+
*/
|
86
|
+
observationId?: string;
|
87
|
+
/**
|
88
|
+
* parent message id
|
89
|
+
*/
|
90
|
+
parentId?: string;
|
91
|
+
|
92
|
+
plugin?: ChatPluginPayload;
|
93
|
+
pluginState?: any;
|
94
|
+
/**
|
95
|
+
* quoted other message's id
|
96
|
+
*/
|
97
|
+
quotaId?: string;
|
98
|
+
ragQuery?: string | null;
|
99
|
+
ragQueryId?: string | null;
|
100
|
+
ragRawQuery?: string | null;
|
101
|
+
|
102
|
+
reasoning?: ModelReasoning | null;
|
103
|
+
|
104
|
+
/**
|
105
|
+
* message role type
|
106
|
+
*/
|
107
|
+
role: MessageRoleType;
|
108
|
+
sessionId?: string;
|
109
|
+
threadId?: string | null;
|
110
|
+
tool_call_id?: string;
|
111
|
+
tools?: ChatToolPayload[];
|
112
|
+
/**
|
113
|
+
* 保存到主题的消息
|
114
|
+
*/
|
115
|
+
topicId?: string;
|
116
|
+
/**
|
117
|
+
* 观测链路 id
|
118
|
+
*/
|
119
|
+
traceId?: string;
|
120
|
+
updatedAt: number;
|
121
|
+
}
|
122
|
+
|
123
|
+
export interface CreateMessageParams
|
124
|
+
extends Partial<Omit<ChatMessage, 'content' | 'role' | 'topicId' | 'chunksList'>> {
|
125
|
+
content: string;
|
126
|
+
error?: ChatMessageError | null;
|
127
|
+
fileChunks?: MessageSemanticSearchChunk[];
|
128
|
+
files?: string[];
|
129
|
+
fromModel?: string;
|
130
|
+
fromProvider?: string;
|
131
|
+
role: MessageRoleType;
|
132
|
+
sessionId: string;
|
133
|
+
threadId?: string | null;
|
134
|
+
topicId?: string;
|
135
|
+
traceId?: string;
|
136
|
+
}
|
@@ -1,142 +1,9 @@
|
|
1
|
-
import { IPluginErrorType } from '@lobehub/chat-plugin-sdk';
|
2
|
-
|
3
|
-
import { ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
|
4
|
-
import { ErrorType } from '@/types/fetch';
|
5
1
|
import { UploadFileItem } from '@/types/files';
|
6
|
-
import { MessageSemanticSearchChunk } from '@/types/rag';
|
7
|
-
|
8
|
-
import { BaseDataModel } from '../meta';
|
9
|
-
import { ChatPluginPayload, ChatToolPayload } from './tools';
|
10
|
-
import { Translate } from './translate';
|
11
|
-
|
12
|
-
export type MessageRoleType = 'user' | 'system' | 'assistant' | 'tool';
|
13
|
-
|
14
|
-
/**
|
15
|
-
* 聊天消息错误对象
|
16
|
-
*/
|
17
|
-
export interface ChatMessageError {
|
18
|
-
body?: any;
|
19
|
-
message: string;
|
20
|
-
type: ErrorType | IPluginErrorType | ILobeAgentRuntimeErrorType;
|
21
|
-
}
|
22
|
-
|
23
|
-
export interface ChatTranslate extends Translate {
|
24
|
-
content?: string;
|
25
|
-
}
|
26
|
-
|
27
|
-
export interface ChatTTS {
|
28
|
-
contentMd5?: string;
|
29
|
-
file?: string;
|
30
|
-
voice?: string;
|
31
|
-
}
|
32
2
|
|
3
|
+
export * from './base';
|
4
|
+
export * from './chat';
|
33
5
|
export * from './tools';
|
34
6
|
|
35
|
-
export interface ChatFileItem {
|
36
|
-
fileType: string;
|
37
|
-
id: string;
|
38
|
-
name: string;
|
39
|
-
size: number;
|
40
|
-
url: string;
|
41
|
-
}
|
42
|
-
|
43
|
-
export interface ChatImageItem {
|
44
|
-
alt: string;
|
45
|
-
id: string;
|
46
|
-
url: string;
|
47
|
-
}
|
48
|
-
|
49
|
-
export interface ChatFileChunk {
|
50
|
-
fileId: string;
|
51
|
-
fileType: string;
|
52
|
-
fileUrl: string;
|
53
|
-
filename: string;
|
54
|
-
id: string;
|
55
|
-
similarity?: number;
|
56
|
-
text: string;
|
57
|
-
}
|
58
|
-
|
59
|
-
export interface ChatMessageExtra {
|
60
|
-
fromModel?: string;
|
61
|
-
fromProvider?: string;
|
62
|
-
// 翻译
|
63
|
-
translate?: ChatTranslate | false | null;
|
64
|
-
// TTS
|
65
|
-
tts?: ChatTTS;
|
66
|
-
}
|
67
|
-
|
68
|
-
export interface ChatMessage extends BaseDataModel {
|
69
|
-
chunksList?: ChatFileChunk[];
|
70
|
-
content: string;
|
71
|
-
error?: ChatMessageError | null;
|
72
|
-
|
73
|
-
// 扩展字段
|
74
|
-
extra?: ChatMessageExtra;
|
75
|
-
fileList?: ChatFileItem[];
|
76
|
-
/**
|
77
|
-
* this is a deprecated field, only use in client db
|
78
|
-
* and should be remove after migrate to pglite
|
79
|
-
* this field is replaced by fileList and imageList
|
80
|
-
* @deprecated
|
81
|
-
*/
|
82
|
-
files?: string[];
|
83
|
-
imageList?: ChatImageItem[];
|
84
|
-
/**
|
85
|
-
* observation id
|
86
|
-
*/
|
87
|
-
observationId?: string;
|
88
|
-
|
89
|
-
/**
|
90
|
-
* parent message id
|
91
|
-
*/
|
92
|
-
parentId?: string;
|
93
|
-
plugin?: ChatPluginPayload;
|
94
|
-
|
95
|
-
pluginState?: any;
|
96
|
-
/**
|
97
|
-
* quoted other message's id
|
98
|
-
*/
|
99
|
-
quotaId?: string;
|
100
|
-
ragQuery?: string | null;
|
101
|
-
ragQueryId?: string | null;
|
102
|
-
ragRawQuery?: string | null;
|
103
|
-
/**
|
104
|
-
* message role type
|
105
|
-
*/
|
106
|
-
role: MessageRoleType;
|
107
|
-
|
108
|
-
sessionId?: string;
|
109
|
-
threadId?: string | null;
|
110
|
-
|
111
|
-
tool_call_id?: string;
|
112
|
-
tools?: ChatToolPayload[];
|
113
|
-
/**
|
114
|
-
* 保存到主题的消息
|
115
|
-
*/
|
116
|
-
topicId?: string;
|
117
|
-
/**
|
118
|
-
* 观测链路 id
|
119
|
-
*/
|
120
|
-
traceId?: string;
|
121
|
-
}
|
122
|
-
|
123
|
-
export type ChatMessageMap = Record<string, ChatMessage>;
|
124
|
-
|
125
|
-
export interface CreateMessageParams
|
126
|
-
extends Partial<Omit<ChatMessage, 'content' | 'role' | 'topicId' | 'chunksList'>> {
|
127
|
-
content: string;
|
128
|
-
error?: ChatMessageError | null;
|
129
|
-
fileChunks?: MessageSemanticSearchChunk[];
|
130
|
-
files?: string[];
|
131
|
-
fromModel?: string;
|
132
|
-
fromProvider?: string;
|
133
|
-
role: MessageRoleType;
|
134
|
-
sessionId: string;
|
135
|
-
threadId?: string | null;
|
136
|
-
topicId?: string;
|
137
|
-
traceId?: string;
|
138
|
-
}
|
139
|
-
|
140
7
|
export interface SendMessageParams {
|
141
8
|
/**
|
142
9
|
* create a thread
|
@@ -154,6 +154,40 @@ describe('fetchSSE', () => {
|
|
154
154
|
});
|
155
155
|
});
|
156
156
|
|
157
|
+
it('should handle reasoning event with smoothing correctly', async () => {
|
158
|
+
const mockOnMessageHandle = vi.fn();
|
159
|
+
const mockOnFinish = vi.fn();
|
160
|
+
|
161
|
+
(fetchEventSource as any).mockImplementationOnce(
|
162
|
+
async (url: string, options: FetchEventSourceInit) => {
|
163
|
+
options.onopen!({ clone: () => ({ ok: true, headers: new Headers() }) } as any);
|
164
|
+
options.onmessage!({ event: 'reasoning', data: JSON.stringify('Hello') } as any);
|
165
|
+
await sleep(100);
|
166
|
+
options.onmessage!({ event: 'reasoning', data: JSON.stringify(' World') } as any);
|
167
|
+
await sleep(100);
|
168
|
+
options.onmessage!({ event: 'text', data: JSON.stringify('hi') } as any);
|
169
|
+
},
|
170
|
+
);
|
171
|
+
|
172
|
+
await fetchSSE('/', {
|
173
|
+
onMessageHandle: mockOnMessageHandle,
|
174
|
+
onFinish: mockOnFinish,
|
175
|
+
smoothing: true,
|
176
|
+
});
|
177
|
+
|
178
|
+
expect(mockOnMessageHandle).toHaveBeenNthCalledWith(1, { text: 'Hell', type: 'reasoning' });
|
179
|
+
expect(mockOnMessageHandle).toHaveBeenNthCalledWith(2, { text: 'o', type: 'reasoning' });
|
180
|
+
expect(mockOnMessageHandle).toHaveBeenNthCalledWith(3, { text: ' Wor', type: 'reasoning' });
|
181
|
+
// more assertions for each character...
|
182
|
+
expect(mockOnFinish).toHaveBeenCalledWith('hi', {
|
183
|
+
observationId: null,
|
184
|
+
toolCalls: undefined,
|
185
|
+
reasoning: 'Hello World',
|
186
|
+
traceId: null,
|
187
|
+
type: 'done',
|
188
|
+
});
|
189
|
+
});
|
190
|
+
|
157
191
|
it('should handle tool_calls event with smoothing correctly', async () => {
|
158
192
|
const mockOnMessageHandle = vi.fn();
|
159
193
|
const mockOnFinish = vi.fn();
|
@@ -21,6 +21,7 @@ export type OnFinishHandler = (
|
|
21
21
|
text: string,
|
22
22
|
context: {
|
23
23
|
observationId?: string | null;
|
24
|
+
reasoning?: string;
|
24
25
|
toolCalls?: MessageToolCall[];
|
25
26
|
traceId?: string | null;
|
26
27
|
type?: SSEFinishType;
|
@@ -32,6 +33,11 @@ export interface MessageTextChunk {
|
|
32
33
|
type: 'text';
|
33
34
|
}
|
34
35
|
|
36
|
+
export interface MessageReasoningChunk {
|
37
|
+
text: string;
|
38
|
+
type: 'reasoning';
|
39
|
+
}
|
40
|
+
|
35
41
|
interface MessageToolCallsChunk {
|
36
42
|
isAnimationActives?: boolean[];
|
37
43
|
tool_calls: MessageToolCall[];
|
@@ -43,7 +49,9 @@ export interface FetchSSEOptions {
|
|
43
49
|
onAbort?: (text: string) => Promise<void>;
|
44
50
|
onErrorHandle?: (error: ChatMessageError) => void;
|
45
51
|
onFinish?: OnFinishHandler;
|
46
|
-
onMessageHandle?: (
|
52
|
+
onMessageHandle?: (
|
53
|
+
chunk: MessageTextChunk | MessageToolCallsChunk | MessageReasoningChunk,
|
54
|
+
) => void;
|
47
55
|
smoothing?: SmoothingParams | boolean;
|
48
56
|
}
|
49
57
|
|
@@ -233,7 +241,6 @@ const createSmoothToolCalls = (params: {
|
|
233
241
|
*/
|
234
242
|
// eslint-disable-next-line no-undef
|
235
243
|
export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptions = {}) => {
|
236
|
-
let output = '';
|
237
244
|
let toolCalls: undefined | MessageToolCall[];
|
238
245
|
let triggerOnMessageHandler = false;
|
239
246
|
|
@@ -247,6 +254,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
247
254
|
typeof smoothing === 'boolean' ? smoothing : (smoothing?.toolsCalling ?? true);
|
248
255
|
const smoothingSpeed = isObject(smoothing) ? smoothing.speed : undefined;
|
249
256
|
|
257
|
+
let output = '';
|
250
258
|
const textController = createSmoothMessage({
|
251
259
|
onTextUpdate: (delta, text) => {
|
252
260
|
output = text;
|
@@ -255,6 +263,15 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
255
263
|
startSpeed: smoothingSpeed,
|
256
264
|
});
|
257
265
|
|
266
|
+
let thinking = '';
|
267
|
+
const thinkingController = createSmoothMessage({
|
268
|
+
onTextUpdate: (delta, text) => {
|
269
|
+
thinking = text;
|
270
|
+
options.onMessageHandle?.({ text: delta, type: 'reasoning' });
|
271
|
+
},
|
272
|
+
startSpeed: smoothingSpeed,
|
273
|
+
});
|
274
|
+
|
258
275
|
const toolCallsController = createSmoothToolCalls({
|
259
276
|
onToolCallsUpdate: (toolCalls, isAnimationActives) => {
|
260
277
|
options.onMessageHandle?.({ isAnimationActives, tool_calls: toolCalls, type: 'tool_calls' });
|
@@ -333,6 +350,18 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
333
350
|
|
334
351
|
break;
|
335
352
|
}
|
353
|
+
case 'reasoning': {
|
354
|
+
if (textSmoothing) {
|
355
|
+
thinkingController.pushToQueue(data);
|
356
|
+
|
357
|
+
if (!thinkingController.isAnimationActive) thinkingController.startAnimation();
|
358
|
+
} else {
|
359
|
+
thinking += data;
|
360
|
+
options.onMessageHandle?.({ text: data, type: 'reasoning' });
|
361
|
+
}
|
362
|
+
|
363
|
+
break;
|
364
|
+
}
|
336
365
|
|
337
366
|
case 'tool_calls': {
|
338
367
|
// get finial
|
@@ -389,7 +418,13 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
389
418
|
await toolCallsController.startAnimations(END_ANIMATION_SPEED);
|
390
419
|
}
|
391
420
|
|
392
|
-
await options?.onFinish?.(output, {
|
421
|
+
await options?.onFinish?.(output, {
|
422
|
+
observationId,
|
423
|
+
reasoning: !!thinking ? thinking : undefined,
|
424
|
+
toolCalls,
|
425
|
+
traceId,
|
426
|
+
type: finishedType,
|
427
|
+
});
|
393
428
|
}
|
394
429
|
}
|
395
430
|
|