@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.
Files changed (98) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/chat.json +4 -0
  4. package/locales/ar/components.json +1 -0
  5. package/locales/ar/models.json +6 -0
  6. package/locales/bg-BG/chat.json +4 -0
  7. package/locales/bg-BG/components.json +1 -0
  8. package/locales/bg-BG/models.json +6 -0
  9. package/locales/de-DE/chat.json +4 -0
  10. package/locales/de-DE/components.json +1 -0
  11. package/locales/de-DE/models.json +6 -0
  12. package/locales/en-US/chat.json +4 -0
  13. package/locales/en-US/components.json +1 -0
  14. package/locales/en-US/models.json +6 -0
  15. package/locales/es-ES/chat.json +4 -0
  16. package/locales/es-ES/components.json +1 -0
  17. package/locales/es-ES/models.json +6 -0
  18. package/locales/fa-IR/chat.json +4 -0
  19. package/locales/fa-IR/components.json +1 -0
  20. package/locales/fa-IR/models.json +6 -0
  21. package/locales/fr-FR/chat.json +4 -0
  22. package/locales/fr-FR/components.json +1 -0
  23. package/locales/fr-FR/models.json +6 -0
  24. package/locales/it-IT/chat.json +4 -0
  25. package/locales/it-IT/components.json +1 -0
  26. package/locales/it-IT/models.json +6 -0
  27. package/locales/ja-JP/chat.json +4 -0
  28. package/locales/ja-JP/components.json +1 -0
  29. package/locales/ja-JP/models.json +6 -0
  30. package/locales/ko-KR/chat.json +4 -0
  31. package/locales/ko-KR/components.json +1 -0
  32. package/locales/ko-KR/models.json +6 -0
  33. package/locales/nl-NL/chat.json +4 -0
  34. package/locales/nl-NL/components.json +1 -0
  35. package/locales/nl-NL/models.json +6 -0
  36. package/locales/pl-PL/chat.json +4 -0
  37. package/locales/pl-PL/components.json +1 -0
  38. package/locales/pl-PL/models.json +6 -0
  39. package/locales/pt-BR/chat.json +4 -0
  40. package/locales/pt-BR/components.json +1 -0
  41. package/locales/pt-BR/models.json +6 -0
  42. package/locales/ru-RU/chat.json +4 -0
  43. package/locales/ru-RU/components.json +1 -0
  44. package/locales/ru-RU/models.json +6 -0
  45. package/locales/tr-TR/chat.json +4 -0
  46. package/locales/tr-TR/components.json +1 -0
  47. package/locales/tr-TR/models.json +6 -0
  48. package/locales/vi-VN/chat.json +4 -0
  49. package/locales/vi-VN/components.json +1 -0
  50. package/locales/vi-VN/models.json +6 -0
  51. package/locales/zh-CN/chat.json +4 -0
  52. package/locales/zh-CN/components.json +1 -0
  53. package/locales/zh-CN/modelProvider.json +2 -2
  54. package/locales/zh-CN/models.json +7 -1
  55. package/locales/zh-TW/chat.json +4 -0
  56. package/locales/zh-TW/components.json +1 -0
  57. package/locales/zh-TW/models.json +6 -0
  58. package/package.json +1 -1
  59. package/src/components/ModelSelect/index.tsx +16 -1
  60. package/src/config/aiModels/deepseek.ts +3 -0
  61. package/src/config/aiModels/hunyuan.ts +132 -12
  62. package/src/config/aiModels/qwen.ts +19 -2
  63. package/src/config/modelProviders/hunyuan.ts +2 -0
  64. package/src/database/client/migrations.json +13 -2
  65. package/src/database/migrations/0014_add_message_reasoning.sql +1 -0
  66. package/src/database/migrations/meta/0014_snapshot.json +3961 -0
  67. package/src/database/migrations/meta/_journal.json +7 -0
  68. package/src/database/schemas/message.ts +2 -3
  69. package/src/database/server/models/__tests__/message.test.ts +5 -4
  70. package/src/database/server/models/message.ts +35 -13
  71. package/src/database/server/models/topic.ts +3 -2
  72. package/src/features/Conversation/Messages/Assistant/Reasoning/index.tsx +123 -0
  73. package/src/features/Conversation/Messages/Assistant/index.tsx +8 -1
  74. package/src/features/Conversation/components/MarkdownElements/LobeThinking/index.ts +2 -2
  75. package/src/libs/agent-runtime/deepseek/index.ts +1 -1
  76. package/src/libs/agent-runtime/google/index.ts +7 -5
  77. package/src/libs/agent-runtime/hunyuan/index.ts +24 -0
  78. package/src/libs/agent-runtime/qwen/index.ts +8 -3
  79. package/src/libs/agent-runtime/stepfun/index.ts +7 -1
  80. package/src/libs/agent-runtime/utils/streams/openai.test.ts +203 -0
  81. package/src/libs/agent-runtime/utils/streams/openai.ts +8 -1
  82. package/src/libs/agent-runtime/utils/streams/protocol.ts +1 -1
  83. package/src/locales/default/chat.ts +4 -0
  84. package/src/locales/default/components.ts +1 -0
  85. package/src/server/routers/lambda/message.ts +4 -2
  86. package/src/services/message/client.test.ts +1 -1
  87. package/src/services/message/type.ts +1 -1
  88. package/src/store/chat/selectors.ts +1 -0
  89. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +60 -14
  90. package/src/store/chat/slices/aiChat/initialState.ts +5 -0
  91. package/src/store/chat/slices/aiChat/selectors.ts +9 -0
  92. package/src/store/chat/slices/message/action.ts +4 -1
  93. package/src/types/aiModel.ts +5 -14
  94. package/src/types/message/base.ts +59 -0
  95. package/src/types/message/chat.ts +136 -0
  96. package/src/types/message/index.ts +2 -135
  97. package/src/utils/fetch/__tests__/fetchSSE.test.ts +34 -0
  98. 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?: (chunk: MessageTextChunk | MessageToolCallsChunk) => void;
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, { observationId, toolCalls, traceId, type: finishedType });
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