@lobehub/lobehub 2.0.0-next.35 → 2.0.0-next.36
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 +25 -0
- package/changelog/v1.json +9 -0
- package/next.config.ts +5 -6
- package/package.json +2 -2
- package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +112 -77
- package/packages/agent-runtime/src/core/runtime.ts +63 -18
- package/packages/agent-runtime/src/types/generalAgent.ts +55 -0
- package/packages/agent-runtime/src/types/index.ts +1 -0
- package/packages/agent-runtime/src/types/instruction.ts +10 -3
- package/packages/const/src/user.ts +0 -1
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +8 -6
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +12 -12
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +249 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +260 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +481 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +5 -1
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +407 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +18 -2
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/complex-scenario.json +25 -3
- package/packages/conversation-flow/src/__tests__/parse.test.ts +12 -0
- package/packages/conversation-flow/src/index.ts +1 -1
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +112 -34
- package/packages/conversation-flow/src/types/flatMessageList.ts +0 -12
- package/packages/conversation-flow/src/{types.ts → types/index.ts} +3 -14
- package/packages/database/src/models/message.ts +18 -19
- package/packages/types/src/aiChat.ts +2 -0
- package/packages/types/src/importer.ts +2 -2
- package/packages/types/src/message/ui/chat.ts +17 -1
- package/packages/types/src/message/ui/extra.ts +2 -2
- package/packages/types/src/message/ui/params.ts +2 -2
- package/packages/types/src/user/preference.ts +0 -4
- package/packages/utils/src/tokenizer/index.ts +3 -11
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
- package/src/app/[variants]/(main)/labs/page.tsx +0 -9
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
- package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
- package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
- package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
- package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
- package/src/features/Conversation/Error/index.tsx +0 -5
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
- package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
- package/src/features/Conversation/Messages/Default.tsx +1 -0
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
- package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
- package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
- package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
- package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
- package/src/features/Conversation/Messages/Group/index.tsx +2 -1
- package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
- package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
- package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
- package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
- package/src/features/Conversation/Messages/User/index.tsx +43 -44
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +3 -3
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
- package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
- package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
- package/src/hooks/useHotkeys/chatScope.ts +15 -7
- package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
- package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
- package/src/server/routers/lambda/aiChat.ts +3 -2
- package/src/server/routers/lambda/message.ts +8 -16
- package/src/server/services/message/__tests__/index.test.ts +29 -39
- package/src/server/services/message/index.ts +41 -36
- package/src/services/electron/desktopNotification.ts +6 -6
- package/src/services/electron/file.ts +6 -6
- package/src/services/file/ClientS3/index.ts +8 -8
- package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
- package/src/services/message/index.ts +21 -15
- package/src/services/upload.ts +11 -11
- package/src/services/utils/abortableRequest.test.ts +161 -0
- package/src/services/utils/abortableRequest.ts +67 -0
- package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
- package/src/store/chat/agents/createAgentExecutors.ts +395 -0
- package/src/store/chat/helpers.test.ts +0 -99
- package/src/store/chat/helpers.ts +0 -11
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
- package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
- package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
- package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
- package/src/store/chat/slices/message/action.test.ts +79 -68
- package/src/store/chat/slices/message/actions/index.ts +39 -0
- package/src/store/chat/slices/message/actions/internals.ts +77 -0
- package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
- package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
- package/src/store/chat/slices/message/actions/query.ts +120 -0
- package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
- package/src/store/chat/slices/message/initialState.ts +13 -0
- package/src/store/chat/slices/message/reducer.test.ts +48 -370
- package/src/store/chat/slices/message/reducer.ts +17 -81
- package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
- package/src/store/chat/slices/message/selectors/chat.ts +78 -242
- package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
- package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
- package/src/store/chat/slices/plugin/action.test.ts +62 -64
- package/src/store/chat/slices/plugin/action.ts +34 -28
- package/src/store/chat/slices/thread/action.test.ts +28 -31
- package/src/store/chat/slices/thread/action.ts +13 -10
- package/src/store/chat/slices/thread/selectors/index.ts +8 -6
- package/src/store/chat/slices/topic/reducer.ts +11 -3
- package/src/store/chat/store.ts +1 -1
- package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
- package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
- package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
- package/packages/database/src/utils/groupMessages.ts +0 -361
- package/packages/utils/src/tokenizer/client.ts +0 -35
- package/packages/utils/src/tokenizer/estimated.ts +0 -4
- package/packages/utils/src/tokenizer/server.ts +0 -11
- package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
- package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
- package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
- package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
- package/src/store/chat/slices/message/action.ts +0 -629
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { UIChatMessage } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_USER_AVATAR } from '@/const/meta';
|
|
4
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
|
5
|
+
import { useAgentStore } from '@/store/agent';
|
|
6
|
+
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
7
|
+
import { useSessionStore } from '@/store/session';
|
|
8
|
+
import { sessionMetaSelectors } from '@/store/session/selectors';
|
|
9
|
+
import { useUserStore } from '@/store/user';
|
|
10
|
+
import { userProfileSelectors } from '@/store/user/selectors';
|
|
11
|
+
|
|
12
|
+
import { chatHelpers } from '../../../helpers';
|
|
13
|
+
import type { ChatStoreState } from '../../../initialState';
|
|
14
|
+
import { messageMapKey } from '../../../utils/messageMapKey';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Display Message Selectors
|
|
18
|
+
*
|
|
19
|
+
* These selectors access processed messages from messagesMap (parsed display data).
|
|
20
|
+
* Use these selectors when you need to:
|
|
21
|
+
* - Render messages in UI components
|
|
22
|
+
* - Display assistantGroup messages with children
|
|
23
|
+
* - Show messages with proper meta information
|
|
24
|
+
* - Present message history with filters
|
|
25
|
+
*
|
|
26
|
+
* DO NOT use these for data mutations - use dbMessage.ts selectors instead.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// ============= Meta Information ========== //
|
|
30
|
+
|
|
31
|
+
const getMeta = (message: UIChatMessage) => {
|
|
32
|
+
switch (message.role) {
|
|
33
|
+
case 'user': {
|
|
34
|
+
return {
|
|
35
|
+
avatar: userProfileSelectors.userAvatar(useUserStore.getState()) || DEFAULT_USER_AVATAR,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case 'system': {
|
|
40
|
+
return message.meta;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
default: {
|
|
44
|
+
// For group chat, get meta from agent session
|
|
45
|
+
if (message.groupId && message.agentId) {
|
|
46
|
+
return sessionMetaSelectors.getAgentMetaByAgentId(message.agentId)(
|
|
47
|
+
useSessionStore.getState(),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Otherwise, use the current session's agent meta for single agent chat
|
|
52
|
+
return sessionMetaSelectors.currentAgentMeta(useSessionStore.getState());
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// ============= Basic Display Message Access ========== //
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the current chat key for accessing messagesMap
|
|
61
|
+
*/
|
|
62
|
+
export const currentDisplayChatKey = (s: ChatStoreState) =>
|
|
63
|
+
messageMapKey(s.activeId, s.activeTopicId);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get display messages by key (with meta information)
|
|
67
|
+
*/
|
|
68
|
+
const getDisplayMessagesByKey =
|
|
69
|
+
(key: string) =>
|
|
70
|
+
(s: ChatStoreState): UIChatMessage[] => {
|
|
71
|
+
const messages = s.messagesMap[key] || [];
|
|
72
|
+
return messages.map((i) => ({ ...i, meta: getMeta(i) }));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get current active session's display messages (includes assistantGroup messages)
|
|
77
|
+
*/
|
|
78
|
+
const activeDisplayMessages = (s: ChatStoreState): UIChatMessage[] => {
|
|
79
|
+
if (!s.activeId) return [];
|
|
80
|
+
return getDisplayMessagesByKey(currentDisplayChatKey(s))(s);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ============= Display Message Queries ========== //
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get display message by ID (searches in messagesMap including assistantGroup children)
|
|
87
|
+
*/
|
|
88
|
+
const getDisplayMessageById = (id: string) => (s: ChatStoreState) =>
|
|
89
|
+
chatHelpers.getMessageById(activeDisplayMessages(s), id);
|
|
90
|
+
|
|
91
|
+
const lastDisplayMessageId = (s: ChatStoreState) => {
|
|
92
|
+
const messages = activeDisplayMessages(s);
|
|
93
|
+
if (messages.length === 0) return undefined;
|
|
94
|
+
return messages.at(-1)?.id;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// ============= Thread Handling ========== //
|
|
98
|
+
|
|
99
|
+
const getChatsWithThread = (s: ChatStoreState, messages: UIChatMessage[]) => {
|
|
100
|
+
// 如果没有 activeThreadId,则返回所有的主消息
|
|
101
|
+
if (!s.activeThreadId) return messages.filter((m) => !m.threadId);
|
|
102
|
+
|
|
103
|
+
const thread = s.threadMaps[s.activeTopicId!]?.find((t) => t.id === s.activeThreadId);
|
|
104
|
+
|
|
105
|
+
if (!thread) return messages.filter((m) => !m.threadId);
|
|
106
|
+
|
|
107
|
+
const sourceIndex = messages.findIndex((m) => m.id === thread.sourceMessageId);
|
|
108
|
+
const sliced = messages.slice(0, sourceIndex + 1);
|
|
109
|
+
|
|
110
|
+
return [...sliced, ...messages.filter((m) => m.threadId === s.activeThreadId)];
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// ============= Main Display Chats ========== //
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Main display chats for UI rendering (without tool messages, with thread handling)
|
|
117
|
+
*/
|
|
118
|
+
const mainDisplayChats = (s: ChatStoreState): UIChatMessage[] => {
|
|
119
|
+
const displayChats = activeDisplayMessages(s);
|
|
120
|
+
return getChatsWithThread(s, displayChats);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Main display chat IDs
|
|
125
|
+
*/
|
|
126
|
+
const mainDisplayChatIDs = (s: ChatStoreState) => mainDisplayChats(s).map((s) => s.id);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Main AI chats (includes tool messages, with thread handling)
|
|
130
|
+
*/
|
|
131
|
+
const mainAIChats = (s: ChatStoreState): UIChatMessage[] => {
|
|
132
|
+
const messages = activeDisplayMessages(s);
|
|
133
|
+
return getChatsWithThread(s, messages);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Main AI chats with history configuration applied
|
|
138
|
+
*/
|
|
139
|
+
const mainAIChatsWithHistoryConfig = (s: ChatStoreState): UIChatMessage[] => {
|
|
140
|
+
const chats = mainAIChats(s);
|
|
141
|
+
const enableHistoryCount = agentChatConfigSelectors.enableHistoryCount(useAgentStore.getState());
|
|
142
|
+
const historyCount = agentChatConfigSelectors.historyCount(useAgentStore.getState());
|
|
143
|
+
|
|
144
|
+
return chatHelpers.getSlicedMessages(chats, {
|
|
145
|
+
enableHistoryCount,
|
|
146
|
+
historyCount,
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Concatenated message string from AI chats with history config
|
|
152
|
+
*/
|
|
153
|
+
const mainAIChatsMessageString = (s: ChatStoreState): string => {
|
|
154
|
+
const chats = mainAIChatsWithHistoryConfig(s);
|
|
155
|
+
return chats.map((m) => m.content).join('');
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Latest message reasoning content
|
|
160
|
+
*/
|
|
161
|
+
const mainAILatestMessageReasoningContent = (s: ChatStoreState) =>
|
|
162
|
+
mainAIChats(s).at(-1)?.reasoning?.content;
|
|
163
|
+
|
|
164
|
+
// ============= Display Message Status ========== //
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if current chat messages are loaded
|
|
168
|
+
*/
|
|
169
|
+
const currentChatLoadingState = (s: ChatStoreState) => !s.messagesInit;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if current chat is loaded in messagesMap
|
|
173
|
+
*/
|
|
174
|
+
const isCurrentDisplayChatLoaded = (s: ChatStoreState) => !!s.messagesMap[currentDisplayChatKey(s)];
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Show inbox welcome screen
|
|
178
|
+
*/
|
|
179
|
+
const showInboxWelcome = (s: ChatStoreState): boolean => {
|
|
180
|
+
const isInbox = s.activeId === INBOX_SESSION_ID;
|
|
181
|
+
if (!isInbox) return false;
|
|
182
|
+
|
|
183
|
+
const data = activeDisplayMessages(s);
|
|
184
|
+
return data.length === 0;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ============= Thread Messages ========== //
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Gets messages between the current user and a specific agent (thread messages)
|
|
191
|
+
* This is like a DM (Direct Message) view between user and agent
|
|
192
|
+
*/
|
|
193
|
+
const getThreadMessages =
|
|
194
|
+
(agentId: string) =>
|
|
195
|
+
(s: ChatStoreState): UIChatMessage[] => {
|
|
196
|
+
if (!agentId) return [];
|
|
197
|
+
|
|
198
|
+
const allMessages = activeDisplayMessages(s);
|
|
199
|
+
|
|
200
|
+
// Filter messages to only include:
|
|
201
|
+
// 1. User messages sent TO the specific agent (role: 'user' && targetId matches agentId)
|
|
202
|
+
// 2. Assistant messages FROM the specific agent sent TO user (role: 'assistant' && agentId matches && targetId is 'user')
|
|
203
|
+
return allMessages.filter((message) => {
|
|
204
|
+
if (message.role === 'user' && message.targetId === agentId) {
|
|
205
|
+
return true; // Include user messages sent to the specific agent
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (
|
|
209
|
+
message.role === 'assistant' &&
|
|
210
|
+
message.agentId === agentId &&
|
|
211
|
+
message.targetId === 'user'
|
|
212
|
+
) {
|
|
213
|
+
return true; // Include messages from the specific agent sent to user
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return false; // Exclude all other messages
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Gets thread message IDs for a specific agent
|
|
222
|
+
*/
|
|
223
|
+
const getThreadMessageIDs =
|
|
224
|
+
(agentId: string) =>
|
|
225
|
+
(s: ChatStoreState): string[] => {
|
|
226
|
+
return getThreadMessages(agentId)(s).map((message) => message.id);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// ============= Group Chat Selectors ========== //
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Gets the latest message block from a group message that doesn't contain tools
|
|
233
|
+
* Returns null if the last block contains tools or if message is not a group message
|
|
234
|
+
*/
|
|
235
|
+
const getGroupLatestMessageWithoutTools = (id: string) => (s: ChatStoreState) => {
|
|
236
|
+
const message = getDisplayMessageById(id)(s);
|
|
237
|
+
|
|
238
|
+
if (
|
|
239
|
+
!message ||
|
|
240
|
+
message.role !== 'assistantGroup' ||
|
|
241
|
+
!message.children ||
|
|
242
|
+
message.children.length === 0
|
|
243
|
+
)
|
|
244
|
+
return;
|
|
245
|
+
|
|
246
|
+
// Get the last child
|
|
247
|
+
const lastChild = message.children.at(-1);
|
|
248
|
+
|
|
249
|
+
if (!lastChild) return;
|
|
250
|
+
|
|
251
|
+
// Return the last child only if it doesn't have tools
|
|
252
|
+
if (!lastChild.tools || lastChild.tools.length === 0) {
|
|
253
|
+
return lastChild;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// ============= Supervisor Selectors ========== //
|
|
260
|
+
|
|
261
|
+
const isSupervisorLoading = (groupId: string) => (s: ChatStoreState) =>
|
|
262
|
+
s.supervisorDecisionLoading.includes(groupId);
|
|
263
|
+
|
|
264
|
+
const getSupervisorTodos = (groupId?: string, topicId?: string | null) => (s: ChatStoreState) => {
|
|
265
|
+
if (!groupId) return [];
|
|
266
|
+
return s.supervisorTodos[messageMapKey(groupId, topicId)] || [];
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// ============= Inbox Selectors ========== //
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get inbox active topic display messages
|
|
273
|
+
*/
|
|
274
|
+
const inboxActiveTopicDisplayMessages = (state: ChatStoreState) => {
|
|
275
|
+
const activeTopicId = state.activeTopicId;
|
|
276
|
+
const key = messageMapKey(INBOX_SESSION_ID, activeTopicId);
|
|
277
|
+
return state.messagesMap[key] || [];
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export const displayMessageSelectors = {
|
|
281
|
+
activeDisplayMessages,
|
|
282
|
+
currentChatLoadingState,
|
|
283
|
+
currentDisplayChatKey,
|
|
284
|
+
getDisplayMessageById,
|
|
285
|
+
getDisplayMessagesByKey,
|
|
286
|
+
getGroupLatestMessageWithoutTools,
|
|
287
|
+
getSupervisorTodos,
|
|
288
|
+
getThreadMessageIDs,
|
|
289
|
+
getThreadMessages,
|
|
290
|
+
inboxActiveTopicDisplayMessages,
|
|
291
|
+
isCurrentDisplayChatLoaded,
|
|
292
|
+
isSupervisorLoading,
|
|
293
|
+
lastDisplayMessageId,
|
|
294
|
+
mainAIChats,
|
|
295
|
+
mainAIChatsMessageString,
|
|
296
|
+
mainAIChatsWithHistoryConfig,
|
|
297
|
+
mainAILatestMessageReasoningContent,
|
|
298
|
+
mainDisplayChatIDs,
|
|
299
|
+
mainDisplayChats,
|
|
300
|
+
showInboxWelcome,
|
|
301
|
+
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ChatStoreState } from '../../../initialState';
|
|
2
|
-
import {
|
|
2
|
+
import { mainDisplayChatIDs } from './chat';
|
|
3
|
+
import { getDbMessageByToolCallId } from './dbMessage';
|
|
3
4
|
|
|
4
5
|
const isMessageEditing = (id: string) => (s: ChatStoreState) => s.messageEditingIds.includes(id);
|
|
5
6
|
const isMessageLoading = (id: string) => (s: ChatStoreState) => s.messageLoadingIds.includes(id);
|
|
7
|
+
const isMessageRegenerating = (id: string) => (s: ChatStoreState) => s.regeneratingIds.includes(id);
|
|
6
8
|
|
|
7
9
|
const isMessageGenerating = (id: string) => (s: ChatStoreState) => s.chatLoadingIds.includes(id);
|
|
8
10
|
const isMessageInRAGFlow = (id: string) => (s: ChatStoreState) =>
|
|
@@ -31,7 +33,7 @@ const isInToolsCalling = (id: string, index: number) => (s: ChatStoreState) => {
|
|
|
31
33
|
|
|
32
34
|
const isToolApiNameShining =
|
|
33
35
|
(messageId: string, index: number, toolCallId: string) => (s: ChatStoreState) => {
|
|
34
|
-
const toolMessageId =
|
|
36
|
+
const toolMessageId = getDbMessageByToolCallId(toolCallId)(s)?.id;
|
|
35
37
|
const isStreaming = isToolCallStreaming(messageId, index)(s);
|
|
36
38
|
const isPluginInvoking = !toolMessageId ? true : isPluginApiInvoking(toolMessageId)(s);
|
|
37
39
|
|
|
@@ -73,6 +75,7 @@ export const messageStateSelectors = {
|
|
|
73
75
|
isMessageInChatReasoning,
|
|
74
76
|
isMessageInRAGFlow,
|
|
75
77
|
isMessageLoading,
|
|
78
|
+
isMessageRegenerating,
|
|
76
79
|
isPluginApiInvoking,
|
|
77
80
|
isSendButtonDisabledByMessage,
|
|
78
81
|
isToolApiNameShining,
|
|
@@ -44,13 +44,13 @@ describe('ChatPluginAction', () => {
|
|
|
44
44
|
content: 'Tool content to summarize',
|
|
45
45
|
} as UIChatMessage;
|
|
46
46
|
|
|
47
|
-
const
|
|
47
|
+
const internal_execAgentRuntimeMock = vi.fn();
|
|
48
48
|
|
|
49
49
|
act(() => {
|
|
50
50
|
useChatStore.setState({
|
|
51
51
|
activeId: 'session-id',
|
|
52
52
|
messagesMap: { [messageMapKey('session-id')]: [toolMessage] },
|
|
53
|
-
|
|
53
|
+
internal_execAgentRuntime: internal_execAgentRuntimeMock,
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -60,27 +60,30 @@ describe('ChatPluginAction', () => {
|
|
|
60
60
|
await result.current.summaryPluginContent(messageId);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
expect(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{
|
|
70
|
-
...toolMessage,
|
|
71
|
-
meta: {
|
|
72
|
-
avatar: DEFAULT_INBOX_AVATAR,
|
|
73
|
-
backgroundColor: 'rgba(0,0,0,0)',
|
|
74
|
-
description: undefined,
|
|
75
|
-
title: undefined,
|
|
63
|
+
expect(internal_execAgentRuntimeMock).toHaveBeenCalledWith(
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
messages: [
|
|
66
|
+
{
|
|
67
|
+
role: 'assistant',
|
|
68
|
+
content: '作为一名总结专家,请结合以上系统提示词,将以下内容进行总结:',
|
|
76
69
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
{
|
|
71
|
+
...toolMessage,
|
|
72
|
+
meta: {
|
|
73
|
+
avatar: DEFAULT_INBOX_AVATAR,
|
|
74
|
+
backgroundColor: 'rgba(0,0,0,0)',
|
|
75
|
+
description: undefined,
|
|
76
|
+
title: undefined,
|
|
77
|
+
},
|
|
78
|
+
content: toolMessage.content,
|
|
79
|
+
role: 'assistant',
|
|
80
|
+
name: undefined,
|
|
81
|
+
tool_call_id: undefined,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
parentMessageId: messageId,
|
|
85
|
+
parentMessageType: 'assistant',
|
|
86
|
+
}),
|
|
84
87
|
);
|
|
85
88
|
});
|
|
86
89
|
|
|
@@ -92,13 +95,13 @@ describe('ChatPluginAction', () => {
|
|
|
92
95
|
content: 'User message',
|
|
93
96
|
} as UIChatMessage;
|
|
94
97
|
|
|
95
|
-
const
|
|
98
|
+
const internal_execAgentRuntimeMock = vi.fn();
|
|
96
99
|
|
|
97
100
|
act(() => {
|
|
98
101
|
useChatStore.setState({
|
|
99
102
|
activeId: 'session-id',
|
|
100
103
|
messagesMap: { [messageMapKey('session-id')]: [nonToolMessage] },
|
|
101
|
-
|
|
104
|
+
internal_execAgentRuntime: internal_execAgentRuntimeMock,
|
|
102
105
|
});
|
|
103
106
|
});
|
|
104
107
|
|
|
@@ -108,7 +111,7 @@ describe('ChatPluginAction', () => {
|
|
|
108
111
|
await result.current.summaryPluginContent(messageId);
|
|
109
112
|
});
|
|
110
113
|
|
|
111
|
-
expect(
|
|
114
|
+
expect(internal_execAgentRuntimeMock).not.toHaveBeenCalled();
|
|
112
115
|
});
|
|
113
116
|
});
|
|
114
117
|
|
|
@@ -156,8 +159,9 @@ describe('ChatPluginAction', () => {
|
|
|
156
159
|
// 设置初始状态
|
|
157
160
|
const initialState = {
|
|
158
161
|
messages: [],
|
|
159
|
-
|
|
162
|
+
internal_execAgentRuntime: vi.fn(),
|
|
160
163
|
refreshMessages: vi.fn(),
|
|
164
|
+
optimisticUpdateMessageContent: vi.fn(),
|
|
161
165
|
};
|
|
162
166
|
useChatStore.setState(initialState);
|
|
163
167
|
|
|
@@ -170,21 +174,19 @@ describe('ChatPluginAction', () => {
|
|
|
170
174
|
await result.current.fillPluginMessageContent(messageId, newContent, true);
|
|
171
175
|
});
|
|
172
176
|
|
|
173
|
-
// 验证
|
|
174
|
-
expect(
|
|
177
|
+
// 验证 optimisticUpdateMessageContent 是否被正确调用
|
|
178
|
+
expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
|
|
175
179
|
messageId,
|
|
176
|
-
|
|
177
|
-
{ sessionId: 'inbox', topicId: null },
|
|
180
|
+
newContent,
|
|
178
181
|
);
|
|
179
182
|
|
|
180
|
-
// 验证 refreshMessages 是否被调用
|
|
181
|
-
expect(result.current.refreshMessages).toHaveBeenCalled();
|
|
182
|
-
|
|
183
183
|
// 验证 coreProcessMessage 是否被正确调用
|
|
184
|
-
expect(result.current.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
expect(result.current.internal_execAgentRuntime).toHaveBeenCalledWith(
|
|
185
|
+
expect.objectContaining({
|
|
186
|
+
messages: mockCurrentChats,
|
|
187
|
+
parentMessageId: messageId,
|
|
188
|
+
parentMessageType: 'user',
|
|
189
|
+
}),
|
|
188
190
|
);
|
|
189
191
|
});
|
|
190
192
|
it('should update message content and not trigger ai message', async () => {
|
|
@@ -196,8 +198,9 @@ describe('ChatPluginAction', () => {
|
|
|
196
198
|
const initialState = {
|
|
197
199
|
messages: [],
|
|
198
200
|
coreProcessMessage: vi.fn(),
|
|
199
|
-
|
|
201
|
+
internal_execAgentRuntime: vi.fn(),
|
|
200
202
|
refreshMessages: vi.fn(),
|
|
203
|
+
optimisticUpdateMessageContent: vi.fn(),
|
|
201
204
|
};
|
|
202
205
|
useChatStore.setState(initialState);
|
|
203
206
|
|
|
@@ -210,18 +213,14 @@ describe('ChatPluginAction', () => {
|
|
|
210
213
|
await result.current.fillPluginMessageContent(messageId, newContent);
|
|
211
214
|
});
|
|
212
215
|
|
|
213
|
-
// 验证
|
|
214
|
-
expect(
|
|
216
|
+
// 验证 optimisticUpdateMessageContent 是否被正确调用
|
|
217
|
+
expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
|
|
215
218
|
messageId,
|
|
216
|
-
|
|
217
|
-
{ sessionId: 'inbox', topicId: null },
|
|
219
|
+
newContent,
|
|
218
220
|
);
|
|
219
221
|
|
|
220
|
-
// 验证 refreshMessages 是否被调用
|
|
221
|
-
expect(result.current.refreshMessages).toHaveBeenCalled();
|
|
222
|
-
|
|
223
222
|
// 验证 coreProcessMessage 没有被正确调用
|
|
224
|
-
expect(result.current.
|
|
223
|
+
expect(result.current.internal_execAgentRuntime).not.toHaveBeenCalled();
|
|
225
224
|
});
|
|
226
225
|
});
|
|
227
226
|
|
|
@@ -236,6 +235,7 @@ describe('ChatPluginAction', () => {
|
|
|
236
235
|
vi.spyOn(storeState, 'refreshMessages');
|
|
237
236
|
vi.spyOn(storeState, 'triggerAIMessage').mockResolvedValue(undefined);
|
|
238
237
|
vi.spyOn(storeState, 'internal_togglePluginApiCalling').mockReturnValue(undefined);
|
|
238
|
+
vi.spyOn(storeState, 'optimisticUpdateMessageContent').mockResolvedValue();
|
|
239
239
|
|
|
240
240
|
const runSpy = vi.spyOn(chatService, 'runPluginApi').mockResolvedValue({
|
|
241
241
|
text: pluginApiResponse,
|
|
@@ -254,12 +254,10 @@ describe('ChatPluginAction', () => {
|
|
|
254
254
|
expect.any(String),
|
|
255
255
|
);
|
|
256
256
|
expect(runSpy).toHaveBeenCalledWith(pluginPayload, { signal: undefined, trace: {} });
|
|
257
|
-
expect(
|
|
257
|
+
expect(storeState.optimisticUpdateMessageContent).toHaveBeenCalledWith(
|
|
258
258
|
messageId,
|
|
259
|
-
|
|
260
|
-
{ sessionId: 'inbox', topicId: null },
|
|
259
|
+
pluginApiResponse,
|
|
261
260
|
);
|
|
262
|
-
expect(storeState.refreshMessages).toHaveBeenCalled();
|
|
263
261
|
expect(storeState.internal_togglePluginApiCalling).toHaveBeenCalledWith(
|
|
264
262
|
false,
|
|
265
263
|
'message-id',
|
|
@@ -352,7 +350,7 @@ describe('ChatPluginAction', () => {
|
|
|
352
350
|
const invokeBuiltinToolMock = vi.fn();
|
|
353
351
|
const invokeDefaultTypePluginMock = vi.fn().mockResolvedValue('Default tool response');
|
|
354
352
|
const triggerAIMessageMock = vi.fn();
|
|
355
|
-
const
|
|
353
|
+
const optimisticCreateMessageMock = vi
|
|
356
354
|
.fn()
|
|
357
355
|
.mockResolvedValue({ id: 'tool-message-id', messages: [] });
|
|
358
356
|
const getTraceIdByMessageIdMock = vi.fn().mockReturnValue('trace-id');
|
|
@@ -367,7 +365,7 @@ describe('ChatPluginAction', () => {
|
|
|
367
365
|
invokeBuiltinTool: invokeBuiltinToolMock,
|
|
368
366
|
invokeDefaultTypePlugin: invokeDefaultTypePluginMock,
|
|
369
367
|
triggerAIMessage: triggerAIMessageMock,
|
|
370
|
-
|
|
368
|
+
optimisticCreateMessage: optimisticCreateMessageMock,
|
|
371
369
|
activeId: 'session-id',
|
|
372
370
|
activeTopicId: 'topic-id',
|
|
373
371
|
});
|
|
@@ -380,8 +378,8 @@ describe('ChatPluginAction', () => {
|
|
|
380
378
|
});
|
|
381
379
|
|
|
382
380
|
// Verify that tool messages were created for each tool call
|
|
383
|
-
expect(
|
|
384
|
-
expect(
|
|
381
|
+
expect(optimisticCreateMessageMock).toHaveBeenCalledTimes(4);
|
|
382
|
+
expect(optimisticCreateMessageMock).toHaveBeenNthCalledWith(1, {
|
|
385
383
|
content: '',
|
|
386
384
|
parentId: assistantId,
|
|
387
385
|
plugin: message.tools![0],
|
|
@@ -392,7 +390,7 @@ describe('ChatPluginAction', () => {
|
|
|
392
390
|
threadId: undefined,
|
|
393
391
|
groupId: undefined,
|
|
394
392
|
});
|
|
395
|
-
expect(
|
|
393
|
+
expect(optimisticCreateMessageMock).toHaveBeenNthCalledWith(2, {
|
|
396
394
|
content: '',
|
|
397
395
|
parentId: assistantId,
|
|
398
396
|
plugin: message.tools![1],
|
|
@@ -403,7 +401,7 @@ describe('ChatPluginAction', () => {
|
|
|
403
401
|
threadId: undefined,
|
|
404
402
|
groupId: undefined,
|
|
405
403
|
});
|
|
406
|
-
expect(
|
|
404
|
+
expect(optimisticCreateMessageMock).toHaveBeenNthCalledWith(3, {
|
|
407
405
|
content: '',
|
|
408
406
|
parentId: assistantId,
|
|
409
407
|
plugin: message.tools![2],
|
|
@@ -414,7 +412,7 @@ describe('ChatPluginAction', () => {
|
|
|
414
412
|
threadId: undefined,
|
|
415
413
|
groupId: undefined,
|
|
416
414
|
});
|
|
417
|
-
expect(
|
|
415
|
+
expect(optimisticCreateMessageMock).toHaveBeenNthCalledWith(4, {
|
|
418
416
|
content: '',
|
|
419
417
|
parentId: assistantId,
|
|
420
418
|
plugin: message.tools![3],
|
|
@@ -481,7 +479,7 @@ describe('ChatPluginAction', () => {
|
|
|
481
479
|
const invokeMarkdownTypePluginMock = vi.fn();
|
|
482
480
|
const invokeBuiltinToolMock = vi.fn();
|
|
483
481
|
const triggerAIMessageMock = vi.fn();
|
|
484
|
-
const
|
|
482
|
+
const optimisticCreateMessageMock = vi
|
|
485
483
|
.fn()
|
|
486
484
|
.mockResolvedValue({ id: 'tool-message-id', messages: [] });
|
|
487
485
|
|
|
@@ -491,7 +489,7 @@ describe('ChatPluginAction', () => {
|
|
|
491
489
|
invokeMarkdownTypePlugin: invokeMarkdownTypePluginMock,
|
|
492
490
|
invokeBuiltinTool: invokeBuiltinToolMock,
|
|
493
491
|
triggerAIMessage: triggerAIMessageMock,
|
|
494
|
-
|
|
492
|
+
optimisticCreateMessage: optimisticCreateMessageMock,
|
|
495
493
|
activeId: 'session-id',
|
|
496
494
|
messagesMap: {
|
|
497
495
|
[messageMapKey('session-id', 'topic-id')]: [message],
|
|
@@ -507,7 +505,7 @@ describe('ChatPluginAction', () => {
|
|
|
507
505
|
});
|
|
508
506
|
|
|
509
507
|
// Verify that tool messages were created for each tool call
|
|
510
|
-
expect(
|
|
508
|
+
expect(optimisticCreateMessageMock).toHaveBeenCalledTimes(3);
|
|
511
509
|
|
|
512
510
|
// Verify that the appropriate plugin types were invoked
|
|
513
511
|
expect(invokeStandaloneTypePluginMock).toHaveBeenCalledWith(
|
|
@@ -801,7 +799,7 @@ describe('ChatPluginAction', () => {
|
|
|
801
799
|
activeId: 'session-id',
|
|
802
800
|
messagesMap: { [messageMapKey('session-id')]: [message] },
|
|
803
801
|
internal_invokeDifferentTypePlugin: internal_invokeDifferentTypePluginMock,
|
|
804
|
-
|
|
802
|
+
optimisticUpdateMessagePluginError: vi.fn(),
|
|
805
803
|
});
|
|
806
804
|
});
|
|
807
805
|
|
|
@@ -840,7 +838,7 @@ describe('ChatPluginAction', () => {
|
|
|
840
838
|
activeId: 'session-id',
|
|
841
839
|
messagesMap: { [messageMapKey('session-id')]: [message] },
|
|
842
840
|
internal_invokeDifferentTypePlugin: vi.fn(),
|
|
843
|
-
|
|
841
|
+
optimisticUpdateMessagePluginError: internal_updateMessageErrorMock,
|
|
844
842
|
});
|
|
845
843
|
});
|
|
846
844
|
|
|
@@ -925,7 +923,7 @@ describe('ChatPluginAction', () => {
|
|
|
925
923
|
act(() => {
|
|
926
924
|
useChatStore.setState({
|
|
927
925
|
internal_togglePluginApiCalling: vi.fn(),
|
|
928
|
-
|
|
926
|
+
optimisticUpdateMessageContent: vi.fn(),
|
|
929
927
|
refreshMessages: vi.fn(),
|
|
930
928
|
});
|
|
931
929
|
});
|
|
@@ -937,7 +935,7 @@ describe('ChatPluginAction', () => {
|
|
|
937
935
|
});
|
|
938
936
|
|
|
939
937
|
expect(chatService.runPluginApi).toHaveBeenCalledWith(payload, expect.any(Object));
|
|
940
|
-
expect(result.current.
|
|
938
|
+
expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
|
|
941
939
|
messageId,
|
|
942
940
|
apiResponse,
|
|
943
941
|
);
|