@lobehub/lobehub 2.0.0-next.8 → 2.0.0-next.9
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/.github/workflows/desktop-pr-build.yml +8 -8
- package/.github/workflows/docker.yml +17 -16
- package/.github/workflows/e2e.yml +3 -3
- package/.github/workflows/release-desktop-beta.yml +8 -8
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/test.yml +4 -4
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/const/src/index.ts +0 -1
- package/packages/const/src/url.ts +1 -4
- package/packages/context-engine/src/index.ts +1 -6
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +12 -2
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +73 -9
- package/packages/context-engine/src/providers/index.ts +0 -2
- package/packages/database/package.json +1 -1
- package/packages/database/src/models/__tests__/message.grouping.test.ts +812 -0
- package/packages/database/src/models/__tests__/message.test.ts +322 -170
- package/packages/database/src/models/message.ts +62 -24
- package/packages/database/src/utils/__tests__/groupMessages.test.ts +145 -2
- package/packages/database/src/utils/groupMessages.ts +7 -5
- package/packages/types/src/message/common/base.ts +13 -0
- package/packages/types/src/message/common/image.ts +8 -0
- package/packages/types/src/message/common/metadata.ts +39 -0
- package/packages/types/src/message/common/tools.ts +10 -0
- package/packages/types/src/message/db/params.ts +47 -1
- package/packages/types/src/message/ui/chat.ts +4 -1
- package/packages/types/src/search.ts +16 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/useSend.ts +6 -4
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +15 -10
- package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +4 -2
- package/src/components/Thinking/index.tsx +4 -3
- package/src/features/AgentSetting/AgentPlugin/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +1 -3
- package/src/features/Conversation/Error/ErrorJsonViewer.tsx +4 -3
- package/src/features/Conversation/Error/OllamaBizError/index.tsx +7 -2
- package/src/features/Conversation/Error/index.tsx +15 -5
- package/src/features/Conversation/MarkdownElements/LobeArtifact/Render/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +5 -3
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +4 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/index.tsx +4 -4
- package/src/features/Conversation/Messages/Default.tsx +2 -2
- package/src/features/Conversation/Messages/User/Extra.tsx +2 -2
- package/src/features/Conversation/Messages/User/index.tsx +4 -4
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +2 -2
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +9 -6
- package/src/features/PluginTag/index.tsx +1 -3
- package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +37 -28
- package/src/features/Portal/Artifacts/Body/index.tsx +2 -2
- package/src/server/modules/ModelRuntime/trace.ts +11 -4
- package/src/server/routers/lambda/message.ts +14 -3
- package/src/services/chat/chat.test.ts +1 -40
- package/src/services/chat/contextEngineering.test.ts +0 -30
- package/src/services/chat/contextEngineering.ts +1 -12
- package/src/services/chat/index.ts +2 -7
- package/src/services/chat/types.ts +1 -1
- package/src/services/message/_deprecated.ts +1 -1
- package/src/services/message/client.ts +8 -2
- package/src/services/message/server.ts +7 -2
- package/src/services/message/type.ts +6 -1
- package/src/store/chat/helpers.test.ts +99 -0
- package/src/store/chat/helpers.ts +21 -2
- package/src/store/chat/selectors.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -3
- package/src/store/chat/slices/builtinTool/actions/index.ts +1 -4
- package/src/store/chat/slices/message/action.test.ts +5 -1
- package/src/store/chat/slices/message/action.ts +102 -14
- package/src/store/chat/slices/message/reducer.test.ts +363 -5
- package/src/store/chat/slices/message/reducer.ts +87 -3
- package/src/store/chat/slices/message/{selectors.test.ts → selectors/chat.test.ts} +266 -30
- package/src/store/chat/slices/message/{selectors.ts → selectors/chat.ts} +29 -79
- package/src/store/chat/slices/message/selectors/index.ts +2 -0
- package/src/store/chat/slices/message/selectors/messageState.test.ts +36 -0
- package/src/store/chat/slices/message/selectors/messageState.ts +80 -0
- package/src/store/chat/slices/plugin/action.test.ts +34 -132
- package/src/store/chat/slices/plugin/action.ts +1 -44
- package/src/store/tool/selectors/tool.test.ts +1 -1
- package/src/store/tool/selectors/tool.ts +6 -8
- package/src/store/tool/slices/builtin/action.test.ts +83 -35
- package/src/store/tool/slices/builtin/action.ts +0 -9
- package/src/store/tool/slices/builtin/selectors.test.ts +4 -30
- package/src/store/tool/slices/builtin/selectors.ts +15 -21
- package/src/tools/index.ts +0 -6
- package/src/tools/renders.ts +0 -3
- package/src/tools/web-browsing/Portal/Search/Footer.tsx +2 -2
- package/packages/const/src/guide.ts +0 -89
- package/packages/context-engine/src/providers/InboxGuide.ts +0 -102
- package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +0 -121
- package/src/services/chat/__snapshots__/chat.test.ts.snap +0 -110
- package/src/store/chat/slices/builtinTool/actions/__tests__/dalle.test.ts +0 -121
- package/src/store/chat/slices/builtinTool/actions/dalle.ts +0 -124
- package/src/tools/dalle/Render/GalleyGrid.tsx +0 -60
- package/src/tools/dalle/Render/Item/EditMode.tsx +0 -66
- package/src/tools/dalle/Render/Item/Error.tsx +0 -49
- package/src/tools/dalle/Render/Item/Image.tsx +0 -44
- package/src/tools/dalle/Render/Item/ImageFileItem.tsx +0 -57
- package/src/tools/dalle/Render/Item/index.tsx +0 -88
- package/src/tools/dalle/Render/ToolBar.tsx +0 -56
- package/src/tools/dalle/Render/index.tsx +0 -52
- package/src/tools/dalle/index.ts +0 -92
|
@@ -29,7 +29,7 @@ import { useSessionStore } from '@/store/session';
|
|
|
29
29
|
import { WebBrowsingManifest } from '@/tools/web-browsing';
|
|
30
30
|
import { Action, setNamespace } from '@/utils/storeDebug';
|
|
31
31
|
|
|
32
|
-
import { chatSelectors, topicSelectors } from '../../../selectors';
|
|
32
|
+
import { chatSelectors, messageStateSelectors, topicSelectors } from '../../../selectors';
|
|
33
33
|
|
|
34
34
|
const n = setNamespace('ai');
|
|
35
35
|
|
|
@@ -598,7 +598,6 @@ export const generateAIChat: StateCreator<
|
|
|
598
598
|
topicId: get().activeTopicId,
|
|
599
599
|
traceName: TraceNameMap.Conversation,
|
|
600
600
|
},
|
|
601
|
-
isWelcomeQuestion: params?.isWelcomeQuestion,
|
|
602
601
|
onErrorHandle: async (error) => {
|
|
603
602
|
await messageService.updateMessageError(messageId, error);
|
|
604
603
|
await refreshMessages();
|
|
@@ -707,7 +706,8 @@ export const generateAIChat: StateCreator<
|
|
|
707
706
|
if (!duration) {
|
|
708
707
|
duration = Date.now() - thinkingStartAt;
|
|
709
708
|
|
|
710
|
-
const isInChatReasoning =
|
|
709
|
+
const isInChatReasoning =
|
|
710
|
+
messageStateSelectors.isMessageInChatReasoning(messageId)(get());
|
|
711
711
|
if (isInChatReasoning) {
|
|
712
712
|
internal_toggleChatReasoning(
|
|
713
713
|
false,
|
|
@@ -2,14 +2,12 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
2
2
|
|
|
3
3
|
import { ChatStore } from '@/store/chat/store';
|
|
4
4
|
|
|
5
|
-
import { ChatDallEAction, dalleSlice } from './dalle';
|
|
6
5
|
import { ChatCodeInterpreterAction, codeInterpreterSlice } from './interpreter';
|
|
7
6
|
import { LocalFileAction, localSystemSlice } from './localSystem';
|
|
8
7
|
import { SearchAction, searchSlice } from './search';
|
|
9
8
|
|
|
10
9
|
export interface ChatBuiltinToolAction
|
|
11
|
-
extends
|
|
12
|
-
SearchAction,
|
|
10
|
+
extends SearchAction,
|
|
13
11
|
LocalFileAction,
|
|
14
12
|
ChatCodeInterpreterAction {}
|
|
15
13
|
|
|
@@ -19,7 +17,6 @@ export const chatToolSlice: StateCreator<
|
|
|
19
17
|
[],
|
|
20
18
|
ChatBuiltinToolAction
|
|
21
19
|
> = (...params) => ({
|
|
22
|
-
...dalleSlice(...params),
|
|
23
20
|
...searchSlice(...params),
|
|
24
21
|
...localSystemSlice(...params),
|
|
25
22
|
...codeInterpreterSlice(...params),
|
|
@@ -471,7 +471,11 @@ describe('chatMessage actions', () => {
|
|
|
471
471
|
await result.current.internal_updateMessageContent(messageId, newContent);
|
|
472
472
|
});
|
|
473
473
|
|
|
474
|
-
expect(spy).toHaveBeenCalledWith(
|
|
474
|
+
expect(spy).toHaveBeenCalledWith(
|
|
475
|
+
messageId,
|
|
476
|
+
{ content: newContent },
|
|
477
|
+
{ sessionId: 'session-id', topicId: 'topic-id' },
|
|
478
|
+
);
|
|
475
479
|
});
|
|
476
480
|
|
|
477
481
|
it('should dispatch message update action', async () => {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ChatMessageError,
|
|
7
7
|
ChatMessagePluginError,
|
|
8
8
|
CreateMessageParams,
|
|
9
|
+
CreateNewMessageParams,
|
|
9
10
|
GroundingSearch,
|
|
10
11
|
MessageMetadata,
|
|
11
12
|
MessageToolCall,
|
|
@@ -117,6 +118,14 @@ export interface ChatMessageAction {
|
|
|
117
118
|
* otherwise the message will be too slow to show
|
|
118
119
|
*/
|
|
119
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>;
|
|
120
129
|
/**
|
|
121
130
|
* delete the message content with optimistic update
|
|
122
131
|
*/
|
|
@@ -374,8 +383,16 @@ export const chatMessage: StateCreator<
|
|
|
374
383
|
|
|
375
384
|
internal_updateMessageError: async (id, error) => {
|
|
376
385
|
get().internal_dispatchMessage({ id, type: 'updateMessage', value: { error } });
|
|
377
|
-
await messageService.updateMessage(
|
|
378
|
-
|
|
386
|
+
const result = await messageService.updateMessage(
|
|
387
|
+
id,
|
|
388
|
+
{ error },
|
|
389
|
+
{ topicId: get().activeTopicId, sessionId: get().activeId },
|
|
390
|
+
);
|
|
391
|
+
if (result?.success && result.messages) {
|
|
392
|
+
get().replaceMessages(result.messages);
|
|
393
|
+
} else {
|
|
394
|
+
await get().refreshMessages();
|
|
395
|
+
}
|
|
379
396
|
},
|
|
380
397
|
|
|
381
398
|
internal_updateMessagePluginError: async (id, error) => {
|
|
@@ -384,7 +401,12 @@ export const chatMessage: StateCreator<
|
|
|
384
401
|
},
|
|
385
402
|
|
|
386
403
|
internal_updateMessageContent: async (id, content, extra) => {
|
|
387
|
-
const {
|
|
404
|
+
const {
|
|
405
|
+
internal_dispatchMessage,
|
|
406
|
+
refreshMessages,
|
|
407
|
+
internal_transformToolCalls,
|
|
408
|
+
replaceMessages,
|
|
409
|
+
} = get();
|
|
388
410
|
|
|
389
411
|
// Due to the async update method and refresh need about 100ms
|
|
390
412
|
// we need to update the message content at the frontend to avoid the update flick
|
|
@@ -403,17 +425,26 @@ export const chatMessage: StateCreator<
|
|
|
403
425
|
});
|
|
404
426
|
}
|
|
405
427
|
|
|
406
|
-
await messageService.updateMessage(
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
428
|
+
const result = await messageService.updateMessage(
|
|
429
|
+
id,
|
|
430
|
+
{
|
|
431
|
+
content,
|
|
432
|
+
tools: extra?.toolCalls ? internal_transformToolCalls(extra?.toolCalls) : undefined,
|
|
433
|
+
reasoning: extra?.reasoning,
|
|
434
|
+
search: extra?.search,
|
|
435
|
+
metadata: extra?.metadata,
|
|
436
|
+
model: extra?.model,
|
|
437
|
+
provider: extra?.provider,
|
|
438
|
+
imageList: extra?.imageList,
|
|
439
|
+
},
|
|
440
|
+
{ topicId: get().activeTopicId, sessionId: get().activeId },
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
if (result && result.success && result.messages) {
|
|
444
|
+
replaceMessages(result.messages);
|
|
445
|
+
} else {
|
|
446
|
+
await refreshMessages();
|
|
447
|
+
}
|
|
417
448
|
},
|
|
418
449
|
|
|
419
450
|
internal_createMessage: async (message, context) => {
|
|
@@ -473,6 +504,63 @@ export const chatMessage: StateCreator<
|
|
|
473
504
|
|
|
474
505
|
return tempId;
|
|
475
506
|
},
|
|
507
|
+
|
|
508
|
+
internal_createNewMessage: async (message, context) => {
|
|
509
|
+
const {
|
|
510
|
+
internal_createTmpMessage,
|
|
511
|
+
internal_toggleMessageLoading,
|
|
512
|
+
internal_dispatchMessage,
|
|
513
|
+
replaceMessages,
|
|
514
|
+
} = get();
|
|
515
|
+
|
|
516
|
+
let tempId = context?.tempMessageId;
|
|
517
|
+
if (!tempId) {
|
|
518
|
+
tempId = 'tmp_' + nanoid();
|
|
519
|
+
|
|
520
|
+
// Check if should add as group block (explicitly controlled by caller)
|
|
521
|
+
if (context?.groupMessageId) {
|
|
522
|
+
internal_dispatchMessage({
|
|
523
|
+
type: 'addGroupBlock',
|
|
524
|
+
groupMessageId: context.groupMessageId,
|
|
525
|
+
blockId: tempId,
|
|
526
|
+
value: {
|
|
527
|
+
id: tempId,
|
|
528
|
+
content: message.content,
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
internal_toggleMessageLoading(true, tempId);
|
|
532
|
+
} else {
|
|
533
|
+
// Regular message creation at top level
|
|
534
|
+
tempId = internal_createTmpMessage(message as any);
|
|
535
|
+
internal_toggleMessageLoading(true, tempId);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
try {
|
|
540
|
+
// 使用 createNewMessage API
|
|
541
|
+
const result = await messageService.createNewMessage(message);
|
|
542
|
+
|
|
543
|
+
// 直接用返回的 messages 更新 store(已包含 group 结构)
|
|
544
|
+
replaceMessages(result.messages);
|
|
545
|
+
|
|
546
|
+
internal_toggleMessageLoading(false, tempId);
|
|
547
|
+
return result;
|
|
548
|
+
} catch (e) {
|
|
549
|
+
internal_toggleMessageLoading(false, tempId);
|
|
550
|
+
internal_dispatchMessage({
|
|
551
|
+
id: tempId,
|
|
552
|
+
type: 'updateMessage',
|
|
553
|
+
value: {
|
|
554
|
+
error: {
|
|
555
|
+
type: ChatErrorType.CreateMessageError,
|
|
556
|
+
message: (e as Error).message,
|
|
557
|
+
body: e,
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
|
|
476
564
|
internal_deleteMessage: async (id: string) => {
|
|
477
565
|
get().internal_dispatchMessage({ type: 'deleteMessage', id });
|
|
478
566
|
await messageService.removeMessage(id);
|
|
@@ -57,16 +57,131 @@ describe('messagesReducer', () => {
|
|
|
57
57
|
expect(newState).toEqual(initialState);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
it('should
|
|
60
|
+
it('should update a block in group message children when id matches a block', () => {
|
|
61
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
62
|
+
...initialState,
|
|
63
|
+
{
|
|
64
|
+
id: 'group1',
|
|
65
|
+
role: 'group',
|
|
66
|
+
content: '',
|
|
67
|
+
createdAt: 1629264000000,
|
|
68
|
+
updatedAt: 1629264000000,
|
|
69
|
+
meta: {},
|
|
70
|
+
children: [
|
|
71
|
+
{
|
|
72
|
+
id: 'block1',
|
|
73
|
+
content: 'Original block content',
|
|
74
|
+
tools: [
|
|
75
|
+
{
|
|
76
|
+
id: 'tool1',
|
|
77
|
+
identifier: 'search',
|
|
78
|
+
apiName: 'search',
|
|
79
|
+
type: 'builtin',
|
|
80
|
+
arguments: '{"query": "test"}',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
} as UIChatMessage,
|
|
86
|
+
];
|
|
87
|
+
|
|
61
88
|
const payload: MessageDispatch = {
|
|
62
89
|
type: 'updateMessage',
|
|
63
|
-
id: '
|
|
64
|
-
value: { content: 'Updated
|
|
90
|
+
id: 'block1',
|
|
91
|
+
value: { content: 'Updated block content' },
|
|
65
92
|
};
|
|
66
93
|
|
|
67
|
-
const newState = messagesReducer(
|
|
94
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
95
|
+
const groupMessage = newState.find((m) => m.id === 'group1');
|
|
96
|
+
const block = groupMessage?.children?.find((b) => b.id === 'block1');
|
|
68
97
|
|
|
69
|
-
expect(
|
|
98
|
+
expect(block?.content).toBe('Updated block content');
|
|
99
|
+
expect(groupMessage?.updatedAt).toBeGreaterThan(1629264000000);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should update block tools in group message children', () => {
|
|
103
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
104
|
+
...initialState,
|
|
105
|
+
{
|
|
106
|
+
id: 'group1',
|
|
107
|
+
role: 'group',
|
|
108
|
+
content: '',
|
|
109
|
+
createdAt: 1629264000000,
|
|
110
|
+
updatedAt: 1629264000000,
|
|
111
|
+
meta: {},
|
|
112
|
+
children: [
|
|
113
|
+
{
|
|
114
|
+
id: 'block1',
|
|
115
|
+
content: 'Block content',
|
|
116
|
+
tools: [
|
|
117
|
+
{
|
|
118
|
+
id: 'tool1',
|
|
119
|
+
identifier: 'search',
|
|
120
|
+
apiName: 'search',
|
|
121
|
+
type: 'builtin',
|
|
122
|
+
arguments: '{"query": "test"}',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
} as UIChatMessage,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const newTools = [
|
|
131
|
+
{
|
|
132
|
+
id: 'tool1',
|
|
133
|
+
identifier: 'search',
|
|
134
|
+
apiName: 'search',
|
|
135
|
+
type: 'builtin',
|
|
136
|
+
arguments: '{"query": "updated"}',
|
|
137
|
+
result: {
|
|
138
|
+
id: 'result1',
|
|
139
|
+
content: 'Search result',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
const payload: MessageDispatch = {
|
|
145
|
+
type: 'updateMessage',
|
|
146
|
+
id: 'block1',
|
|
147
|
+
value: { tools: newTools as any },
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
151
|
+
const groupMessage = newState.find((m) => m.id === 'group1');
|
|
152
|
+
const block = groupMessage?.children?.find((b) => b.id === 'block1');
|
|
153
|
+
|
|
154
|
+
expect(block?.tools).toEqual(newTools);
|
|
155
|
+
expect(groupMessage?.updatedAt).toBeGreaterThan(1629264000000);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should not modify state when updating non-existent block in group message', () => {
|
|
159
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
160
|
+
...initialState,
|
|
161
|
+
{
|
|
162
|
+
id: 'group1',
|
|
163
|
+
role: 'group',
|
|
164
|
+
content: '',
|
|
165
|
+
createdAt: 1629264000000,
|
|
166
|
+
updatedAt: 1629264000000,
|
|
167
|
+
meta: {},
|
|
168
|
+
children: [
|
|
169
|
+
{
|
|
170
|
+
id: 'block1',
|
|
171
|
+
content: 'Block content',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
} as UIChatMessage,
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const payload: MessageDispatch = {
|
|
178
|
+
type: 'updateMessage',
|
|
179
|
+
id: 'nonexistentBlock',
|
|
180
|
+
value: { content: 'Updated content' },
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
184
|
+
expect(newState).toEqual(stateWithGroup);
|
|
70
185
|
});
|
|
71
186
|
});
|
|
72
187
|
|
|
@@ -502,4 +617,247 @@ describe('messagesReducer', () => {
|
|
|
502
617
|
expect(newState).toEqual(initialState);
|
|
503
618
|
});
|
|
504
619
|
});
|
|
620
|
+
|
|
621
|
+
describe('updateGroupBlockToolResult', () => {
|
|
622
|
+
it('should update a tool result in a group message block', () => {
|
|
623
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
624
|
+
...initialState,
|
|
625
|
+
{
|
|
626
|
+
id: 'group1',
|
|
627
|
+
role: 'group',
|
|
628
|
+
content: '',
|
|
629
|
+
createdAt: 1629264000000,
|
|
630
|
+
updatedAt: 1629264000000,
|
|
631
|
+
meta: {},
|
|
632
|
+
children: [
|
|
633
|
+
{
|
|
634
|
+
id: 'block1',
|
|
635
|
+
content: 'Assistant response',
|
|
636
|
+
tools: [
|
|
637
|
+
{
|
|
638
|
+
id: 'tool1',
|
|
639
|
+
identifier: 'search',
|
|
640
|
+
apiName: 'search',
|
|
641
|
+
type: 'builtin',
|
|
642
|
+
arguments: '{"query": "test"}',
|
|
643
|
+
result: {
|
|
644
|
+
id: 'result1',
|
|
645
|
+
content: 'Initial result',
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
},
|
|
650
|
+
],
|
|
651
|
+
} as UIChatMessage,
|
|
652
|
+
];
|
|
653
|
+
|
|
654
|
+
const payload: MessageDispatch = {
|
|
655
|
+
type: 'updateGroupBlockToolResult',
|
|
656
|
+
groupMessageId: 'group1',
|
|
657
|
+
blockId: 'block1',
|
|
658
|
+
toolId: 'tool1',
|
|
659
|
+
toolResult: {
|
|
660
|
+
id: 'result1',
|
|
661
|
+
content: 'Updated result content',
|
|
662
|
+
state: { foo: 'bar' },
|
|
663
|
+
},
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
667
|
+
const groupMessage = newState.find((m) => m.id === 'group1');
|
|
668
|
+
const block = groupMessage?.children?.find((b) => b.id === 'block1');
|
|
669
|
+
const tool = block?.tools?.find((t) => t.id === 'tool1');
|
|
670
|
+
|
|
671
|
+
expect(tool?.result).toEqual({
|
|
672
|
+
id: 'result1',
|
|
673
|
+
content: 'Updated result content',
|
|
674
|
+
state: { foo: 'bar' },
|
|
675
|
+
});
|
|
676
|
+
expect(groupMessage?.updatedAt).toBeGreaterThan(1629264000000);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('should not modify state if group message is not found', () => {
|
|
680
|
+
const payload: MessageDispatch = {
|
|
681
|
+
type: 'updateGroupBlockToolResult',
|
|
682
|
+
groupMessageId: 'nonexistent',
|
|
683
|
+
blockId: 'block1',
|
|
684
|
+
toolId: 'tool1',
|
|
685
|
+
toolResult: {
|
|
686
|
+
id: 'result1',
|
|
687
|
+
content: 'Updated result',
|
|
688
|
+
},
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
const newState = messagesReducer(initialState, payload);
|
|
692
|
+
expect(newState).toEqual(initialState);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('should not modify state if block is not found', () => {
|
|
696
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
697
|
+
...initialState,
|
|
698
|
+
{
|
|
699
|
+
id: 'group1',
|
|
700
|
+
role: 'group',
|
|
701
|
+
content: '',
|
|
702
|
+
createdAt: 1629264000000,
|
|
703
|
+
updatedAt: 1629264000000,
|
|
704
|
+
meta: {},
|
|
705
|
+
children: [
|
|
706
|
+
{
|
|
707
|
+
id: 'block1',
|
|
708
|
+
content: 'Assistant response',
|
|
709
|
+
tools: [],
|
|
710
|
+
},
|
|
711
|
+
],
|
|
712
|
+
} as UIChatMessage,
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
const payload: MessageDispatch = {
|
|
716
|
+
type: 'updateGroupBlockToolResult',
|
|
717
|
+
groupMessageId: 'group1',
|
|
718
|
+
blockId: 'nonexistentBlock',
|
|
719
|
+
toolId: 'tool1',
|
|
720
|
+
toolResult: {
|
|
721
|
+
id: 'result1',
|
|
722
|
+
content: 'Updated result',
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
727
|
+
expect(newState).toEqual(stateWithGroup);
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it('should not modify state if tool is not found', () => {
|
|
731
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
732
|
+
...initialState,
|
|
733
|
+
{
|
|
734
|
+
id: 'group1',
|
|
735
|
+
role: 'group',
|
|
736
|
+
content: '',
|
|
737
|
+
createdAt: 1629264000000,
|
|
738
|
+
updatedAt: 1629264000000,
|
|
739
|
+
meta: {},
|
|
740
|
+
children: [
|
|
741
|
+
{
|
|
742
|
+
id: 'block1',
|
|
743
|
+
content: 'Assistant response',
|
|
744
|
+
tools: [
|
|
745
|
+
{
|
|
746
|
+
id: 'tool1',
|
|
747
|
+
identifier: 'search',
|
|
748
|
+
apiName: 'search',
|
|
749
|
+
type: 'builtin',
|
|
750
|
+
arguments: '{"query": "test"}',
|
|
751
|
+
},
|
|
752
|
+
],
|
|
753
|
+
},
|
|
754
|
+
],
|
|
755
|
+
} as UIChatMessage,
|
|
756
|
+
];
|
|
757
|
+
|
|
758
|
+
const payload: MessageDispatch = {
|
|
759
|
+
type: 'updateGroupBlockToolResult',
|
|
760
|
+
groupMessageId: 'group1',
|
|
761
|
+
blockId: 'block1',
|
|
762
|
+
toolId: 'nonexistentTool',
|
|
763
|
+
toolResult: {
|
|
764
|
+
id: 'result1',
|
|
765
|
+
content: 'Updated result',
|
|
766
|
+
},
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
770
|
+
expect(newState).toEqual(stateWithGroup);
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
describe('addGroupBlock', () => {
|
|
775
|
+
it('should add a new block to group message children', () => {
|
|
776
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
777
|
+
...initialState,
|
|
778
|
+
{
|
|
779
|
+
id: 'group1',
|
|
780
|
+
role: 'group',
|
|
781
|
+
content: '',
|
|
782
|
+
createdAt: 1629264000000,
|
|
783
|
+
updatedAt: 1629264000000,
|
|
784
|
+
meta: {},
|
|
785
|
+
children: [
|
|
786
|
+
{
|
|
787
|
+
id: 'block1',
|
|
788
|
+
content: 'First block',
|
|
789
|
+
},
|
|
790
|
+
],
|
|
791
|
+
} as UIChatMessage,
|
|
792
|
+
];
|
|
793
|
+
|
|
794
|
+
const payload: MessageDispatch = {
|
|
795
|
+
type: 'addGroupBlock',
|
|
796
|
+
groupMessageId: 'group1',
|
|
797
|
+
blockId: 'block2',
|
|
798
|
+
value: {
|
|
799
|
+
id: 'block2',
|
|
800
|
+
content: 'Second block',
|
|
801
|
+
},
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
805
|
+
const groupMessage = newState.find((m) => m.id === 'group1');
|
|
806
|
+
|
|
807
|
+
expect(groupMessage?.children).toHaveLength(2);
|
|
808
|
+
expect(groupMessage?.children?.[1]).toEqual({
|
|
809
|
+
id: 'block2',
|
|
810
|
+
content: 'Second block',
|
|
811
|
+
});
|
|
812
|
+
expect(groupMessage?.updatedAt).toBeGreaterThan(1629264000000);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('should not modify state if group message is not found', () => {
|
|
816
|
+
const stateWithGroup: UIChatMessage[] = [
|
|
817
|
+
...initialState,
|
|
818
|
+
{
|
|
819
|
+
id: 'group1',
|
|
820
|
+
role: 'group',
|
|
821
|
+
content: '',
|
|
822
|
+
createdAt: 1629264000000,
|
|
823
|
+
updatedAt: 1629264000000,
|
|
824
|
+
meta: {},
|
|
825
|
+
children: [
|
|
826
|
+
{
|
|
827
|
+
id: 'block1',
|
|
828
|
+
content: 'First block',
|
|
829
|
+
},
|
|
830
|
+
],
|
|
831
|
+
} as UIChatMessage,
|
|
832
|
+
];
|
|
833
|
+
|
|
834
|
+
const payload: MessageDispatch = {
|
|
835
|
+
type: 'addGroupBlock',
|
|
836
|
+
groupMessageId: 'nonexistentGroup',
|
|
837
|
+
blockId: 'block2',
|
|
838
|
+
value: {
|
|
839
|
+
id: 'block2',
|
|
840
|
+
content: 'Second block',
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const newState = messagesReducer(stateWithGroup, payload);
|
|
845
|
+
expect(newState).toEqual(stateWithGroup);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it('should not modify state if message is not a group message', () => {
|
|
849
|
+
const payload: MessageDispatch = {
|
|
850
|
+
type: 'addGroupBlock',
|
|
851
|
+
groupMessageId: 'message1',
|
|
852
|
+
blockId: 'block2',
|
|
853
|
+
value: {
|
|
854
|
+
id: 'block2',
|
|
855
|
+
content: 'Second block',
|
|
856
|
+
},
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
const newState = messagesReducer(initialState, payload);
|
|
860
|
+
expect(newState).toEqual(initialState);
|
|
861
|
+
});
|
|
862
|
+
});
|
|
505
863
|
});
|