@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INBOX_SESSION_ID } from '@lobechat/const';
|
|
1
2
|
import {
|
|
2
3
|
ChatFileItem,
|
|
3
4
|
ChatImageItem,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
UIChatMessage,
|
|
15
16
|
UpdateMessageParams,
|
|
16
17
|
UpdateMessageRAGParams,
|
|
18
|
+
UpdateMessageResult,
|
|
17
19
|
} from '@lobechat/types';
|
|
18
20
|
import type { HeatmapsProps } from '@lobehub/charts';
|
|
19
21
|
import dayjs from 'dayjs';
|
|
@@ -39,6 +41,7 @@ import {
|
|
|
39
41
|
} from '../schemas';
|
|
40
42
|
import { LobeChatDatabase } from '../type';
|
|
41
43
|
import { genEndDateWhere, genRangeWhere, genStartDateWhere, genWhere } from '../utils/genWhere';
|
|
44
|
+
import { groupAssistantMessages } from '../utils/groupMessages';
|
|
42
45
|
import { idGenerator } from '../utils/idGenerator';
|
|
43
46
|
|
|
44
47
|
export class MessageModel {
|
|
@@ -54,6 +57,7 @@ export class MessageModel {
|
|
|
54
57
|
query = async (
|
|
55
58
|
{ current = 0, pageSize = 1000, sessionId, topicId, groupId }: QueryMessageParams = {},
|
|
56
59
|
options: {
|
|
60
|
+
groupAssistantMessages?: boolean;
|
|
57
61
|
postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise<string>;
|
|
58
62
|
} = {},
|
|
59
63
|
) => {
|
|
@@ -211,7 +215,7 @@ export class MessageModel {
|
|
|
211
215
|
.from(messageQueries)
|
|
212
216
|
.where(inArray(messageQueries.messageId, messageIds));
|
|
213
217
|
|
|
214
|
-
|
|
218
|
+
const mappedMessages = result.map(
|
|
215
219
|
({ model, provider, translate, ttsId, ttsFile, ttsContentMd5, ttsVoice, ...item }) => {
|
|
216
220
|
const messageQuery = messageQueriesList.find((relation) => relation.messageId === item.id);
|
|
217
221
|
return {
|
|
@@ -246,13 +250,15 @@ export class MessageModel {
|
|
|
246
250
|
size: size!,
|
|
247
251
|
url,
|
|
248
252
|
})),
|
|
249
|
-
|
|
250
253
|
imageList: imageList
|
|
251
254
|
.filter((relation) => relation.messageId === item.id)
|
|
252
255
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
253
256
|
.map<ChatImageItem>(({ id, url, name }) => ({ alt: name!, id, url })),
|
|
254
|
-
|
|
255
257
|
meta: {},
|
|
258
|
+
|
|
259
|
+
model,
|
|
260
|
+
|
|
261
|
+
provider,
|
|
256
262
|
ragQuery: messageQuery?.rewriteQuery,
|
|
257
263
|
ragQueryId: messageQuery?.id,
|
|
258
264
|
ragRawQuery: messageQuery?.userQuery,
|
|
@@ -263,6 +269,10 @@ export class MessageModel {
|
|
|
263
269
|
} as unknown as UIChatMessage;
|
|
264
270
|
},
|
|
265
271
|
);
|
|
272
|
+
|
|
273
|
+
// Group assistant messages with their tool results
|
|
274
|
+
const { groupAssistantMessages: useGroup = false } = options;
|
|
275
|
+
return useGroup ? groupAssistantMessages(mappedMessages) : mappedMessages;
|
|
266
276
|
};
|
|
267
277
|
|
|
268
278
|
findById = async (id: string) => {
|
|
@@ -560,7 +570,7 @@ export class MessageModel {
|
|
|
560
570
|
sessionId: params.sessionId,
|
|
561
571
|
topicId: params.topicId, // Get all messages
|
|
562
572
|
},
|
|
563
|
-
options,
|
|
573
|
+
{ ...options, groupAssistantMessages: true },
|
|
564
574
|
);
|
|
565
575
|
|
|
566
576
|
// 3. Return the result
|
|
@@ -589,27 +599,52 @@ export class MessageModel {
|
|
|
589
599
|
};
|
|
590
600
|
// **************** Update *************** //
|
|
591
601
|
|
|
592
|
-
update = async (
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
602
|
+
update = async (
|
|
603
|
+
id: string,
|
|
604
|
+
{ imageList, ...message }: Partial<UpdateMessageParams>,
|
|
605
|
+
options?: {
|
|
606
|
+
postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise<string>;
|
|
607
|
+
sessionId?: string | null;
|
|
608
|
+
topicId?: string | null;
|
|
609
|
+
},
|
|
610
|
+
): Promise<UpdateMessageResult> => {
|
|
611
|
+
try {
|
|
612
|
+
await this.db.transaction(async (trx) => {
|
|
613
|
+
// 1. insert message files
|
|
614
|
+
if (imageList && imageList.length > 0) {
|
|
615
|
+
await trx
|
|
616
|
+
.insert(messagesFiles)
|
|
617
|
+
.values(
|
|
618
|
+
imageList.map((file) => ({ fileId: file.id, messageId: id, userId: this.userId })),
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
596
622
|
await trx
|
|
597
|
-
.
|
|
598
|
-
.
|
|
599
|
-
|
|
600
|
-
|
|
623
|
+
.update(messages)
|
|
624
|
+
.set({ ...message })
|
|
625
|
+
.where(and(eq(messages.id, id), eq(messages.userId, this.userId)));
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// if sessionId or topicId provided, return the updated message list
|
|
629
|
+
if (options?.sessionId !== undefined || options?.topicId !== undefined) {
|
|
630
|
+
const messageList = await this.query(
|
|
631
|
+
{
|
|
632
|
+
sessionId: options.sessionId,
|
|
633
|
+
topicId: options.topicId,
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
postProcessUrl: options.postProcessUrl,
|
|
637
|
+
},
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
return { messages: messageList, success: true };
|
|
601
641
|
}
|
|
602
642
|
|
|
603
|
-
return
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
// TODO: but I forget why 🤡
|
|
609
|
-
role: message.role as any,
|
|
610
|
-
})
|
|
611
|
-
.where(and(eq(messages.id, id), eq(messages.userId, this.userId)));
|
|
612
|
-
});
|
|
643
|
+
return { success: true };
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.error('Update message error:', error);
|
|
646
|
+
return { success: false };
|
|
647
|
+
}
|
|
613
648
|
};
|
|
614
649
|
|
|
615
650
|
updateMetadata = async (id: string, metadata: Record<string, any>) => {
|
|
@@ -778,8 +813,11 @@ export class MessageModel {
|
|
|
778
813
|
|
|
779
814
|
private genId = () => idGenerator('messages', 14);
|
|
780
815
|
|
|
781
|
-
private matchSession = (sessionId?: string | null) =>
|
|
782
|
-
|
|
816
|
+
private matchSession = (sessionId?: string | null) => {
|
|
817
|
+
if (sessionId === INBOX_SESSION_ID) return isNull(messages.sessionId);
|
|
818
|
+
|
|
819
|
+
return sessionId ? eq(messages.sessionId, sessionId) : isNull(messages.sessionId);
|
|
820
|
+
};
|
|
783
821
|
|
|
784
822
|
private matchTopic = (topicId?: string | null) =>
|
|
785
823
|
topicId ? eq(messages.topicId, topicId) : isNull(messages.topicId);
|
|
@@ -54,6 +54,7 @@ describe('groupAssistantMessages', () => {
|
|
|
54
54
|
content: 'Beijing: Sunny, 25°C',
|
|
55
55
|
state: { cached: true },
|
|
56
56
|
},
|
|
57
|
+
result_msg_id: 'msg-2',
|
|
57
58
|
});
|
|
58
59
|
});
|
|
59
60
|
|
|
@@ -112,7 +113,9 @@ describe('groupAssistantMessages', () => {
|
|
|
112
113
|
const block = result[0].children![0];
|
|
113
114
|
expect(block.tools).toHaveLength(2);
|
|
114
115
|
expect(block.tools![0].result?.content).toBe('Beijing: Sunny, 25°C');
|
|
116
|
+
expect(block.tools![0].result_msg_id).toBe('msg-2');
|
|
115
117
|
expect(block.tools![1].result?.content).toBe('Latest tech news: AI breakthrough');
|
|
118
|
+
expect(block.tools![1].result_msg_id).toBe('msg-3');
|
|
116
119
|
});
|
|
117
120
|
|
|
118
121
|
it('should handle assistant message without tools', () => {
|
|
@@ -165,6 +168,7 @@ describe('groupAssistantMessages', () => {
|
|
|
165
168
|
const block = result[0].children![0];
|
|
166
169
|
expect(block.tools).toHaveLength(1);
|
|
167
170
|
expect(block.tools![0].result).toBeUndefined();
|
|
171
|
+
expect(block.tools![0].result_msg_id).toBeUndefined();
|
|
168
172
|
});
|
|
169
173
|
});
|
|
170
174
|
|
|
@@ -366,11 +370,13 @@ describe('groupAssistantMessages', () => {
|
|
|
366
370
|
// First child: original assistant with tool result
|
|
367
371
|
expect(result[0].children![0].id).toBe('msg-1');
|
|
368
372
|
expect(result[0].children![0].tools![0].result?.content).toBe('Sunny, 25°C');
|
|
373
|
+
expect(result[0].children![0].tools![0].result_msg_id).toBe('msg-2');
|
|
369
374
|
|
|
370
375
|
// Second child: follow-up assistant with its own tool result
|
|
371
376
|
expect(result[0].children![1].id).toBe('msg-3');
|
|
372
377
|
expect(result[0].children![1].tools).toHaveLength(1);
|
|
373
378
|
expect(result[0].children![1].tools![0].result?.content).toBe('Breaking news');
|
|
379
|
+
expect(result[0].children![1].tools![0].result_msg_id).toBe('msg-4');
|
|
374
380
|
});
|
|
375
381
|
|
|
376
382
|
it('should group multiple follow-up assistants in chain (3+ assistants)', () => {
|
|
@@ -448,9 +454,11 @@ describe('groupAssistantMessages', () => {
|
|
|
448
454
|
|
|
449
455
|
expect(result[0].children![0].id).toBe('msg-1');
|
|
450
456
|
expect(result[0].children![0].tools![0].result?.content).toBe('Result 1');
|
|
457
|
+
expect(result[0].children![0].tools![0].result_msg_id).toBe('msg-2');
|
|
451
458
|
|
|
452
459
|
expect(result[0].children![1].id).toBe('msg-3');
|
|
453
460
|
expect(result[0].children![1].tools![0].result?.content).toBe('Result 2');
|
|
461
|
+
expect(result[0].children![1].tools![0].result_msg_id).toBe('msg-4');
|
|
454
462
|
|
|
455
463
|
expect(result[0].children![2].id).toBe('msg-5');
|
|
456
464
|
expect(result[0].children![2].content).toBe('Step 3 final');
|
|
@@ -677,7 +685,6 @@ describe('groupAssistantMessages', () => {
|
|
|
677
685
|
expect(block.content).toBe('Test');
|
|
678
686
|
expect(block.tools).toHaveLength(1);
|
|
679
687
|
expect(block.imageList).toHaveLength(1);
|
|
680
|
-
expect(block.fileList).toHaveLength(1);
|
|
681
688
|
});
|
|
682
689
|
|
|
683
690
|
it('should preserve all tool result fields', () => {
|
|
@@ -720,6 +727,7 @@ describe('groupAssistantMessages', () => {
|
|
|
720
727
|
state: { step: 1 },
|
|
721
728
|
error: null,
|
|
722
729
|
});
|
|
730
|
+
expect(block.tools![0].result_msg_id).toBe('msg-2');
|
|
723
731
|
});
|
|
724
732
|
});
|
|
725
733
|
|
|
@@ -928,6 +936,142 @@ describe('groupAssistantMessages', () => {
|
|
|
928
936
|
expect(result[0].performance).toBeUndefined();
|
|
929
937
|
expect(result[0].metadata).toBeUndefined();
|
|
930
938
|
});
|
|
939
|
+
|
|
940
|
+
it('should map reasoning field to reasoning in children blocks', () => {
|
|
941
|
+
const input: UIChatMessage[] = [
|
|
942
|
+
{
|
|
943
|
+
id: 'msg-1',
|
|
944
|
+
role: 'assistant',
|
|
945
|
+
content: 'Test response',
|
|
946
|
+
reasoning: {
|
|
947
|
+
content: 'This is my reasoning process',
|
|
948
|
+
duration: 1500,
|
|
949
|
+
},
|
|
950
|
+
tools: [
|
|
951
|
+
{
|
|
952
|
+
id: 'tool-1',
|
|
953
|
+
identifier: 'test',
|
|
954
|
+
apiName: 'test',
|
|
955
|
+
arguments: '{}',
|
|
956
|
+
type: 'default',
|
|
957
|
+
},
|
|
958
|
+
],
|
|
959
|
+
createdAt: Date.now(),
|
|
960
|
+
updatedAt: Date.now(),
|
|
961
|
+
meta: {},
|
|
962
|
+
} as UIChatMessage,
|
|
963
|
+
{
|
|
964
|
+
id: 'msg-2',
|
|
965
|
+
role: 'tool',
|
|
966
|
+
tool_call_id: 'tool-1',
|
|
967
|
+
content: 'Tool result',
|
|
968
|
+
createdAt: Date.now(),
|
|
969
|
+
updatedAt: Date.now(),
|
|
970
|
+
meta: {},
|
|
971
|
+
} as UIChatMessage,
|
|
972
|
+
{
|
|
973
|
+
id: 'msg-3',
|
|
974
|
+
role: 'assistant',
|
|
975
|
+
content: 'Follow-up response',
|
|
976
|
+
parentId: 'msg-2',
|
|
977
|
+
reasoning: {
|
|
978
|
+
content: 'Follow-up reasoning',
|
|
979
|
+
duration: 2000,
|
|
980
|
+
},
|
|
981
|
+
createdAt: Date.now(),
|
|
982
|
+
updatedAt: Date.now(),
|
|
983
|
+
meta: {},
|
|
984
|
+
} as UIChatMessage,
|
|
985
|
+
];
|
|
986
|
+
|
|
987
|
+
const result = groupAssistantMessages(input);
|
|
988
|
+
|
|
989
|
+
// First block should have reasoning
|
|
990
|
+
expect(result[0].children![0].reasoning).toEqual({
|
|
991
|
+
content: 'This is my reasoning process',
|
|
992
|
+
duration: 1500,
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
// Second block (follow-up) should also have reasoning
|
|
996
|
+
expect(result[0].children![1].reasoning).toEqual({
|
|
997
|
+
content: 'Follow-up reasoning',
|
|
998
|
+
duration: 2000,
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
// Group message should not have reasoning (moved to children)
|
|
1002
|
+
expect(result[0].reasoning).toBeUndefined();
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
it('should preserve error field in children blocks', () => {
|
|
1006
|
+
const input = [
|
|
1007
|
+
{
|
|
1008
|
+
id: 'msg-1',
|
|
1009
|
+
role: 'assistant',
|
|
1010
|
+
content: 'Failed to process',
|
|
1011
|
+
error: {
|
|
1012
|
+
type: 'InvalidAPIKey',
|
|
1013
|
+
message: 'API key is invalid',
|
|
1014
|
+
},
|
|
1015
|
+
tools: [
|
|
1016
|
+
{
|
|
1017
|
+
id: 'tool-1',
|
|
1018
|
+
identifier: 'test',
|
|
1019
|
+
apiName: 'test',
|
|
1020
|
+
arguments: '{}',
|
|
1021
|
+
type: 'default',
|
|
1022
|
+
},
|
|
1023
|
+
],
|
|
1024
|
+
createdAt: Date.now(),
|
|
1025
|
+
updatedAt: Date.now(),
|
|
1026
|
+
meta: {},
|
|
1027
|
+
} as unknown as UIChatMessage,
|
|
1028
|
+
] as UIChatMessage[];
|
|
1029
|
+
|
|
1030
|
+
const result = groupAssistantMessages(input);
|
|
1031
|
+
|
|
1032
|
+
// Child block should have error
|
|
1033
|
+
expect(result[0].children![0].error).toEqual({
|
|
1034
|
+
type: 'InvalidAPIKey',
|
|
1035
|
+
message: 'API key is invalid',
|
|
1036
|
+
});
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
it('should preserve imageList in children blocks', () => {
|
|
1040
|
+
const input: UIChatMessage[] = [
|
|
1041
|
+
{
|
|
1042
|
+
id: 'msg-1',
|
|
1043
|
+
role: 'assistant',
|
|
1044
|
+
content: 'Here are the images',
|
|
1045
|
+
imageList: [
|
|
1046
|
+
{ id: 'img-1', url: 'https://example.com/img1.jpg', alt: 'Image 1' },
|
|
1047
|
+
{ id: 'img-2', url: 'https://example.com/img2.jpg', alt: 'Image 2' },
|
|
1048
|
+
],
|
|
1049
|
+
tools: [
|
|
1050
|
+
{
|
|
1051
|
+
id: 'tool-1',
|
|
1052
|
+
identifier: 'test',
|
|
1053
|
+
apiName: 'test',
|
|
1054
|
+
arguments: '{}',
|
|
1055
|
+
type: 'default',
|
|
1056
|
+
},
|
|
1057
|
+
],
|
|
1058
|
+
createdAt: Date.now(),
|
|
1059
|
+
updatedAt: Date.now(),
|
|
1060
|
+
meta: {},
|
|
1061
|
+
} as UIChatMessage,
|
|
1062
|
+
];
|
|
1063
|
+
|
|
1064
|
+
const result = groupAssistantMessages(input);
|
|
1065
|
+
|
|
1066
|
+
// Child block should have imageList
|
|
1067
|
+
expect(result[0].children![0].imageList).toEqual([
|
|
1068
|
+
{ id: 'img-1', url: 'https://example.com/img1.jpg', alt: 'Image 1' },
|
|
1069
|
+
{ id: 'img-2', url: 'https://example.com/img2.jpg', alt: 'Image 2' },
|
|
1070
|
+
]);
|
|
1071
|
+
|
|
1072
|
+
// Parent should not have imageList (moved to children)
|
|
1073
|
+
expect(result[0].imageList).toBeUndefined();
|
|
1074
|
+
});
|
|
931
1075
|
});
|
|
932
1076
|
|
|
933
1077
|
describe('Empty and Null Cases', () => {
|
|
@@ -958,7 +1102,6 @@ describe('groupAssistantMessages', () => {
|
|
|
958
1102
|
|
|
959
1103
|
// Empty arrays should become undefined
|
|
960
1104
|
expect(result[0].children![0].imageList).toBeUndefined();
|
|
961
|
-
expect(result[0].children![0].fileList).toBeUndefined();
|
|
962
1105
|
});
|
|
963
1106
|
|
|
964
1107
|
it('should handle empty message list', () => {
|
|
@@ -243,6 +243,7 @@ export function groupAssistantMessages(messages: UIChatMessage[]): UIChatMessage
|
|
|
243
243
|
id: toolMsg.id,
|
|
244
244
|
state: toolMsg.pluginState,
|
|
245
245
|
},
|
|
246
|
+
result_msg_id: toolMsg.id,
|
|
246
247
|
};
|
|
247
248
|
}
|
|
248
249
|
|
|
@@ -253,10 +254,11 @@ export function groupAssistantMessages(messages: UIChatMessage[]): UIChatMessage
|
|
|
253
254
|
const { usage: msgUsage, performance: msgPerformance } = splitMetadata(msg.metadata);
|
|
254
255
|
children.push({
|
|
255
256
|
content: msg.content || '',
|
|
256
|
-
|
|
257
|
+
error: msg.error,
|
|
257
258
|
id: msg.id,
|
|
258
259
|
imageList: msg.imageList && msg.imageList.length > 0 ? msg.imageList : undefined,
|
|
259
260
|
performance: msgPerformance,
|
|
261
|
+
reasoning: msg.reasoning || undefined,
|
|
260
262
|
tools: toolsWithResults,
|
|
261
263
|
usage: msgUsage,
|
|
262
264
|
});
|
|
@@ -299,6 +301,7 @@ export function groupAssistantMessages(messages: UIChatMessage[]): UIChatMessage
|
|
|
299
301
|
id: followUpToolMsg.id,
|
|
300
302
|
state: followUpToolMsg.pluginState,
|
|
301
303
|
},
|
|
304
|
+
result_msg_id: followUpToolMsg.id,
|
|
302
305
|
};
|
|
303
306
|
}
|
|
304
307
|
|
|
@@ -311,16 +314,14 @@ export function groupAssistantMessages(messages: UIChatMessage[]): UIChatMessage
|
|
|
311
314
|
);
|
|
312
315
|
children.push({
|
|
313
316
|
content: followUpMsg.content || '',
|
|
314
|
-
|
|
315
|
-
followUpMsg.fileList && followUpMsg.fileList.length > 0
|
|
316
|
-
? followUpMsg.fileList
|
|
317
|
-
: undefined,
|
|
317
|
+
error: followUpMsg.error,
|
|
318
318
|
id: followUpMsg.id,
|
|
319
319
|
imageList:
|
|
320
320
|
followUpMsg.imageList && followUpMsg.imageList.length > 0
|
|
321
321
|
? followUpMsg.imageList
|
|
322
322
|
: undefined,
|
|
323
323
|
performance: followUpPerformance,
|
|
324
|
+
reasoning: followUpMsg.reasoning || undefined,
|
|
324
325
|
tools: followUpToolsWithResults,
|
|
325
326
|
usage: followUpUsage,
|
|
326
327
|
});
|
|
@@ -347,6 +348,7 @@ export function groupAssistantMessages(messages: UIChatMessage[]): UIChatMessage
|
|
|
347
348
|
assistantMsg.performance = aggregated.performance;
|
|
348
349
|
}
|
|
349
350
|
delete assistantMsg.metadata; // Clear individual metadata
|
|
351
|
+
delete assistantMsg.reasoning; // Reasoning moved to children blocks
|
|
350
352
|
delete assistantMsg.tools;
|
|
351
353
|
delete assistantMsg.imageList;
|
|
352
354
|
delete assistantMsg.fileList;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ILobeAgentRuntimeErrorType } from '@lobechat/model-runtime';
|
|
2
2
|
import type { IPluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
|
|
4
5
|
import { ErrorType } from '../../fetch';
|
|
5
6
|
|
|
@@ -12,6 +13,12 @@ export interface ChatMessageError {
|
|
|
12
13
|
type: ErrorType | IPluginErrorType | ILobeAgentRuntimeErrorType;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
export const ChatMessageErrorSchema = z.object({
|
|
17
|
+
body: z.any().optional(),
|
|
18
|
+
message: z.string(),
|
|
19
|
+
type: z.union([z.string(), z.number()]),
|
|
20
|
+
});
|
|
21
|
+
|
|
15
22
|
export interface ChatCitationItem {
|
|
16
23
|
id?: string;
|
|
17
24
|
onlyUrl?: boolean;
|
|
@@ -24,3 +31,9 @@ export interface ModelReasoning {
|
|
|
24
31
|
duration?: number;
|
|
25
32
|
signature?: string;
|
|
26
33
|
}
|
|
34
|
+
|
|
35
|
+
export const ModelReasoningSchema = z.object({
|
|
36
|
+
content: z.string().optional(),
|
|
37
|
+
duration: z.number().optional(),
|
|
38
|
+
signature: z.string().optional(),
|
|
39
|
+
});
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
1
3
|
export interface ChatImageItem {
|
|
2
4
|
alt: string;
|
|
3
5
|
id: string;
|
|
4
6
|
url: string;
|
|
5
7
|
}
|
|
6
8
|
|
|
9
|
+
export const ChatImageItemSchema = z.object({
|
|
10
|
+
alt: z.string(),
|
|
11
|
+
id: z.string(),
|
|
12
|
+
url: z.string(),
|
|
13
|
+
});
|
|
14
|
+
|
|
7
15
|
export interface ChatImageChunk {
|
|
8
16
|
data: string;
|
|
9
17
|
id: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
|
|
3
4
|
export interface ModelTokensUsage {
|
|
4
5
|
// Input tokens breakdown
|
|
@@ -38,6 +39,44 @@ export interface ModelTokensUsage {
|
|
|
38
39
|
totalTokens?: number;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
export const ModelUsageSchema = z.object({
|
|
43
|
+
// Input tokens breakdown
|
|
44
|
+
inputCachedTokens: z.number().optional(),
|
|
45
|
+
inputCacheMissTokens: z.number().optional(),
|
|
46
|
+
inputWriteCacheTokens: z.number().optional(),
|
|
47
|
+
inputTextTokens: z.number().optional(),
|
|
48
|
+
inputImageTokens: z.number().optional(),
|
|
49
|
+
inputAudioTokens: z.number().optional(),
|
|
50
|
+
inputCitationTokens: z.number().optional(),
|
|
51
|
+
|
|
52
|
+
// Output tokens breakdown
|
|
53
|
+
outputTextTokens: z.number().optional(),
|
|
54
|
+
outputImageTokens: z.number().optional(),
|
|
55
|
+
outputAudioTokens: z.number().optional(),
|
|
56
|
+
outputReasoningTokens: z.number().optional(),
|
|
57
|
+
|
|
58
|
+
// Prediction tokens
|
|
59
|
+
acceptedPredictionTokens: z.number().optional(),
|
|
60
|
+
rejectedPredictionTokens: z.number().optional(),
|
|
61
|
+
|
|
62
|
+
// Total tokens
|
|
63
|
+
totalInputTokens: z.number().optional(),
|
|
64
|
+
totalOutputTokens: z.number().optional(),
|
|
65
|
+
totalTokens: z.number().optional(),
|
|
66
|
+
|
|
67
|
+
// Cost
|
|
68
|
+
cost: z.number().optional(),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export const ModelPerformanceSchema = z.object({
|
|
72
|
+
tps: z.number().optional(),
|
|
73
|
+
ttft: z.number().optional(),
|
|
74
|
+
duration: z.number().optional(),
|
|
75
|
+
latency: z.number().optional(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export const MessageMetadataSchema = ModelUsageSchema.merge(ModelPerformanceSchema);
|
|
79
|
+
|
|
41
80
|
export interface ModelUsage extends ModelTokensUsage {
|
|
42
81
|
/**
|
|
43
82
|
* dollar
|
|
@@ -16,6 +16,7 @@ export interface ChatToolPayload {
|
|
|
16
16
|
arguments: string;
|
|
17
17
|
id: string;
|
|
18
18
|
identifier: string;
|
|
19
|
+
result_msg_id?: string;
|
|
19
20
|
type: LobeToolRenderType;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -85,6 +86,15 @@ export const MessageToolCallSchema = z.object({
|
|
|
85
86
|
type: z.string(),
|
|
86
87
|
});
|
|
87
88
|
|
|
89
|
+
export const ChatToolPayloadSchema = z.object({
|
|
90
|
+
apiName: z.string(),
|
|
91
|
+
arguments: z.string(),
|
|
92
|
+
id: z.string(),
|
|
93
|
+
identifier: z.string(),
|
|
94
|
+
result_msg_id: z.string().optional(),
|
|
95
|
+
type: z.string(),
|
|
96
|
+
});
|
|
97
|
+
|
|
88
98
|
/**
|
|
89
99
|
* 聊天消息错误对象
|
|
90
100
|
*/
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { GroundingSearch, GroundingSearchSchema } from '../../search';
|
|
2
5
|
import {
|
|
3
6
|
ChatImageItem,
|
|
7
|
+
ChatImageItemSchema,
|
|
4
8
|
ChatMessageError,
|
|
9
|
+
ChatMessageErrorSchema,
|
|
5
10
|
ChatToolPayload,
|
|
11
|
+
ChatToolPayloadSchema,
|
|
6
12
|
MessageMetadata,
|
|
13
|
+
MessageMetadataSchema,
|
|
7
14
|
MessageToolCall,
|
|
15
|
+
MessageToolCallSchema,
|
|
8
16
|
ModelReasoning,
|
|
17
|
+
ModelReasoningSchema,
|
|
9
18
|
} from '../common';
|
|
10
19
|
import { UIChatMessage } from '../ui';
|
|
11
20
|
|
|
@@ -34,6 +43,21 @@ export interface CreateMessageResult {
|
|
|
34
43
|
messages: UIChatMessage[];
|
|
35
44
|
}
|
|
36
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Result type for updateMessage
|
|
48
|
+
* Contains success status and optional message list
|
|
49
|
+
*/
|
|
50
|
+
export interface UpdateMessageResult {
|
|
51
|
+
/**
|
|
52
|
+
* Updated message list (only present when success is true and sessionId/topicId provided)
|
|
53
|
+
*/
|
|
54
|
+
messages?: UIChatMessage[];
|
|
55
|
+
/**
|
|
56
|
+
* Whether the update was successful
|
|
57
|
+
*/
|
|
58
|
+
success: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
37
61
|
export interface NewMessage {
|
|
38
62
|
agentId?: string | null;
|
|
39
63
|
clientId?: string | null;
|
|
@@ -67,12 +91,14 @@ export interface UpdateMessageParams {
|
|
|
67
91
|
imageList?: ChatImageItem[];
|
|
68
92
|
metadata?: MessageMetadata;
|
|
69
93
|
model?: string;
|
|
94
|
+
observationId?: string;
|
|
70
95
|
provider?: string;
|
|
71
96
|
reasoning?: ModelReasoning;
|
|
72
97
|
role?: string;
|
|
73
98
|
search?: GroundingSearch;
|
|
74
99
|
toolCalls?: MessageToolCall[];
|
|
75
100
|
tools?: ChatToolPayload[] | null;
|
|
101
|
+
traceId?: string;
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
export interface NewMessageQueryParams {
|
|
@@ -81,3 +107,23 @@ export interface NewMessageQueryParams {
|
|
|
81
107
|
rewriteQuery: string;
|
|
82
108
|
userQuery: string;
|
|
83
109
|
}
|
|
110
|
+
|
|
111
|
+
// ========== Zod Schemas ========== //
|
|
112
|
+
|
|
113
|
+
export const UpdateMessageParamsSchema = z
|
|
114
|
+
.object({
|
|
115
|
+
content: z.string().optional(),
|
|
116
|
+
error: ChatMessageErrorSchema.nullable().optional(),
|
|
117
|
+
imageList: z.array(ChatImageItemSchema).optional(),
|
|
118
|
+
metadata: MessageMetadataSchema.optional(),
|
|
119
|
+
model: z.string().optional(),
|
|
120
|
+
observationId: z.string().optional(),
|
|
121
|
+
provider: z.string().optional(),
|
|
122
|
+
reasoning: ModelReasoningSchema.optional(),
|
|
123
|
+
role: z.string().optional(),
|
|
124
|
+
search: GroundingSearchSchema.optional(),
|
|
125
|
+
toolCalls: z.array(MessageToolCallSchema).optional(),
|
|
126
|
+
tools: z.array(ChatToolPayloadSchema).nullable().optional(),
|
|
127
|
+
traceId: z.string().optional(),
|
|
128
|
+
})
|
|
129
|
+
.passthrough();
|
|
@@ -26,10 +26,11 @@ export interface ChatFileItem {
|
|
|
26
26
|
|
|
27
27
|
export interface AssistantContentBlock {
|
|
28
28
|
content: string;
|
|
29
|
-
|
|
29
|
+
error?: ChatMessageError | null;
|
|
30
30
|
id: string;
|
|
31
31
|
imageList?: ChatImageItem[];
|
|
32
32
|
performance?: ModelPerformance;
|
|
33
|
+
reasoning?: ModelReasoning;
|
|
33
34
|
tools?: ChatToolPayloadWithResult[];
|
|
34
35
|
usage?: ModelUsage;
|
|
35
36
|
}
|
|
@@ -62,6 +63,7 @@ export interface UIChatMessage {
|
|
|
62
63
|
imageList?: ChatImageItem[];
|
|
63
64
|
meta: MetaData;
|
|
64
65
|
metadata?: MessageMetadata | null;
|
|
66
|
+
model?: string | null;
|
|
65
67
|
/**
|
|
66
68
|
* observation id
|
|
67
69
|
*/
|
|
@@ -78,6 +80,7 @@ export interface UIChatMessage {
|
|
|
78
80
|
plugin?: ChatPluginPayload;
|
|
79
81
|
pluginError?: any;
|
|
80
82
|
pluginState?: any;
|
|
83
|
+
provider?: string | null;
|
|
81
84
|
/**
|
|
82
85
|
* quoted other message's id
|
|
83
86
|
*/
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
1
3
|
export type SearchMode = 'off' | 'auto' | 'on';
|
|
2
4
|
|
|
3
5
|
export enum ModelSearchImplement {
|
|
@@ -27,3 +29,17 @@ export interface GroundingSearch {
|
|
|
27
29
|
citations?: CitationItem[];
|
|
28
30
|
searchQueries?: string[];
|
|
29
31
|
}
|
|
32
|
+
|
|
33
|
+
export const GroundingSearchSchema = z.object({
|
|
34
|
+
citations: z
|
|
35
|
+
.array(
|
|
36
|
+
z.object({
|
|
37
|
+
favicon: z.string().optional(),
|
|
38
|
+
id: z.string().optional(),
|
|
39
|
+
title: z.string().optional(),
|
|
40
|
+
url: z.string(),
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
.optional(),
|
|
44
|
+
searchQueries: z.array(z.string()).optional(),
|
|
45
|
+
});
|