@lobehub/lobehub 2.0.0-next.23 → 2.0.0-next.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/labs.json +4 -0
- package/locales/bg-BG/labs.json +4 -0
- package/locales/de-DE/labs.json +4 -0
- package/locales/en-US/labs.json +4 -0
- package/locales/es-ES/labs.json +4 -0
- package/locales/fa-IR/labs.json +4 -0
- package/locales/fr-FR/labs.json +4 -0
- package/locales/it-IT/labs.json +4 -0
- package/locales/ja-JP/labs.json +4 -0
- package/locales/ko-KR/labs.json +4 -0
- package/locales/nl-NL/labs.json +4 -0
- package/locales/pl-PL/labs.json +4 -0
- package/locales/pt-BR/labs.json +4 -0
- package/locales/ru-RU/labs.json +4 -0
- package/locales/tr-TR/labs.json +4 -0
- package/locales/vi-VN/labs.json +4 -0
- package/locales/zh-CN/labs.json +4 -0
- package/locales/zh-TW/labs.json +4 -0
- package/package.json +1 -1
- package/packages/const/src/user.ts +5 -2
- package/packages/types/src/index.ts +0 -1
- package/packages/types/src/user/index.ts +2 -88
- package/packages/types/src/user/preference.ts +105 -0
- package/renovate.json +1 -6
- package/src/app/[variants]/(main)/labs/components/LabCard.tsx +5 -5
- package/src/app/[variants]/(main)/labs/page.tsx +19 -22
- package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/azureai/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/bedrock/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/cloudflare/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/comfyui/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/github/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/vertexai/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +2 -4
- package/src/components/Skeleton/SkeletonSwitch.tsx +13 -0
- package/src/components/Skeleton/index.ts +2 -0
- package/src/features/ChatInput/ActionBar/index.tsx +2 -2
- package/src/features/ChatInput/InputEditor/index.tsx +2 -2
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +152 -0
- package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +70 -0
- package/src/features/Conversation/Messages/Group/Actions/index.tsx +21 -0
- package/src/features/Conversation/Messages/Group/ContentBlock.tsx +91 -0
- package/src/features/Conversation/Messages/Group/EditState.tsx +51 -0
- package/src/features/Conversation/Messages/Group/Error/index.tsx +53 -0
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +73 -0
- package/src/features/Conversation/Messages/Group/MessageContent.tsx +39 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +49 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/Debug.tsx +70 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginResult.tsx +34 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginState.tsx +18 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/Settings.tsx +40 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +92 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +176 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ValueCell.tsx +43 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +134 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +88 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/ErrorResponse.tsx +35 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +29 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +66 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +105 -0
- package/src/features/Conversation/Messages/Group/Tool/index.tsx +75 -0
- package/src/features/Conversation/Messages/Group/Tools.tsx +46 -0
- package/src/features/Conversation/Messages/Group/index.tsx +140 -0
- package/src/features/Conversation/Messages/index.tsx +12 -0
- package/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx +2 -2
- package/src/locales/default/labs.ts +4 -0
- package/src/server/routers/lambda/message.ts +5 -20
- package/src/services/chat/contextEngineering.ts +6 -5
- package/src/services/message/server.ts +10 -10
- package/src/services/message/type.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +309 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +2 -22
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +272 -14
- package/src/store/user/selectors.ts +1 -1
- package/src/store/user/slices/preference/action.ts +8 -1
- package/src/store/user/slices/preference/selectors/index.ts +2 -0
- package/src/store/user/slices/preference/selectors/labPrefer.ts +13 -0
- package/src/store/user/slices/preference/{selectors.ts → selectors/preference.ts} +0 -2
- /package/src/{app/[variants]/(main)/settings/provider/features/ProviderConfig → components/Skeleton}/SkeletonInput.tsx +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { UIChatMessage } from '@lobechat/types';
|
|
4
|
+
import { useResponsive } from 'antd-style';
|
|
5
|
+
import { memo, useCallback } from 'react';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import Avatar from '@/features/ChatItem/components/Avatar';
|
|
9
|
+
import BorderSpacing from '@/features/ChatItem/components/BorderSpacing';
|
|
10
|
+
import Title from '@/features/ChatItem/components/Title';
|
|
11
|
+
import { useStyles } from '@/features/ChatItem/style';
|
|
12
|
+
import GroupChildren from '@/features/Conversation/Messages/Group/GroupChildren';
|
|
13
|
+
import Usage from '@/features/Conversation/components/Extras/Usage';
|
|
14
|
+
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
15
|
+
import { useAgentStore } from '@/store/agent';
|
|
16
|
+
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
17
|
+
import { useChatStore } from '@/store/chat';
|
|
18
|
+
import { chatSelectors, messageStateSelectors } from '@/store/chat/slices/message/selectors';
|
|
19
|
+
import { useGlobalStore } from '@/store/global';
|
|
20
|
+
import { useSessionStore } from '@/store/session';
|
|
21
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
22
|
+
|
|
23
|
+
import { GroupActionsBar } from './Actions';
|
|
24
|
+
import EditState from './EditState';
|
|
25
|
+
|
|
26
|
+
const MOBILE_AVATAR_SIZE = 32;
|
|
27
|
+
|
|
28
|
+
interface GroupMessageProps extends UIChatMessage {
|
|
29
|
+
disableEditing?: boolean;
|
|
30
|
+
index: number;
|
|
31
|
+
showTitle?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const GroupMessage = memo<GroupMessageProps>((props) => {
|
|
35
|
+
const {
|
|
36
|
+
showTitle,
|
|
37
|
+
id,
|
|
38
|
+
disableEditing,
|
|
39
|
+
usage,
|
|
40
|
+
index,
|
|
41
|
+
createdAt,
|
|
42
|
+
meta,
|
|
43
|
+
children,
|
|
44
|
+
performance,
|
|
45
|
+
model,
|
|
46
|
+
provider,
|
|
47
|
+
} = props;
|
|
48
|
+
const avatar = meta;
|
|
49
|
+
const { mobile } = useResponsive();
|
|
50
|
+
const placement = 'left';
|
|
51
|
+
const type = useAgentStore(agentChatConfigSelectors.displayMode);
|
|
52
|
+
const variant = type === 'chat' ? 'bubble' : 'docs';
|
|
53
|
+
|
|
54
|
+
const { styles } = useStyles({
|
|
55
|
+
editing: false,
|
|
56
|
+
placement,
|
|
57
|
+
primary: false,
|
|
58
|
+
showTitle,
|
|
59
|
+
time: createdAt,
|
|
60
|
+
title: avatar.title,
|
|
61
|
+
variant,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
|
|
65
|
+
const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
|
|
66
|
+
const openChatSettings = useOpenChatSettings();
|
|
67
|
+
const lastAssistantMsg = useChatStore(chatSelectors.getGroupLatestMessageWithoutTools(id));
|
|
68
|
+
|
|
69
|
+
const contentId = lastAssistantMsg?.id;
|
|
70
|
+
|
|
71
|
+
const isEditing = useChatStore(messageStateSelectors.isMessageEditing(contentId || ''));
|
|
72
|
+
|
|
73
|
+
// ======================= Performance Optimization ======================= //
|
|
74
|
+
// these useMemo/useCallback are all for the performance optimization
|
|
75
|
+
// maybe we can remove it in React 19
|
|
76
|
+
// ======================================================================== //
|
|
77
|
+
const onAvatarClick = useCallback(() => {
|
|
78
|
+
if (!isInbox) {
|
|
79
|
+
toggleSystemRole(true);
|
|
80
|
+
} else {
|
|
81
|
+
openChatSettings();
|
|
82
|
+
}
|
|
83
|
+
}, [isInbox]);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Flexbox className={styles.container} gap={mobile ? 6 : 12}>
|
|
87
|
+
<Flexbox gap={4} horizontal>
|
|
88
|
+
<Avatar
|
|
89
|
+
alt={avatar.title || 'avatar'}
|
|
90
|
+
avatar={avatar}
|
|
91
|
+
onClick={onAvatarClick}
|
|
92
|
+
placement={placement}
|
|
93
|
+
size={mobile ? MOBILE_AVATAR_SIZE : undefined}
|
|
94
|
+
style={{ marginTop: 6 }}
|
|
95
|
+
/>
|
|
96
|
+
<Title avatar={avatar} placement={placement} showTitle time={createdAt} />
|
|
97
|
+
</Flexbox>
|
|
98
|
+
{isEditing && contentId ? (
|
|
99
|
+
<EditState content={lastAssistantMsg?.content} id={contentId} />
|
|
100
|
+
) : (
|
|
101
|
+
<Flexbox
|
|
102
|
+
align={'flex-start'}
|
|
103
|
+
className={styles.messageContent}
|
|
104
|
+
data-layout={'vertical'}
|
|
105
|
+
direction={'vertical'}
|
|
106
|
+
gap={8}
|
|
107
|
+
width={'100%'}
|
|
108
|
+
>
|
|
109
|
+
{children && children.length > 0 && (
|
|
110
|
+
<GroupChildren
|
|
111
|
+
blocks={children}
|
|
112
|
+
contentId={contentId}
|
|
113
|
+
disableEditing={disableEditing}
|
|
114
|
+
messageIndex={index}
|
|
115
|
+
/>
|
|
116
|
+
)}
|
|
117
|
+
|
|
118
|
+
{model && (
|
|
119
|
+
<Usage metadata={{ ...performance, ...usage }} model={model} provider={provider!} />
|
|
120
|
+
)}
|
|
121
|
+
{!disableEditing && (
|
|
122
|
+
<Flexbox align={'flex-start'} className={styles.actions} role="menubar">
|
|
123
|
+
<GroupActionsBar
|
|
124
|
+
contentBlock={lastAssistantMsg}
|
|
125
|
+
contentId={contentId}
|
|
126
|
+
data={props}
|
|
127
|
+
id={id}
|
|
128
|
+
index={index}
|
|
129
|
+
/>
|
|
130
|
+
</Flexbox>
|
|
131
|
+
)}
|
|
132
|
+
</Flexbox>
|
|
133
|
+
)}
|
|
134
|
+
|
|
135
|
+
{mobile && <BorderSpacing borderSpacing={MOBILE_AVATAR_SIZE} />}
|
|
136
|
+
</Flexbox>
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export default GroupMessage;
|
|
@@ -16,6 +16,7 @@ import { chatSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
|
16
16
|
import History from '../components/History';
|
|
17
17
|
import { InPortalThreadContext } from '../context/InPortalThreadContext';
|
|
18
18
|
import AssistantMessage from './Assistant';
|
|
19
|
+
import GroupMessage from './Group';
|
|
19
20
|
import SupervisorMessage from './Supervisor';
|
|
20
21
|
import UserMessage from './User';
|
|
21
22
|
|
|
@@ -132,6 +133,17 @@ const Item = memo<ChatListItemProps>(
|
|
|
132
133
|
);
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
case 'group': {
|
|
137
|
+
return (
|
|
138
|
+
<GroupMessage
|
|
139
|
+
{...item}
|
|
140
|
+
disableEditing={disableEditing}
|
|
141
|
+
index={index}
|
|
142
|
+
showTitle={item.groupId ? true : false}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
135
147
|
case 'supervisor': {
|
|
136
148
|
return <SupervisorMessage {...item} disableEditing={disableEditing} index={index} />;
|
|
137
149
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OFFICIAL_DOMAIN } from '@lobechat/const';
|
|
1
2
|
import { UIChatMessage } from '@lobechat/types';
|
|
2
3
|
import { ModelTag } from '@lobehub/icons';
|
|
3
4
|
import { Avatar } from '@lobehub/ui';
|
|
@@ -14,7 +15,6 @@ import { agentSelectors } from '@/store/agent/selectors';
|
|
|
14
15
|
import { useSessionStore } from '@/store/session';
|
|
15
16
|
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
16
17
|
|
|
17
|
-
import pkg from '../../../../../../package.json';
|
|
18
18
|
import { useContainerStyles } from '../style';
|
|
19
19
|
import { useStyles } from './style';
|
|
20
20
|
import { FieldType } from './type';
|
|
@@ -75,7 +75,7 @@ const Preview = memo<PreviewProps>(
|
|
|
75
75
|
{withFooter ? (
|
|
76
76
|
<Flexbox align={'center'} className={styles.footer} gap={4}>
|
|
77
77
|
<ProductLogo type={'combine'} />
|
|
78
|
-
<div className={styles.url}>{
|
|
78
|
+
<div className={styles.url}>{OFFICIAL_DOMAIN}</div>
|
|
79
79
|
</Flexbox>
|
|
80
80
|
) : (
|
|
81
81
|
<div />
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CreateMessageParamsSchema,
|
|
3
3
|
CreateNewMessageParamsSchema,
|
|
4
|
-
UIChatMessage,
|
|
5
4
|
UpdateMessageParamsSchema,
|
|
6
5
|
UpdateMessageRAGParamsSchema,
|
|
7
6
|
} from '@lobechat/types';
|
|
@@ -14,8 +13,6 @@ import { authedProcedure, publicProcedure, router } from '@/libs/trpc/lambda';
|
|
|
14
13
|
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
|
15
14
|
import { FileService } from '@/server/services/file';
|
|
16
15
|
|
|
17
|
-
type ChatMessageList = UIChatMessage[];
|
|
18
|
-
|
|
19
16
|
const messageProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
|
20
17
|
const { ctx } = opts;
|
|
21
18
|
|
|
@@ -72,22 +69,6 @@ export const messageRouter = router({
|
|
|
72
69
|
});
|
|
73
70
|
}),
|
|
74
71
|
|
|
75
|
-
// TODO: it will be removed in V2
|
|
76
|
-
getAllMessages: messageProcedure.query(async ({ ctx }): Promise<ChatMessageList> => {
|
|
77
|
-
return ctx.messageModel.queryAll() as any;
|
|
78
|
-
}),
|
|
79
|
-
|
|
80
|
-
// TODO: it will be removed in V2
|
|
81
|
-
getAllMessagesInSession: messageProcedure
|
|
82
|
-
.input(
|
|
83
|
-
z.object({
|
|
84
|
-
sessionId: z.string().nullable().optional(),
|
|
85
|
-
}),
|
|
86
|
-
)
|
|
87
|
-
.query(async ({ ctx, input }): Promise<ChatMessageList> => {
|
|
88
|
-
return ctx.messageModel.queryBySessionId(input.sessionId) as any;
|
|
89
|
-
}),
|
|
90
|
-
|
|
91
72
|
getHeatmaps: messageProcedure.query(async ({ ctx }) => {
|
|
92
73
|
return ctx.messageModel.getHeatmaps();
|
|
93
74
|
}),
|
|
@@ -101,16 +82,20 @@ export const messageRouter = router({
|
|
|
101
82
|
pageSize: z.number().optional(),
|
|
102
83
|
sessionId: z.string().nullable().optional(),
|
|
103
84
|
topicId: z.string().nullable().optional(),
|
|
85
|
+
useGroup: z.boolean().optional(),
|
|
104
86
|
}),
|
|
105
87
|
)
|
|
106
88
|
.query(async ({ input, ctx }) => {
|
|
107
89
|
if (!ctx.userId) return [];
|
|
108
90
|
const serverDB = await getServerDB();
|
|
109
91
|
|
|
92
|
+
const { useGroup, ...queryParams } = input;
|
|
93
|
+
|
|
110
94
|
const messageModel = new MessageModel(serverDB, ctx.userId);
|
|
111
95
|
const fileService = new FileService(serverDB, ctx.userId);
|
|
112
96
|
|
|
113
|
-
return messageModel.query(
|
|
97
|
+
return messageModel.query(queryParams, {
|
|
98
|
+
groupAssistantMessages: useGroup ?? false,
|
|
114
99
|
postProcessUrl: (path) => fileService.getFullFileUrl(path),
|
|
115
100
|
});
|
|
116
101
|
}),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isDesktop, isServerMode } from '@lobechat/const';
|
|
2
2
|
import {
|
|
3
3
|
ContextEngine,
|
|
4
|
+
GroupMessageFlattenProcessor,
|
|
4
5
|
HistorySummaryProvider,
|
|
5
6
|
HistoryTruncateProcessor,
|
|
6
7
|
InputTemplateProcessor,
|
|
@@ -28,7 +29,6 @@ interface ContextEngineeringContext {
|
|
|
28
29
|
historyCount?: number;
|
|
29
30
|
historySummary?: string;
|
|
30
31
|
inputTemplate?: string;
|
|
31
|
-
isWelcomeQuestion?: boolean;
|
|
32
32
|
messages: UIChatMessage[];
|
|
33
33
|
model: string;
|
|
34
34
|
provider: string;
|
|
@@ -78,14 +78,15 @@ export const contextEngineering = async ({
|
|
|
78
78
|
// Create message processing processors
|
|
79
79
|
|
|
80
80
|
// 6. Input template processing
|
|
81
|
-
new InputTemplateProcessor({
|
|
82
|
-
inputTemplate,
|
|
83
|
-
}),
|
|
81
|
+
new InputTemplateProcessor({ inputTemplate }),
|
|
84
82
|
|
|
85
83
|
// 7. Placeholder variables processing
|
|
86
84
|
new PlaceholderVariablesProcessor({ variableGenerators: VARIABLE_GENERATORS }),
|
|
87
85
|
|
|
88
|
-
// 8.
|
|
86
|
+
// 8. Group message flatten (convert role=group to standard assistant + tool messages)
|
|
87
|
+
new GroupMessageFlattenProcessor(),
|
|
88
|
+
|
|
89
|
+
// 8.5 Message content processing
|
|
89
90
|
new MessageContentProcessor({
|
|
90
91
|
fileContext: { enabled: isServerMode, includeFileUrl: !isDesktop },
|
|
91
92
|
isCanUseVideo,
|
|
@@ -3,6 +3,8 @@ import { ChatTranslate, UIChatMessage } from '@lobechat/types';
|
|
|
3
3
|
|
|
4
4
|
import { INBOX_SESSION_ID } from '@/const/session';
|
|
5
5
|
import { lambdaClient } from '@/libs/trpc/client';
|
|
6
|
+
import { useUserStore } from '@/store/user';
|
|
7
|
+
import { labPreferSelectors } from '@/store/user/selectors';
|
|
6
8
|
|
|
7
9
|
import { IMessageService } from './type';
|
|
8
10
|
|
|
@@ -22,33 +24,31 @@ export class ServerService implements IMessageService {
|
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
getMessages: IMessageService['getMessages'] = async (sessionId, topicId, groupId) => {
|
|
27
|
+
// Get user lab preference for message grouping
|
|
28
|
+
const useGroup = labPreferSelectors.enableAssistantMessageGroup(useUserStore.getState());
|
|
29
|
+
|
|
25
30
|
const data = await lambdaClient.message.getMessages.query({
|
|
26
31
|
groupId,
|
|
27
32
|
sessionId: this.toDbSessionId(sessionId),
|
|
28
33
|
topicId,
|
|
34
|
+
useGroup,
|
|
29
35
|
});
|
|
30
36
|
|
|
31
37
|
return data as unknown as UIChatMessage[];
|
|
32
38
|
};
|
|
33
39
|
|
|
34
40
|
getGroupMessages: IMessageService['getGroupMessages'] = async (groupId, topicId) => {
|
|
41
|
+
// Get user lab preference for message grouping
|
|
42
|
+
const useGroup = labPreferSelectors.enableAssistantMessageGroup(useUserStore.getState());
|
|
43
|
+
|
|
35
44
|
const data = await lambdaClient.message.getMessages.query({
|
|
36
45
|
groupId,
|
|
37
46
|
topicId,
|
|
47
|
+
useGroup,
|
|
38
48
|
});
|
|
39
49
|
return data as unknown as UIChatMessage[];
|
|
40
50
|
};
|
|
41
51
|
|
|
42
|
-
getAllMessages: IMessageService['getAllMessages'] = async () => {
|
|
43
|
-
return lambdaClient.message.getAllMessages.query();
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
getAllMessagesInSession: IMessageService['getAllMessagesInSession'] = async (sessionId) => {
|
|
47
|
-
return lambdaClient.message.getAllMessagesInSession.query({
|
|
48
|
-
sessionId: this.toDbSessionId(sessionId),
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
|
|
52
52
|
countMessages: IMessageService['countMessages'] = async (params) => {
|
|
53
53
|
return lambdaClient.message.count.query(params);
|
|
54
54
|
};
|
|
@@ -21,8 +21,6 @@ export interface IMessageService {
|
|
|
21
21
|
|
|
22
22
|
getMessages(sessionId: string, topicId?: string, groupId?: string): Promise<UIChatMessage[]>;
|
|
23
23
|
getGroupMessages(groupId: string, topicId?: string): Promise<UIChatMessage[]>;
|
|
24
|
-
getAllMessages(): Promise<UIChatMessage[]>;
|
|
25
|
-
getAllMessagesInSession(sessionId: string): Promise<UIChatMessage[]>;
|
|
26
24
|
countMessages(params?: {
|
|
27
25
|
endDate?: string;
|
|
28
26
|
range?: [string, string];
|
|
@@ -541,7 +541,7 @@ describe('generateAIChatV2 actions', () => {
|
|
|
541
541
|
result.current.cancelSendMessageInServer();
|
|
542
542
|
});
|
|
543
543
|
|
|
544
|
-
expect(mockAbort).toHaveBeenCalledWith('User cancelled
|
|
544
|
+
expect(mockAbort).toHaveBeenCalledWith('User cancelled sendMessage operation');
|
|
545
545
|
expect(
|
|
546
546
|
result.current.mainSendMessageOperations[
|
|
547
547
|
messageMapKey(TEST_IDS.SESSION_ID, TEST_IDS.TOPIC_ID)
|
|
@@ -571,7 +571,7 @@ describe('generateAIChatV2 actions', () => {
|
|
|
571
571
|
result.current.cancelSendMessageInServer(customTopicId);
|
|
572
572
|
});
|
|
573
573
|
|
|
574
|
-
expect(mockAbort).toHaveBeenCalledWith('User cancelled
|
|
574
|
+
expect(mockAbort).toHaveBeenCalledWith('User cancelled sendMessage operation');
|
|
575
575
|
});
|
|
576
576
|
|
|
577
577
|
it('should handle gracefully when operation does not exist', () => {
|
|
@@ -740,4 +740,311 @@ describe('generateAIChatV2 actions', () => {
|
|
|
740
740
|
});
|
|
741
741
|
});
|
|
742
742
|
});
|
|
743
|
+
|
|
744
|
+
describe('callToolFollowAssistantMessage', () => {
|
|
745
|
+
const TOOL_RESULT_MSG_ID = 'tool-result-msg-id';
|
|
746
|
+
const ASSISTANT_BLOCK_ID = 'assistant-block-id';
|
|
747
|
+
const GROUP_MESSAGE_ID = 'group-message-id';
|
|
748
|
+
const TOOL_CALL_ID = 'tool-call-id';
|
|
749
|
+
|
|
750
|
+
beforeEach(() => {
|
|
751
|
+
// Reset mocks
|
|
752
|
+
vi.spyOn(messageService, 'createNewMessage').mockResolvedValue({
|
|
753
|
+
id: 'new-assistant-block-id',
|
|
754
|
+
messages: [] as any,
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it('should find group message from tool result_msg_id in tools array', async () => {
|
|
759
|
+
const { result } = renderHook(() => useChatStore());
|
|
760
|
+
const dispatchSpy = vi.fn();
|
|
761
|
+
|
|
762
|
+
// Create a group message structure with tool results
|
|
763
|
+
const groupMessage: UIChatMessage = {
|
|
764
|
+
id: GROUP_MESSAGE_ID,
|
|
765
|
+
role: 'group',
|
|
766
|
+
content: '',
|
|
767
|
+
sessionId: TEST_IDS.SESSION_ID,
|
|
768
|
+
topicId: TEST_IDS.TOPIC_ID,
|
|
769
|
+
children: [
|
|
770
|
+
{
|
|
771
|
+
id: ASSISTANT_BLOCK_ID,
|
|
772
|
+
content: 'Assistant response',
|
|
773
|
+
tools: [
|
|
774
|
+
{
|
|
775
|
+
id: TOOL_CALL_ID,
|
|
776
|
+
type: 'builtin',
|
|
777
|
+
apiName: 'testTool',
|
|
778
|
+
identifier: 'test-tool',
|
|
779
|
+
arguments: '{}',
|
|
780
|
+
result: {
|
|
781
|
+
id: TOOL_RESULT_MSG_ID,
|
|
782
|
+
content: 'Tool result',
|
|
783
|
+
},
|
|
784
|
+
result_msg_id: TOOL_RESULT_MSG_ID,
|
|
785
|
+
},
|
|
786
|
+
],
|
|
787
|
+
},
|
|
788
|
+
],
|
|
789
|
+
} as any;
|
|
790
|
+
|
|
791
|
+
act(() => {
|
|
792
|
+
useChatStore.setState({
|
|
793
|
+
activeId: TEST_IDS.SESSION_ID,
|
|
794
|
+
activeTopicId: TEST_IDS.TOPIC_ID,
|
|
795
|
+
messagesMap: {
|
|
796
|
+
[messageMapKey(TEST_IDS.SESSION_ID, TEST_IDS.TOPIC_ID)]: [groupMessage],
|
|
797
|
+
},
|
|
798
|
+
internal_execAgentRuntime: vi.fn(),
|
|
799
|
+
internal_dispatchMessage: dispatchSpy,
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
await act(async () => {
|
|
804
|
+
await result.current.callToolFollowAssistantMessage({
|
|
805
|
+
parentId: TOOL_RESULT_MSG_ID,
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
// Verify that addGroupBlock was called with the correct groupMessageId
|
|
810
|
+
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
811
|
+
expect.objectContaining({
|
|
812
|
+
type: 'addGroupBlock',
|
|
813
|
+
groupMessageId: GROUP_MESSAGE_ID,
|
|
814
|
+
}),
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
// Verify that createNewMessage was called with message params
|
|
818
|
+
expect(messageService.createNewMessage).toHaveBeenCalledWith(
|
|
819
|
+
expect.objectContaining({
|
|
820
|
+
role: 'assistant',
|
|
821
|
+
parentId: TOOL_RESULT_MSG_ID,
|
|
822
|
+
}),
|
|
823
|
+
);
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
it('should handle case when tool result is not found in any group message', async () => {
|
|
827
|
+
const { result } = renderHook(() => useChatStore());
|
|
828
|
+
const dispatchSpy = vi.fn();
|
|
829
|
+
|
|
830
|
+
const groupMessage: UIChatMessage = {
|
|
831
|
+
id: GROUP_MESSAGE_ID,
|
|
832
|
+
role: 'group',
|
|
833
|
+
content: '',
|
|
834
|
+
sessionId: TEST_IDS.SESSION_ID,
|
|
835
|
+
children: [
|
|
836
|
+
{
|
|
837
|
+
id: ASSISTANT_BLOCK_ID,
|
|
838
|
+
content: 'Assistant response',
|
|
839
|
+
tools: [], // No tools
|
|
840
|
+
},
|
|
841
|
+
],
|
|
842
|
+
} as any;
|
|
843
|
+
|
|
844
|
+
act(() => {
|
|
845
|
+
useChatStore.setState({
|
|
846
|
+
activeId: TEST_IDS.SESSION_ID,
|
|
847
|
+
activeTopicId: TEST_IDS.TOPIC_ID,
|
|
848
|
+
messagesMap: {
|
|
849
|
+
[messageMapKey(TEST_IDS.SESSION_ID, TEST_IDS.TOPIC_ID)]: [groupMessage],
|
|
850
|
+
},
|
|
851
|
+
internal_execAgentRuntime: vi.fn(),
|
|
852
|
+
internal_dispatchMessage: dispatchSpy,
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
await act(async () => {
|
|
857
|
+
await result.current.callToolFollowAssistantMessage({
|
|
858
|
+
parentId: 'non-existent-tool-result-id',
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// Should create message as regular top-level message (createMessage, not addGroupBlock)
|
|
863
|
+
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
864
|
+
expect.objectContaining({
|
|
865
|
+
type: 'createMessage',
|
|
866
|
+
}),
|
|
867
|
+
);
|
|
868
|
+
|
|
869
|
+
expect(messageService.createNewMessage).toHaveBeenCalledWith(
|
|
870
|
+
expect.objectContaining({
|
|
871
|
+
role: 'assistant',
|
|
872
|
+
parentId: 'non-existent-tool-result-id',
|
|
873
|
+
}),
|
|
874
|
+
);
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
it('should find group message from nested tool results in multiple children', async () => {
|
|
878
|
+
const { result } = renderHook(() => useChatStore());
|
|
879
|
+
const dispatchSpy = vi.fn();
|
|
880
|
+
|
|
881
|
+
const groupMessage: UIChatMessage = {
|
|
882
|
+
id: GROUP_MESSAGE_ID,
|
|
883
|
+
role: 'group',
|
|
884
|
+
content: '',
|
|
885
|
+
sessionId: TEST_IDS.SESSION_ID,
|
|
886
|
+
children: [
|
|
887
|
+
{
|
|
888
|
+
id: 'first-block',
|
|
889
|
+
content: 'First assistant response',
|
|
890
|
+
tools: [
|
|
891
|
+
{
|
|
892
|
+
id: 'tool-1',
|
|
893
|
+
type: 'builtin',
|
|
894
|
+
apiName: 'tool1',
|
|
895
|
+
identifier: 'tool-1',
|
|
896
|
+
arguments: '{}',
|
|
897
|
+
result_msg_id: 'other-result-id',
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
id: 'second-block',
|
|
903
|
+
content: 'Second assistant response',
|
|
904
|
+
tools: [
|
|
905
|
+
{
|
|
906
|
+
id: 'tool-2',
|
|
907
|
+
type: 'builtin',
|
|
908
|
+
apiName: 'tool2',
|
|
909
|
+
identifier: 'tool-2',
|
|
910
|
+
arguments: '{}',
|
|
911
|
+
result_msg_id: TOOL_RESULT_MSG_ID, // Target tool result
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
},
|
|
915
|
+
],
|
|
916
|
+
} as any;
|
|
917
|
+
|
|
918
|
+
act(() => {
|
|
919
|
+
useChatStore.setState({
|
|
920
|
+
activeId: TEST_IDS.SESSION_ID,
|
|
921
|
+
messagesMap: {
|
|
922
|
+
[messageMapKey(TEST_IDS.SESSION_ID, TEST_IDS.TOPIC_ID)]: [groupMessage],
|
|
923
|
+
},
|
|
924
|
+
internal_execAgentRuntime: vi.fn(),
|
|
925
|
+
internal_dispatchMessage: dispatchSpy,
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
await act(async () => {
|
|
930
|
+
await result.current.callToolFollowAssistantMessage({
|
|
931
|
+
parentId: TOOL_RESULT_MSG_ID,
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// Should find the correct group message even with multiple children
|
|
936
|
+
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
937
|
+
expect.objectContaining({
|
|
938
|
+
type: 'addGroupBlock',
|
|
939
|
+
groupMessageId: GROUP_MESSAGE_ID,
|
|
940
|
+
}),
|
|
941
|
+
);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it('should call internal_execAgentRuntime after creating assistant message', async () => {
|
|
945
|
+
const { result } = renderHook(() => useChatStore());
|
|
946
|
+
const mockExecAgentRuntime = vi.fn();
|
|
947
|
+
|
|
948
|
+
const groupMessage: UIChatMessage = {
|
|
949
|
+
id: GROUP_MESSAGE_ID,
|
|
950
|
+
role: 'group',
|
|
951
|
+
content: '',
|
|
952
|
+
sessionId: TEST_IDS.SESSION_ID,
|
|
953
|
+
children: [
|
|
954
|
+
{
|
|
955
|
+
id: ASSISTANT_BLOCK_ID,
|
|
956
|
+
content: 'Response',
|
|
957
|
+
tools: [
|
|
958
|
+
{
|
|
959
|
+
id: TOOL_CALL_ID,
|
|
960
|
+
type: 'builtin',
|
|
961
|
+
apiName: 'test',
|
|
962
|
+
identifier: 'test',
|
|
963
|
+
arguments: '{}',
|
|
964
|
+
result_msg_id: TOOL_RESULT_MSG_ID,
|
|
965
|
+
},
|
|
966
|
+
],
|
|
967
|
+
},
|
|
968
|
+
],
|
|
969
|
+
} as any;
|
|
970
|
+
|
|
971
|
+
act(() => {
|
|
972
|
+
useChatStore.setState({
|
|
973
|
+
activeId: TEST_IDS.SESSION_ID,
|
|
974
|
+
messagesMap: {
|
|
975
|
+
[messageMapKey(TEST_IDS.SESSION_ID, TEST_IDS.TOPIC_ID)]: [groupMessage],
|
|
976
|
+
},
|
|
977
|
+
internal_execAgentRuntime: mockExecAgentRuntime,
|
|
978
|
+
});
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
await act(async () => {
|
|
982
|
+
await result.current.callToolFollowAssistantMessage({
|
|
983
|
+
parentId: TOOL_RESULT_MSG_ID,
|
|
984
|
+
traceId: 'test-trace-id',
|
|
985
|
+
threadId: 'test-thread-id',
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
expect(mockExecAgentRuntime).toHaveBeenCalledWith(
|
|
990
|
+
expect.objectContaining({
|
|
991
|
+
assistantMessageId: 'new-assistant-block-id',
|
|
992
|
+
traceId: 'test-trace-id',
|
|
993
|
+
threadId: 'test-thread-id',
|
|
994
|
+
}),
|
|
995
|
+
);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
it('should handle missing result_msg_id field gracefully', async () => {
|
|
999
|
+
const { result } = renderHook(() => useChatStore());
|
|
1000
|
+
const dispatchSpy = vi.fn();
|
|
1001
|
+
|
|
1002
|
+
const groupMessage: UIChatMessage = {
|
|
1003
|
+
id: GROUP_MESSAGE_ID,
|
|
1004
|
+
role: 'group',
|
|
1005
|
+
content: '',
|
|
1006
|
+
sessionId: TEST_IDS.SESSION_ID,
|
|
1007
|
+
children: [
|
|
1008
|
+
{
|
|
1009
|
+
id: ASSISTANT_BLOCK_ID,
|
|
1010
|
+
content: 'Response',
|
|
1011
|
+
tools: [
|
|
1012
|
+
{
|
|
1013
|
+
id: TOOL_CALL_ID,
|
|
1014
|
+
type: 'builtin',
|
|
1015
|
+
apiName: 'test',
|
|
1016
|
+
identifier: 'test',
|
|
1017
|
+
arguments: '{}',
|
|
1018
|
+
// Missing result_msg_id
|
|
1019
|
+
},
|
|
1020
|
+
],
|
|
1021
|
+
},
|
|
1022
|
+
],
|
|
1023
|
+
} as any;
|
|
1024
|
+
|
|
1025
|
+
act(() => {
|
|
1026
|
+
useChatStore.setState({
|
|
1027
|
+
activeId: TEST_IDS.SESSION_ID,
|
|
1028
|
+
messagesMap: {
|
|
1029
|
+
[messageMapKey(TEST_IDS.SESSION_ID, TEST_IDS.TOPIC_ID)]: [groupMessage],
|
|
1030
|
+
},
|
|
1031
|
+
internal_execAgentRuntime: vi.fn(),
|
|
1032
|
+
internal_dispatchMessage: dispatchSpy,
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
await act(async () => {
|
|
1037
|
+
await result.current.callToolFollowAssistantMessage({
|
|
1038
|
+
parentId: TOOL_RESULT_MSG_ID,
|
|
1039
|
+
});
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// Should create as regular message since no groupMessageId found
|
|
1043
|
+
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
1044
|
+
expect.objectContaining({
|
|
1045
|
+
type: 'createMessage',
|
|
1046
|
+
}),
|
|
1047
|
+
);
|
|
1048
|
+
});
|
|
1049
|
+
});
|
|
743
1050
|
});
|