@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.
Files changed (154) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/next.config.ts +5 -6
  4. package/package.json +2 -2
  5. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +112 -77
  6. package/packages/agent-runtime/src/core/runtime.ts +63 -18
  7. package/packages/agent-runtime/src/types/generalAgent.ts +55 -0
  8. package/packages/agent-runtime/src/types/index.ts +1 -0
  9. package/packages/agent-runtime/src/types/instruction.ts +10 -3
  10. package/packages/const/src/user.ts +0 -1
  11. package/packages/context-engine/src/processors/GroupMessageFlatten.ts +8 -6
  12. package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +12 -12
  13. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +249 -0
  14. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/index.ts +4 -0
  15. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +260 -0
  16. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -0
  17. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +481 -0
  18. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +5 -1
  19. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/index.ts +4 -0
  20. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +407 -0
  21. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +18 -2
  22. package/packages/conversation-flow/src/__tests__/fixtures/outputs/complex-scenario.json +25 -3
  23. package/packages/conversation-flow/src/__tests__/parse.test.ts +12 -0
  24. package/packages/conversation-flow/src/index.ts +1 -1
  25. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +112 -34
  26. package/packages/conversation-flow/src/types/flatMessageList.ts +0 -12
  27. package/packages/conversation-flow/src/{types.ts → types/index.ts} +3 -14
  28. package/packages/database/src/models/message.ts +18 -19
  29. package/packages/types/src/aiChat.ts +2 -0
  30. package/packages/types/src/importer.ts +2 -2
  31. package/packages/types/src/message/ui/chat.ts +17 -1
  32. package/packages/types/src/message/ui/extra.ts +2 -2
  33. package/packages/types/src/message/ui/params.ts +2 -2
  34. package/packages/types/src/user/preference.ts +0 -4
  35. package/packages/utils/src/tokenizer/index.ts +3 -11
  36. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
  37. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
  40. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
  41. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
  42. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
  43. package/src/app/[variants]/(main)/labs/page.tsx +0 -9
  44. package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
  45. package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
  46. package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
  47. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
  48. package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
  49. package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
  50. package/src/features/Conversation/Error/index.tsx +0 -5
  51. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
  52. package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
  53. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
  54. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
  55. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
  56. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
  57. package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
  58. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
  59. package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
  60. package/src/features/Conversation/Messages/Default.tsx +1 -0
  61. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
  62. package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
  63. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
  64. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
  65. package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
  66. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
  67. package/src/features/Conversation/Messages/Group/index.tsx +2 -1
  68. package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
  69. package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
  70. package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
  71. package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
  72. package/src/features/Conversation/Messages/User/index.tsx +43 -44
  73. package/src/features/Conversation/Messages/index.tsx +3 -3
  74. package/src/features/Conversation/components/AutoScroll.tsx +3 -3
  75. package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
  76. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
  77. package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
  78. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
  79. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
  80. package/src/hooks/useHotkeys/chatScope.ts +15 -7
  81. package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
  82. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
  83. package/src/server/routers/lambda/aiChat.ts +3 -2
  84. package/src/server/routers/lambda/message.ts +8 -16
  85. package/src/server/services/message/__tests__/index.test.ts +29 -39
  86. package/src/server/services/message/index.ts +41 -36
  87. package/src/services/electron/desktopNotification.ts +6 -6
  88. package/src/services/electron/file.ts +6 -6
  89. package/src/services/file/ClientS3/index.ts +8 -8
  90. package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
  91. package/src/services/message/index.ts +21 -15
  92. package/src/services/upload.ts +11 -11
  93. package/src/services/utils/abortableRequest.test.ts +161 -0
  94. package/src/services/utils/abortableRequest.ts +67 -0
  95. package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
  96. package/src/store/chat/agents/createAgentExecutors.ts +395 -0
  97. package/src/store/chat/helpers.test.ts +0 -99
  98. package/src/store/chat/helpers.ts +0 -11
  99. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
  100. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
  101. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
  102. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
  103. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
  104. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
  105. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
  106. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
  107. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
  108. package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
  109. package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
  110. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
  111. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
  112. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
  113. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
  114. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
  115. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  116. package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
  117. package/src/store/chat/slices/message/action.test.ts +79 -68
  118. package/src/store/chat/slices/message/actions/index.ts +39 -0
  119. package/src/store/chat/slices/message/actions/internals.ts +77 -0
  120. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
  121. package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
  122. package/src/store/chat/slices/message/actions/query.ts +120 -0
  123. package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
  124. package/src/store/chat/slices/message/initialState.ts +13 -0
  125. package/src/store/chat/slices/message/reducer.test.ts +48 -370
  126. package/src/store/chat/slices/message/reducer.ts +17 -81
  127. package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
  128. package/src/store/chat/slices/message/selectors/chat.ts +78 -242
  129. package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
  130. package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
  131. package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
  132. package/src/store/chat/slices/plugin/action.test.ts +62 -64
  133. package/src/store/chat/slices/plugin/action.ts +34 -28
  134. package/src/store/chat/slices/thread/action.test.ts +28 -31
  135. package/src/store/chat/slices/thread/action.ts +13 -10
  136. package/src/store/chat/slices/thread/selectors/index.ts +8 -6
  137. package/src/store/chat/slices/topic/reducer.ts +11 -3
  138. package/src/store/chat/store.ts +1 -1
  139. package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
  140. package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
  141. package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
  142. package/packages/database/src/utils/groupMessages.ts +0 -361
  143. package/packages/utils/src/tokenizer/client.ts +0 -35
  144. package/packages/utils/src/tokenizer/estimated.ts +0 -4
  145. package/packages/utils/src/tokenizer/server.ts +0 -11
  146. package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
  147. package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
  148. package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
  149. package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
  150. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
  151. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
  152. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
  153. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
  154. 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 { getMessageByToolCallId, mainDisplayChatIDs } from './chat';
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 = getMessageByToolCallId(toolCallId)(s)?.id;
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 internal_coreProcessMessageMock = vi.fn();
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
- internal_coreProcessMessage: internal_coreProcessMessageMock,
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(internal_coreProcessMessageMock).toHaveBeenCalledWith(
64
- [
65
- {
66
- role: 'assistant',
67
- content: '作为一名总结专家,请结合以上系统提示词,将以下内容进行总结:',
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
- content: toolMessage.content,
78
- role: 'assistant',
79
- name: undefined,
80
- tool_call_id: undefined,
81
- },
82
- ],
83
- messageId,
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 internal_coreProcessMessageMock = vi.fn();
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
- internal_coreProcessMessage: internal_coreProcessMessageMock,
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(internal_coreProcessMessageMock).not.toHaveBeenCalled();
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
- internal_coreProcessMessage: vi.fn(),
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
- // 验证 messageService.internal_updateMessageContent 是否被正确调用
174
- expect(messageService.updateMessage).toHaveBeenCalledWith(
177
+ // 验证 optimisticUpdateMessageContent 是否被正确调用
178
+ expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
175
179
  messageId,
176
- { content: newContent },
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.internal_coreProcessMessage).toHaveBeenCalledWith(
185
- mockCurrentChats,
186
- messageId,
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
- internal_coreProcessMessage: vi.fn(),
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
- // 验证 messageService.internal_updateMessageContent 是否被正确调用
214
- expect(messageService.updateMessage).toHaveBeenCalledWith(
216
+ // 验证 optimisticUpdateMessageContent 是否被正确调用
217
+ expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
215
218
  messageId,
216
- { content: newContent },
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.internal_coreProcessMessage).not.toHaveBeenCalled();
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(messageService.updateMessage).toHaveBeenCalledWith(
257
+ expect(storeState.optimisticUpdateMessageContent).toHaveBeenCalledWith(
258
258
  messageId,
259
- { content: pluginApiResponse },
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 internal_createMessageMock = vi
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
- internal_createMessage: internal_createMessageMock,
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(internal_createMessageMock).toHaveBeenCalledTimes(4);
384
- expect(internal_createMessageMock).toHaveBeenNthCalledWith(1, {
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(internal_createMessageMock).toHaveBeenNthCalledWith(2, {
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(internal_createMessageMock).toHaveBeenNthCalledWith(3, {
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(internal_createMessageMock).toHaveBeenNthCalledWith(4, {
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 internal_createMessageMock = vi
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
- internal_createMessage: internal_createMessageMock,
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(internal_createMessageMock).toHaveBeenCalledTimes(3);
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
- internal_updateMessageError: vi.fn(),
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
- internal_updateMessagePluginError: internal_updateMessageErrorMock,
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
- internal_updateMessageContent: vi.fn(),
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.internal_updateMessageContent).toHaveBeenCalledWith(
938
+ expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
941
939
  messageId,
942
940
  apiResponse,
943
941
  );