@lobehub/lobehub 2.0.0-next.27 → 2.0.0-next.29
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/package.json +1 -1
- package/packages/database/package.json +1 -1
- package/packages/database/src/models/message.ts +29 -2
- package/packages/types/src/discover/mcp.ts +6 -0
- package/packages/types/src/message/ui/params.ts +0 -49
- package/packages/types/src/plugins/mcp.ts +4 -1
- package/renovate.json +4 -30
- package/src/features/MCP/utils.test.ts +91 -0
- package/src/features/MCP/utils.ts +20 -2
- package/src/features/PluginStore/Content.tsx +2 -3
- package/src/features/PluginStore/McpList/index.tsx +6 -2
- package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +35 -35
- package/src/server/routers/lambda/market/index.ts +4 -2
- package/src/server/routers/lambda/message.ts +104 -16
- package/src/services/mcp.ts +40 -6
- package/src/services/message/index.ts +63 -35
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +17 -10
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +4 -4
- package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +2 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +2 -2
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +7 -2
- package/src/store/chat/slices/builtinTool/actions/search.ts +3 -3
- package/src/store/chat/slices/message/action.test.ts +152 -15
- package/src/store/chat/slices/message/action.ts +70 -82
- package/src/store/chat/slices/plugin/action.test.ts +84 -25
- package/src/store/chat/slices/plugin/action.ts +52 -24
- package/src/store/chat/slices/thread/action.test.ts +13 -4
- package/src/store/chat/slices/thread/action.ts +3 -1
- package/src/store/tool/slices/mcpStore/action.test.ts +95 -3
- package/src/store/tool/slices/mcpStore/action.ts +177 -53
- package/src/store/tool/slices/oldStore/initialState.ts +1 -2
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
ChatMessageError,
|
|
7
7
|
ChatMessagePluginError,
|
|
8
8
|
CreateMessageParams,
|
|
9
|
-
CreateNewMessageParams,
|
|
10
9
|
GroundingSearch,
|
|
11
10
|
MessageMetadata,
|
|
12
11
|
MessageToolCall,
|
|
@@ -108,24 +107,17 @@ export interface ChatMessageAction {
|
|
|
108
107
|
) => Promise<void>;
|
|
109
108
|
/**
|
|
110
109
|
* create a message with optimistic update
|
|
110
|
+
* returns the created message ID and updated message list
|
|
111
111
|
*/
|
|
112
112
|
internal_createMessage: (
|
|
113
113
|
params: CreateMessageParams,
|
|
114
|
-
context?: { tempMessageId?: string; skipRefresh?: boolean },
|
|
115
|
-
) => Promise<string | undefined>;
|
|
114
|
+
context?: { tempMessageId?: string; skipRefresh?: boolean; groupMessageId?: string },
|
|
115
|
+
) => Promise<{ id: string; messages: UIChatMessage[] } | undefined>;
|
|
116
116
|
/**
|
|
117
117
|
* create a temp message for optimistic update
|
|
118
118
|
* otherwise the message will be too slow to show
|
|
119
119
|
*/
|
|
120
120
|
internal_createTmpMessage: (params: CreateMessageParams) => string;
|
|
121
|
-
/**
|
|
122
|
-
* create a new message using createNewMessage API and return full message list
|
|
123
|
-
* used for group message scenarios to reduce network requests
|
|
124
|
-
*/
|
|
125
|
-
internal_createNewMessage: (
|
|
126
|
-
params: CreateNewMessageParams,
|
|
127
|
-
context?: { tempMessageId?: string; groupMessageId?: string },
|
|
128
|
-
) => Promise<{ id: string; messages: UIChatMessage[] } | undefined>;
|
|
129
121
|
/**
|
|
130
122
|
* delete the message content with optimistic update
|
|
131
123
|
*/
|
|
@@ -172,22 +164,42 @@ export const chatMessage: StateCreator<
|
|
|
172
164
|
if (!message) return;
|
|
173
165
|
|
|
174
166
|
let ids = [message.id];
|
|
167
|
+
const allMessages = chatSelectors.activeBaseChats(get());
|
|
175
168
|
|
|
176
|
-
// if the message is a tool calls, then delete all the related messages
|
|
169
|
+
// if the message is a tool calls, then delete all the related tool messages
|
|
177
170
|
if (message.tools) {
|
|
178
171
|
const toolMessageIds = message.tools.flatMap((tool) => {
|
|
179
|
-
const messages =
|
|
180
|
-
.activeBaseChats(get())
|
|
181
|
-
.filter((m) => m.tool_call_id === tool.id);
|
|
182
|
-
|
|
172
|
+
const messages = allMessages.filter((m) => m.tool_call_id === tool.id);
|
|
183
173
|
return messages.map((m) => m.id);
|
|
184
174
|
});
|
|
185
175
|
ids = ids.concat(toolMessageIds);
|
|
186
176
|
}
|
|
187
177
|
|
|
178
|
+
// if the message is a group message, find all children messages (via parentId)
|
|
179
|
+
if (message.role === 'group') {
|
|
180
|
+
const childMessages = allMessages.filter((m) => m.parentId === message.id);
|
|
181
|
+
const childMessageIds = childMessages.map((m) => m.id);
|
|
182
|
+
ids = ids.concat(childMessageIds);
|
|
183
|
+
|
|
184
|
+
// Also delete tool results of children messages
|
|
185
|
+
const childToolMessageIds = childMessages.flatMap((child) => {
|
|
186
|
+
if (!child.tools) return [];
|
|
187
|
+
return child.tools.flatMap((tool) => {
|
|
188
|
+
const toolMessages = allMessages.filter((m) => m.tool_call_id === tool.id);
|
|
189
|
+
return toolMessages.map((m) => m.id);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
ids = ids.concat(childToolMessageIds);
|
|
193
|
+
}
|
|
194
|
+
|
|
188
195
|
get().internal_dispatchMessage({ type: 'deleteMessages', ids });
|
|
189
|
-
await messageService.removeMessages(ids
|
|
190
|
-
|
|
196
|
+
const result = await messageService.removeMessages(ids, {
|
|
197
|
+
sessionId: get().activeId,
|
|
198
|
+
topicId: get().activeTopicId,
|
|
199
|
+
});
|
|
200
|
+
if (result?.success && result.messages) {
|
|
201
|
+
get().replaceMessages(result.messages);
|
|
202
|
+
}
|
|
191
203
|
},
|
|
192
204
|
|
|
193
205
|
deleteToolMessage: async (id) => {
|
|
@@ -208,14 +220,7 @@ export const chatMessage: StateCreator<
|
|
|
208
220
|
},
|
|
209
221
|
|
|
210
222
|
clearMessage: async () => {
|
|
211
|
-
const {
|
|
212
|
-
activeId,
|
|
213
|
-
activeTopicId,
|
|
214
|
-
refreshMessages,
|
|
215
|
-
refreshTopic,
|
|
216
|
-
switchTopic,
|
|
217
|
-
activeSessionType,
|
|
218
|
-
} = get();
|
|
223
|
+
const { activeId, activeTopicId, refreshTopic, switchTopic, activeSessionType } = get();
|
|
219
224
|
|
|
220
225
|
// Check if this is a group session - use activeSessionType if available, otherwise check session store
|
|
221
226
|
let isGroupSession = activeSessionType === 'group';
|
|
@@ -239,22 +244,24 @@ export const chatMessage: StateCreator<
|
|
|
239
244
|
await topicService.removeTopic(activeTopicId);
|
|
240
245
|
}
|
|
241
246
|
await refreshTopic();
|
|
242
|
-
|
|
247
|
+
|
|
248
|
+
// Clear messages directly since all messages are deleted
|
|
249
|
+
get().replaceMessages([]);
|
|
243
250
|
|
|
244
251
|
// after remove topic , go back to default topic
|
|
245
252
|
switchTopic();
|
|
246
253
|
},
|
|
247
254
|
clearAllMessages: async () => {
|
|
248
|
-
const { refreshMessages } = get();
|
|
249
255
|
await messageService.removeAllMessages();
|
|
250
|
-
|
|
256
|
+
// Clear messages directly since all messages are deleted
|
|
257
|
+
get().replaceMessages([]);
|
|
251
258
|
},
|
|
252
259
|
addAIMessage: async () => {
|
|
253
260
|
const { internal_createMessage, updateInputMessage, activeTopicId, activeId, inputMessage } =
|
|
254
261
|
get();
|
|
255
262
|
if (!activeId) return;
|
|
256
263
|
|
|
257
|
-
await internal_createMessage({
|
|
264
|
+
const result = await internal_createMessage({
|
|
258
265
|
content: inputMessage,
|
|
259
266
|
role: 'assistant',
|
|
260
267
|
sessionId: activeId,
|
|
@@ -262,14 +269,16 @@ export const chatMessage: StateCreator<
|
|
|
262
269
|
topicId: activeTopicId,
|
|
263
270
|
});
|
|
264
271
|
|
|
265
|
-
|
|
272
|
+
if (result) {
|
|
273
|
+
updateInputMessage('');
|
|
274
|
+
}
|
|
266
275
|
},
|
|
267
276
|
addUserMessage: async ({ message, fileList }) => {
|
|
268
277
|
const { internal_createMessage, updateInputMessage, activeTopicId, activeId, activeThreadId } =
|
|
269
278
|
get();
|
|
270
279
|
if (!activeId) return;
|
|
271
280
|
|
|
272
|
-
await internal_createMessage({
|
|
281
|
+
const result = await internal_createMessage({
|
|
273
282
|
content: message,
|
|
274
283
|
files: fileList,
|
|
275
284
|
role: 'user',
|
|
@@ -279,7 +288,9 @@ export const chatMessage: StateCreator<
|
|
|
279
288
|
threadId: activeThreadId,
|
|
280
289
|
});
|
|
281
290
|
|
|
282
|
-
|
|
291
|
+
if (result) {
|
|
292
|
+
updateInputMessage('');
|
|
293
|
+
}
|
|
283
294
|
},
|
|
284
295
|
copyMessage: async (id, content) => {
|
|
285
296
|
await copyToClipboard(content);
|
|
@@ -359,10 +370,13 @@ export const chatMessage: StateCreator<
|
|
|
359
370
|
},
|
|
360
371
|
|
|
361
372
|
internal_updateMessageRAG: async (id, data) => {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
373
|
+
const result = await messageService.updateMessageRAG(id, data, {
|
|
374
|
+
sessionId: get().activeId,
|
|
375
|
+
topicId: get().activeTopicId,
|
|
376
|
+
});
|
|
377
|
+
if (result?.success && result.messages) {
|
|
378
|
+
get().replaceMessages(result.messages);
|
|
379
|
+
}
|
|
366
380
|
},
|
|
367
381
|
|
|
368
382
|
// the internal process method of the AI message
|
|
@@ -396,8 +410,13 @@ export const chatMessage: StateCreator<
|
|
|
396
410
|
},
|
|
397
411
|
|
|
398
412
|
internal_updateMessagePluginError: async (id, error) => {
|
|
399
|
-
await messageService.updateMessagePluginError(id, error
|
|
400
|
-
|
|
413
|
+
const result = await messageService.updateMessagePluginError(id, error, {
|
|
414
|
+
sessionId: get().activeId,
|
|
415
|
+
topicId: get().activeTopicId,
|
|
416
|
+
});
|
|
417
|
+
if (result?.success && result.messages) {
|
|
418
|
+
get().replaceMessages(result.messages);
|
|
419
|
+
}
|
|
401
420
|
},
|
|
402
421
|
|
|
403
422
|
internal_updateMessageContent: async (id, content, extra) => {
|
|
@@ -447,42 +466,6 @@ export const chatMessage: StateCreator<
|
|
|
447
466
|
}
|
|
448
467
|
},
|
|
449
468
|
|
|
450
|
-
internal_createMessage: async (message, context) => {
|
|
451
|
-
const {
|
|
452
|
-
internal_createTmpMessage,
|
|
453
|
-
refreshMessages,
|
|
454
|
-
internal_toggleMessageLoading,
|
|
455
|
-
internal_dispatchMessage,
|
|
456
|
-
} = get();
|
|
457
|
-
let tempId = context?.tempMessageId;
|
|
458
|
-
if (!tempId) {
|
|
459
|
-
// use optimistic update to avoid the slow waiting
|
|
460
|
-
tempId = internal_createTmpMessage(message);
|
|
461
|
-
|
|
462
|
-
internal_toggleMessageLoading(true, tempId);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
const id = await messageService.createMessage(message);
|
|
467
|
-
if (!context?.skipRefresh) {
|
|
468
|
-
internal_toggleMessageLoading(true, tempId);
|
|
469
|
-
await refreshMessages();
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
internal_toggleMessageLoading(false, tempId);
|
|
473
|
-
return id;
|
|
474
|
-
} catch (e) {
|
|
475
|
-
internal_toggleMessageLoading(false, tempId);
|
|
476
|
-
internal_dispatchMessage({
|
|
477
|
-
id: tempId,
|
|
478
|
-
type: 'updateMessage',
|
|
479
|
-
value: {
|
|
480
|
-
error: { type: ChatErrorType.CreateMessageError, message: (e as Error).message, body: e },
|
|
481
|
-
},
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
|
|
486
469
|
internal_fetchMessages: async () => {
|
|
487
470
|
const messages = await messageService.getMessages(get().activeId, get().activeTopicId);
|
|
488
471
|
const nextMap = { ...get().messagesMap, [chatSelectors.currentChatKey(get())]: messages };
|
|
@@ -504,8 +487,7 @@ export const chatMessage: StateCreator<
|
|
|
504
487
|
|
|
505
488
|
return tempId;
|
|
506
489
|
},
|
|
507
|
-
|
|
508
|
-
internal_createNewMessage: async (message, context) => {
|
|
490
|
+
internal_createMessage: async (message, context) => {
|
|
509
491
|
const {
|
|
510
492
|
internal_createTmpMessage,
|
|
511
493
|
internal_toggleMessageLoading,
|
|
@@ -537,11 +519,12 @@ export const chatMessage: StateCreator<
|
|
|
537
519
|
}
|
|
538
520
|
|
|
539
521
|
try {
|
|
540
|
-
// 使用 createNewMessage API
|
|
541
522
|
const result = await messageService.createNewMessage(message);
|
|
542
523
|
|
|
543
|
-
|
|
544
|
-
|
|
524
|
+
if (!context?.skipRefresh) {
|
|
525
|
+
// Use the messages returned from createNewMessage (already grouped)
|
|
526
|
+
replaceMessages(result.messages);
|
|
527
|
+
}
|
|
545
528
|
|
|
546
529
|
internal_toggleMessageLoading(false, tempId);
|
|
547
530
|
return result;
|
|
@@ -563,8 +546,13 @@ export const chatMessage: StateCreator<
|
|
|
563
546
|
|
|
564
547
|
internal_deleteMessage: async (id: string) => {
|
|
565
548
|
get().internal_dispatchMessage({ type: 'deleteMessage', id });
|
|
566
|
-
await messageService.removeMessage(id
|
|
567
|
-
|
|
549
|
+
const result = await messageService.removeMessage(id, {
|
|
550
|
+
sessionId: get().activeId,
|
|
551
|
+
topicId: get().activeTopicId,
|
|
552
|
+
});
|
|
553
|
+
if (result?.success && result.messages) {
|
|
554
|
+
get().replaceMessages(result.messages);
|
|
555
|
+
}
|
|
568
556
|
},
|
|
569
557
|
internal_traceMessage: async (id, payload) => {
|
|
570
558
|
// tracing the diff of update
|
|
@@ -26,7 +26,7 @@ vi.mock('@/services/message', () => ({
|
|
|
26
26
|
updateMessageError: vi.fn(),
|
|
27
27
|
updateMessagePluginState: vi.fn(),
|
|
28
28
|
updateMessagePluginArguments: vi.fn(),
|
|
29
|
-
|
|
29
|
+
createNewMessage: vi.fn(),
|
|
30
30
|
},
|
|
31
31
|
}));
|
|
32
32
|
|
|
@@ -271,9 +271,16 @@ describe('ChatPluginAction', () => {
|
|
|
271
271
|
const pluginPayload = { apiName: 'testApi', arguments: { key: 'value' } };
|
|
272
272
|
const messageId = 'message-id';
|
|
273
273
|
const error = new Error('API call failed');
|
|
274
|
+
const mockMessages = [{ id: 'msg-1', content: 'test' }] as any;
|
|
275
|
+
|
|
276
|
+
// Mock the service to return messages
|
|
277
|
+
(messageService.updateMessageError as Mock).mockResolvedValue({
|
|
278
|
+
success: true,
|
|
279
|
+
messages: mockMessages,
|
|
280
|
+
});
|
|
274
281
|
|
|
275
282
|
const storeState = useChatStore.getState();
|
|
276
|
-
vi.spyOn(storeState, '
|
|
283
|
+
const replaceMessagesSpy = vi.spyOn(storeState, 'replaceMessages');
|
|
277
284
|
vi.spyOn(storeState, 'triggerAIMessage').mockResolvedValue(undefined);
|
|
278
285
|
vi.spyOn(storeState, 'internal_togglePluginApiCalling').mockReturnValue(undefined);
|
|
279
286
|
|
|
@@ -291,7 +298,7 @@ describe('ChatPluginAction', () => {
|
|
|
291
298
|
);
|
|
292
299
|
expect(chatService.runPluginApi).toHaveBeenCalledWith(pluginPayload, { trace: {} });
|
|
293
300
|
expect(messageService.updateMessageError).toHaveBeenCalledWith(messageId, error);
|
|
294
|
-
expect(
|
|
301
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
|
|
295
302
|
expect(storeState.internal_togglePluginApiCalling).toHaveBeenCalledWith(
|
|
296
303
|
false,
|
|
297
304
|
'message-id',
|
|
@@ -345,7 +352,9 @@ describe('ChatPluginAction', () => {
|
|
|
345
352
|
const invokeBuiltinToolMock = vi.fn();
|
|
346
353
|
const invokeDefaultTypePluginMock = vi.fn().mockResolvedValue('Default tool response');
|
|
347
354
|
const triggerAIMessageMock = vi.fn();
|
|
348
|
-
const internal_createMessageMock = vi
|
|
355
|
+
const internal_createMessageMock = vi
|
|
356
|
+
.fn()
|
|
357
|
+
.mockResolvedValue({ id: 'tool-message-id', messages: [] });
|
|
349
358
|
const getTraceIdByMessageIdMock = vi.fn().mockReturnValue('trace-id');
|
|
350
359
|
|
|
351
360
|
act(() => {
|
|
@@ -472,7 +481,9 @@ describe('ChatPluginAction', () => {
|
|
|
472
481
|
const invokeMarkdownTypePluginMock = vi.fn();
|
|
473
482
|
const invokeBuiltinToolMock = vi.fn();
|
|
474
483
|
const triggerAIMessageMock = vi.fn();
|
|
475
|
-
const internal_createMessageMock = vi
|
|
484
|
+
const internal_createMessageMock = vi
|
|
485
|
+
.fn()
|
|
486
|
+
.mockResolvedValue({ id: 'tool-message-id', messages: [] });
|
|
476
487
|
|
|
477
488
|
act(() => {
|
|
478
489
|
useChatStore.setState({
|
|
@@ -518,9 +529,17 @@ describe('ChatPluginAction', () => {
|
|
|
518
529
|
it('should update the plugin state for a message', async () => {
|
|
519
530
|
const messageId = 'message-id';
|
|
520
531
|
const pluginStateValue = { key: 'value' };
|
|
532
|
+
const mockMessages = [{ id: 'msg-1', content: 'test' }] as any;
|
|
521
533
|
|
|
534
|
+
// Mock the service to return messages
|
|
535
|
+
(messageService.updateMessagePluginState as Mock).mockResolvedValue({
|
|
536
|
+
success: true,
|
|
537
|
+
messages: mockMessages,
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
const replaceMessagesSpy = vi.fn();
|
|
522
541
|
const initialState = {
|
|
523
|
-
|
|
542
|
+
replaceMessages: replaceMessagesSpy,
|
|
524
543
|
};
|
|
525
544
|
useChatStore.setState(initialState);
|
|
526
545
|
|
|
@@ -533,19 +552,28 @@ describe('ChatPluginAction', () => {
|
|
|
533
552
|
expect(messageService.updateMessagePluginState).toHaveBeenCalledWith(
|
|
534
553
|
messageId,
|
|
535
554
|
pluginStateValue,
|
|
555
|
+
{
|
|
556
|
+
sessionId: 'inbox',
|
|
557
|
+
topicId: null,
|
|
558
|
+
},
|
|
536
559
|
);
|
|
537
|
-
|
|
560
|
+
|
|
561
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
|
|
538
562
|
});
|
|
539
563
|
});
|
|
540
564
|
|
|
541
565
|
describe('createAssistantMessageByPlugin', () => {
|
|
542
|
-
it('should create an assistant message and
|
|
543
|
-
|
|
544
|
-
|
|
566
|
+
it('should create an assistant message and replace messages', async () => {
|
|
567
|
+
const mockMessages = [{ id: 'msg-1', content: 'test' }] as any;
|
|
568
|
+
// 模拟 messageService.createNewMessage 方法的实现
|
|
569
|
+
(messageService.createNewMessage as Mock).mockResolvedValue({
|
|
570
|
+
id: 'new-message-id',
|
|
571
|
+
messages: mockMessages,
|
|
572
|
+
});
|
|
545
573
|
|
|
546
|
-
// 设置初始状态并模拟
|
|
574
|
+
// 设置初始状态并模拟 replaceMessages 方法
|
|
547
575
|
const initialState = {
|
|
548
|
-
|
|
576
|
+
replaceMessages: vi.fn(),
|
|
549
577
|
activeId: 'session-id',
|
|
550
578
|
activeTopicId: 'topic-id',
|
|
551
579
|
};
|
|
@@ -560,8 +588,8 @@ describe('ChatPluginAction', () => {
|
|
|
560
588
|
await result.current.createAssistantMessageByPlugin(content, parentId);
|
|
561
589
|
});
|
|
562
590
|
|
|
563
|
-
// 验证 messageService.
|
|
564
|
-
expect(messageService.
|
|
591
|
+
// 验证 messageService.createNewMessage 是否被带有正确参数调用
|
|
592
|
+
expect(messageService.createNewMessage).toHaveBeenCalledWith({
|
|
565
593
|
content,
|
|
566
594
|
parentId,
|
|
567
595
|
role: 'assistant',
|
|
@@ -569,14 +597,14 @@ describe('ChatPluginAction', () => {
|
|
|
569
597
|
topicId: initialState.activeTopicId,
|
|
570
598
|
});
|
|
571
599
|
|
|
572
|
-
// 验证
|
|
573
|
-
expect(result.current.
|
|
600
|
+
// 验证 replaceMessages 是否被调用
|
|
601
|
+
expect(result.current.replaceMessages).toHaveBeenCalledWith(mockMessages);
|
|
574
602
|
});
|
|
575
603
|
|
|
576
604
|
it('should handle errors when message creation fails', async () => {
|
|
577
605
|
// 模拟 messageService.create 方法,使其抛出错误
|
|
578
606
|
const errorMessage = 'Failed to create message';
|
|
579
|
-
(messageService.
|
|
607
|
+
(messageService.createNewMessage as Mock).mockRejectedValue(new Error(errorMessage));
|
|
580
608
|
|
|
581
609
|
// 设置初始状态并模拟 refreshMessages 方法
|
|
582
610
|
const initialState = {
|
|
@@ -598,7 +626,7 @@ describe('ChatPluginAction', () => {
|
|
|
598
626
|
});
|
|
599
627
|
|
|
600
628
|
// 验证 messageService.create 是否被带有正确参数调用
|
|
601
|
-
expect(messageService.
|
|
629
|
+
expect(messageService.createNewMessage).toHaveBeenCalledWith({
|
|
602
630
|
content,
|
|
603
631
|
parentId,
|
|
604
632
|
role: 'assistant',
|
|
@@ -706,11 +734,20 @@ describe('ChatPluginAction', () => {
|
|
|
706
734
|
describe('invokeStandaloneTypePlugin', () => {
|
|
707
735
|
it('should update message with error and refresh messages if plugin settings are invalid', async () => {
|
|
708
736
|
const messageId = 'message-id';
|
|
737
|
+
const mockMessages = [{ id: 'msg-1', content: 'test' }] as any;
|
|
709
738
|
|
|
710
739
|
const payload = {
|
|
711
740
|
identifier: 'pluginName',
|
|
712
741
|
} as ChatToolPayload;
|
|
713
742
|
|
|
743
|
+
// Mock the service to return messages
|
|
744
|
+
(messageService.updateMessageError as Mock).mockResolvedValue({
|
|
745
|
+
success: true,
|
|
746
|
+
messages: mockMessages,
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
const replaceMessagesSpy = vi.fn();
|
|
750
|
+
|
|
714
751
|
act(() => {
|
|
715
752
|
useToolStore.setState({
|
|
716
753
|
validatePluginSettings: vi
|
|
@@ -718,7 +755,7 @@ describe('ChatPluginAction', () => {
|
|
|
718
755
|
.mockResolvedValue({ valid: false, errors: ['Invalid setting'] }),
|
|
719
756
|
});
|
|
720
757
|
|
|
721
|
-
useChatStore.setState({
|
|
758
|
+
useChatStore.setState({ replaceMessages: replaceMessagesSpy, invokeStandaloneTypePlugin });
|
|
722
759
|
});
|
|
723
760
|
|
|
724
761
|
const { result } = renderHook(() => useChatStore());
|
|
@@ -738,7 +775,7 @@ describe('ChatPluginAction', () => {
|
|
|
738
775
|
type: 'PluginSettingsInvalid',
|
|
739
776
|
});
|
|
740
777
|
|
|
741
|
-
expect(
|
|
778
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
|
|
742
779
|
});
|
|
743
780
|
});
|
|
744
781
|
|
|
@@ -917,13 +954,22 @@ describe('ChatPluginAction', () => {
|
|
|
917
954
|
arguments: '{}',
|
|
918
955
|
};
|
|
919
956
|
const error = new Error('API call failed');
|
|
957
|
+
const mockMessages = [{ id: 'msg-1', content: 'test' }] as any;
|
|
958
|
+
|
|
959
|
+
// Mock the service to return messages
|
|
960
|
+
(messageService.updateMessageError as Mock).mockResolvedValue({
|
|
961
|
+
success: true,
|
|
962
|
+
messages: mockMessages,
|
|
963
|
+
});
|
|
920
964
|
|
|
921
965
|
vi.spyOn(chatService, 'runPluginApi').mockRejectedValue(error);
|
|
922
966
|
|
|
967
|
+
const replaceMessagesSpy = vi.fn();
|
|
968
|
+
|
|
923
969
|
act(() => {
|
|
924
970
|
useChatStore.setState({
|
|
925
971
|
internal_togglePluginApiCalling: vi.fn(),
|
|
926
|
-
|
|
972
|
+
replaceMessages: replaceMessagesSpy,
|
|
927
973
|
});
|
|
928
974
|
});
|
|
929
975
|
|
|
@@ -934,7 +980,7 @@ describe('ChatPluginAction', () => {
|
|
|
934
980
|
});
|
|
935
981
|
|
|
936
982
|
expect(messageService.updateMessageError).toHaveBeenCalledWith(messageId, error);
|
|
937
|
-
expect(
|
|
983
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
|
|
938
984
|
});
|
|
939
985
|
});
|
|
940
986
|
|
|
@@ -1040,10 +1086,19 @@ describe('ChatPluginAction', () => {
|
|
|
1040
1086
|
it('should update plugin error and refresh messages', async () => {
|
|
1041
1087
|
const messageId = 'message-id';
|
|
1042
1088
|
const error = { message: 'Plugin error' } as any;
|
|
1089
|
+
const mockMessages = [{ id: 'msg-1', content: 'test' }] as any;
|
|
1090
|
+
|
|
1091
|
+
// Mock the service to return messages
|
|
1092
|
+
(messageService.updateMessage as Mock).mockResolvedValue({
|
|
1093
|
+
success: true,
|
|
1094
|
+
messages: mockMessages,
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
const replaceMessagesSpy = vi.fn();
|
|
1043
1098
|
|
|
1044
1099
|
act(() => {
|
|
1045
1100
|
useChatStore.setState({
|
|
1046
|
-
|
|
1101
|
+
replaceMessages: replaceMessagesSpy,
|
|
1047
1102
|
});
|
|
1048
1103
|
});
|
|
1049
1104
|
|
|
@@ -1053,8 +1108,12 @@ describe('ChatPluginAction', () => {
|
|
|
1053
1108
|
await result.current.internal_updatePluginError(messageId, error);
|
|
1054
1109
|
});
|
|
1055
1110
|
|
|
1056
|
-
expect(messageService.updateMessage).toHaveBeenCalledWith(
|
|
1057
|
-
|
|
1111
|
+
expect(messageService.updateMessage).toHaveBeenCalledWith(
|
|
1112
|
+
messageId,
|
|
1113
|
+
{ error },
|
|
1114
|
+
{ sessionId: 'inbox', topicId: null },
|
|
1115
|
+
);
|
|
1116
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
|
|
1058
1117
|
});
|
|
1059
1118
|
});
|
|
1060
1119
|
|
|
@@ -98,8 +98,8 @@ export const chatPlugin: StateCreator<
|
|
|
98
98
|
topicId: get().activeTopicId, // if there is activeTopicId,then add it to topicId
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
-
await messageService.
|
|
102
|
-
|
|
101
|
+
const result = await messageService.createNewMessage(newMessage);
|
|
102
|
+
get().replaceMessages(result.messages);
|
|
103
103
|
},
|
|
104
104
|
|
|
105
105
|
fillPluginMessageContent: async (id, content, triggerAiMessage) => {
|
|
@@ -144,7 +144,7 @@ export const chatPlugin: StateCreator<
|
|
|
144
144
|
|
|
145
145
|
// if the plugin settings is not valid, then set the message with error type
|
|
146
146
|
if (!result.valid) {
|
|
147
|
-
await messageService.updateMessageError(id, {
|
|
147
|
+
const updateResult = await messageService.updateMessageError(id, {
|
|
148
148
|
body: {
|
|
149
149
|
error: result.errors,
|
|
150
150
|
message: '[plugin] your settings is invalid with plugin manifest setting schema',
|
|
@@ -153,7 +153,9 @@ export const chatPlugin: StateCreator<
|
|
|
153
153
|
type: PluginErrorType.PluginSettingsInvalid as any,
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
if (updateResult?.success && updateResult.messages) {
|
|
157
|
+
get().replaceMessages(updateResult.messages);
|
|
158
|
+
}
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
159
161
|
},
|
|
@@ -228,15 +230,15 @@ export const chatPlugin: StateCreator<
|
|
|
228
230
|
groupId: message.groupId, // Propagate groupId from parent message for group chat
|
|
229
231
|
};
|
|
230
232
|
|
|
231
|
-
const
|
|
232
|
-
if (!
|
|
233
|
+
const result = await get().internal_createMessage(toolMessage);
|
|
234
|
+
if (!result) return;
|
|
233
235
|
|
|
234
236
|
// trigger the plugin call
|
|
235
|
-
const data = await get().internal_invokeDifferentTypePlugin(id, payload);
|
|
237
|
+
const data = await get().internal_invokeDifferentTypePlugin(result.id, payload);
|
|
236
238
|
|
|
237
239
|
if (data && !['markdown', 'standalone'].includes(payload.type)) {
|
|
238
240
|
shouldCreateMessage = true;
|
|
239
|
-
latestToolId = id;
|
|
241
|
+
latestToolId = result.id;
|
|
240
242
|
}
|
|
241
243
|
});
|
|
242
244
|
|
|
@@ -252,13 +254,19 @@ export const chatPlugin: StateCreator<
|
|
|
252
254
|
await get().triggerAIMessage({ traceId, threadId, inPortalThread, inSearchWorkflow });
|
|
253
255
|
},
|
|
254
256
|
updatePluginState: async (id, value) => {
|
|
255
|
-
const {
|
|
257
|
+
const { replaceMessages } = get();
|
|
256
258
|
|
|
257
259
|
// optimistic update
|
|
258
260
|
get().internal_dispatchMessage({ id, type: 'updateMessage', value: { pluginState: value } });
|
|
259
261
|
|
|
260
|
-
await messageService.updateMessagePluginState(id, value
|
|
261
|
-
|
|
262
|
+
const result = await messageService.updateMessagePluginState(id, value, {
|
|
263
|
+
sessionId: get().activeId,
|
|
264
|
+
topicId: get().activeTopicId,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
if (result?.success && result.messages) {
|
|
268
|
+
replaceMessages(result.messages);
|
|
269
|
+
}
|
|
262
270
|
},
|
|
263
271
|
|
|
264
272
|
updatePluginArguments: async (id, value, replace = false) => {
|
|
@@ -336,18 +344,26 @@ export const chatPlugin: StateCreator<
|
|
|
336
344
|
const message = chatSelectors.getMessageById(id)(get());
|
|
337
345
|
if (!message || !message.tools) return;
|
|
338
346
|
|
|
339
|
-
const { internal_toggleMessageLoading,
|
|
347
|
+
const { internal_toggleMessageLoading, replaceMessages } = get();
|
|
340
348
|
|
|
341
349
|
internal_toggleMessageLoading(true, id);
|
|
342
|
-
await messageService.updateMessage(
|
|
350
|
+
const result = await messageService.updateMessage(
|
|
351
|
+
id,
|
|
352
|
+
{ tools: message.tools },
|
|
353
|
+
{
|
|
354
|
+
sessionId: get().activeId,
|
|
355
|
+
topicId: get().activeTopicId,
|
|
356
|
+
},
|
|
357
|
+
);
|
|
343
358
|
internal_toggleMessageLoading(false, id);
|
|
344
359
|
|
|
345
|
-
|
|
360
|
+
if (result?.success && result.messages) {
|
|
361
|
+
replaceMessages(result.messages);
|
|
362
|
+
}
|
|
346
363
|
},
|
|
347
364
|
|
|
348
365
|
internal_callPluginApi: async (id, payload) => {
|
|
349
|
-
const { internal_updateMessageContent,
|
|
350
|
-
get();
|
|
366
|
+
const { internal_updateMessageContent, internal_togglePluginApiCalling } = get();
|
|
351
367
|
let data: string;
|
|
352
368
|
|
|
353
369
|
try {
|
|
@@ -375,8 +391,10 @@ export const chatPlugin: StateCreator<
|
|
|
375
391
|
|
|
376
392
|
// ignore the aborted request error
|
|
377
393
|
if (!err.message.includes('The user aborted a request.')) {
|
|
378
|
-
await messageService.updateMessageError(id, error as any);
|
|
379
|
-
|
|
394
|
+
const result = await messageService.updateMessageError(id, error as any);
|
|
395
|
+
if (result?.success && result.messages) {
|
|
396
|
+
get().replaceMessages(result.messages);
|
|
397
|
+
}
|
|
380
398
|
}
|
|
381
399
|
|
|
382
400
|
data = '';
|
|
@@ -418,7 +436,6 @@ export const chatPlugin: StateCreator<
|
|
|
418
436
|
invokeMCPTypePlugin: async (id, payload) => {
|
|
419
437
|
const {
|
|
420
438
|
internal_updateMessageContent,
|
|
421
|
-
refreshMessages,
|
|
422
439
|
internal_togglePluginApiCalling,
|
|
423
440
|
internal_constructToolsCallingContext,
|
|
424
441
|
} = get();
|
|
@@ -444,8 +461,10 @@ export const chatPlugin: StateCreator<
|
|
|
444
461
|
|
|
445
462
|
// ignore the aborted request error
|
|
446
463
|
if (!err.message.includes('The user aborted a request.')) {
|
|
447
|
-
await messageService.updateMessageError(id, error as any);
|
|
448
|
-
|
|
464
|
+
const result = await messageService.updateMessageError(id, error as any);
|
|
465
|
+
if (result?.success && result.messages) {
|
|
466
|
+
get().replaceMessages(result.messages);
|
|
467
|
+
}
|
|
449
468
|
}
|
|
450
469
|
}
|
|
451
470
|
|
|
@@ -487,11 +506,20 @@ export const chatPlugin: StateCreator<
|
|
|
487
506
|
return toolNameResolver.resolve(toolCalls, manifests);
|
|
488
507
|
},
|
|
489
508
|
internal_updatePluginError: async (id, error) => {
|
|
490
|
-
const {
|
|
509
|
+
const { replaceMessages } = get();
|
|
491
510
|
|
|
492
511
|
get().internal_dispatchMessage({ id, type: 'updateMessage', value: { error } });
|
|
493
|
-
await messageService.updateMessage(
|
|
494
|
-
|
|
512
|
+
const result = await messageService.updateMessage(
|
|
513
|
+
id,
|
|
514
|
+
{ error },
|
|
515
|
+
{
|
|
516
|
+
sessionId: get().activeId,
|
|
517
|
+
topicId: get().activeTopicId,
|
|
518
|
+
},
|
|
519
|
+
);
|
|
520
|
+
if (result?.success && result.messages) {
|
|
521
|
+
replaceMessages(result.messages);
|
|
522
|
+
}
|
|
495
523
|
},
|
|
496
524
|
|
|
497
525
|
internal_constructToolsCallingContext: (id: string) => {
|