@lobehub/lobehub 2.0.0-next.35 → 2.0.0-next.37

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 (156) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -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/__tests__/apiKey.test.ts +444 -0
  29. package/packages/database/src/models/message.ts +18 -19
  30. package/packages/types/src/aiChat.ts +2 -0
  31. package/packages/types/src/importer.ts +2 -2
  32. package/packages/types/src/message/ui/chat.ts +17 -1
  33. package/packages/types/src/message/ui/extra.ts +2 -2
  34. package/packages/types/src/message/ui/params.ts +2 -2
  35. package/packages/types/src/user/preference.ts +0 -4
  36. package/packages/utils/src/tokenizer/index.ts +3 -11
  37. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
  40. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
  41. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
  42. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
  43. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
  44. package/src/app/[variants]/(main)/labs/page.tsx +0 -9
  45. package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
  46. package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
  47. package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
  48. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
  49. package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
  50. package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
  51. package/src/features/Conversation/Error/index.tsx +0 -5
  52. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
  53. package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
  54. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
  55. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
  56. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
  57. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
  58. package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
  59. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
  60. package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
  61. package/src/features/Conversation/Messages/Default.tsx +1 -0
  62. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
  63. package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
  64. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
  65. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
  66. package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
  67. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
  68. package/src/features/Conversation/Messages/Group/index.tsx +2 -1
  69. package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
  70. package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
  71. package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
  72. package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
  73. package/src/features/Conversation/Messages/User/index.tsx +43 -44
  74. package/src/features/Conversation/Messages/index.tsx +3 -3
  75. package/src/features/Conversation/components/AutoScroll.tsx +3 -3
  76. package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
  77. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
  78. package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
  79. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
  80. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
  81. package/src/hooks/useHotkeys/chatScope.ts +15 -7
  82. package/src/libs/trpc/client/lambda.ts +4 -3
  83. package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
  84. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
  85. package/src/server/routers/lambda/aiChat.ts +3 -2
  86. package/src/server/routers/lambda/message.ts +8 -16
  87. package/src/server/services/message/__tests__/index.test.ts +29 -39
  88. package/src/server/services/message/index.ts +41 -36
  89. package/src/services/electron/desktopNotification.ts +6 -6
  90. package/src/services/electron/file.ts +6 -6
  91. package/src/services/file/ClientS3/index.ts +8 -8
  92. package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
  93. package/src/services/message/index.ts +21 -15
  94. package/src/services/upload.ts +11 -11
  95. package/src/services/utils/abortableRequest.test.ts +161 -0
  96. package/src/services/utils/abortableRequest.ts +67 -0
  97. package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
  98. package/src/store/chat/agents/createAgentExecutors.ts +395 -0
  99. package/src/store/chat/helpers.test.ts +0 -99
  100. package/src/store/chat/helpers.ts +0 -11
  101. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
  102. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
  103. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
  104. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
  105. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
  106. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
  107. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
  108. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
  109. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
  110. package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
  111. package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
  112. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
  113. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
  114. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
  115. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
  116. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
  117. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  118. package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
  119. package/src/store/chat/slices/message/action.test.ts +79 -68
  120. package/src/store/chat/slices/message/actions/index.ts +39 -0
  121. package/src/store/chat/slices/message/actions/internals.ts +77 -0
  122. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
  123. package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
  124. package/src/store/chat/slices/message/actions/query.ts +120 -0
  125. package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
  126. package/src/store/chat/slices/message/initialState.ts +13 -0
  127. package/src/store/chat/slices/message/reducer.test.ts +48 -370
  128. package/src/store/chat/slices/message/reducer.ts +17 -81
  129. package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
  130. package/src/store/chat/slices/message/selectors/chat.ts +78 -242
  131. package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
  132. package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
  133. package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
  134. package/src/store/chat/slices/plugin/action.test.ts +62 -64
  135. package/src/store/chat/slices/plugin/action.ts +34 -28
  136. package/src/store/chat/slices/thread/action.test.ts +28 -31
  137. package/src/store/chat/slices/thread/action.ts +13 -10
  138. package/src/store/chat/slices/thread/selectors/index.ts +8 -6
  139. package/src/store/chat/slices/topic/reducer.ts +11 -3
  140. package/src/store/chat/store.ts +1 -1
  141. package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
  142. package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
  143. package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
  144. package/packages/database/src/utils/groupMessages.ts +0 -361
  145. package/packages/utils/src/tokenizer/client.ts +0 -35
  146. package/packages/utils/src/tokenizer/estimated.ts +0 -4
  147. package/packages/utils/src/tokenizer/server.ts +0 -11
  148. package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
  149. package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
  150. package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
  151. package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
  152. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
  153. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
  154. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
  155. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
  156. package/src/store/chat/slices/message/action.ts +0 -629
@@ -40,7 +40,6 @@ import {
40
40
  } from '../schemas';
41
41
  import { LobeChatDatabase } from '../type';
42
42
  import { genEndDateWhere, genRangeWhere, genStartDateWhere, genWhere } from '../utils/genWhere';
43
- import { groupAssistantMessages } from '../utils/groupMessages';
44
43
  import { idGenerator } from '../utils/idGenerator';
45
44
 
46
45
  export class MessageModel {
@@ -227,8 +226,8 @@ export class MessageModel {
227
226
  })),
228
227
 
229
228
  extra: {
230
- fromModel: model,
231
- fromProvider: provider,
229
+ model: model,
230
+ provider: provider,
232
231
  translate,
233
232
  tts: ttsId
234
233
  ? {
@@ -269,9 +268,7 @@ export class MessageModel {
269
268
  },
270
269
  );
271
270
 
272
- // Group assistant messages with their tool results
273
- const { groupAssistantMessages: useGroup = false } = options;
274
- return useGroup ? groupAssistantMessages(mappedMessages) : mappedMessages;
271
+ return mappedMessages;
275
272
  };
276
273
 
277
274
  findById = async (id: string) => {
@@ -464,8 +461,8 @@ export class MessageModel {
464
461
 
465
462
  create = async (
466
463
  {
467
- fromModel,
468
- fromProvider,
464
+ model: fromModel,
465
+ provider: fromProvider,
469
466
  files,
470
467
  plugin,
471
468
  pluginState,
@@ -771,17 +768,19 @@ export class MessageModel {
771
768
  sessionId?: string | null,
772
769
  topicId?: string | null,
773
770
  groupId?: string | null,
774
- ) =>
775
- this.db
776
- .delete(messages)
777
- .where(
778
- and(
779
- eq(messages.userId, this.userId),
780
- this.matchSession(sessionId),
781
- this.matchTopic(topicId),
782
- this.matchGroup(groupId),
783
- ),
784
- );
771
+ ) => {
772
+ const conditions = [eq(messages.userId, this.userId), this.matchSession(sessionId)];
773
+
774
+ // For deletion: only filter by topicId/groupId if explicitly provided
775
+ if (topicId !== undefined && topicId !== null) {
776
+ conditions.push(eq(messages.topicId, topicId));
777
+ }
778
+ if (groupId !== undefined && groupId !== null) {
779
+ conditions.push(eq(messages.groupId, groupId));
780
+ }
781
+
782
+ return this.db.delete(messages).where(and(...conditions));
783
+ };
785
784
 
786
785
  deleteAllMessages = async () => {
787
786
  return this.db.delete(messages).where(eq(messages.userId, this.userId));
@@ -9,6 +9,7 @@ export interface SendNewMessage {
9
9
  content: string;
10
10
  // if message has attached with files, then add files to message and the agent
11
11
  files?: string[];
12
+ parentId?: string;
12
13
  }
13
14
 
14
15
  export interface SendMessageServerParams {
@@ -41,6 +42,7 @@ export const AiSendMessageServerSchema = z.object({
41
42
  newUserMessage: z.object({
42
43
  content: z.string(),
43
44
  files: z.array(z.string()).optional(),
45
+ parentId: z.string().optional(),
44
46
  }),
45
47
  sessionId: z.string().optional(),
46
48
  threadId: z.string().optional(),
@@ -28,8 +28,8 @@ export interface ImportMessage {
28
28
 
29
29
  // 扩展字段
30
30
  extra?: {
31
- fromModel?: string;
32
- fromProvider?: string;
31
+ model?: string;
32
+ provider?: string;
33
33
  // 翻译
34
34
  translate?: ChatTranslate | false | null;
35
35
  // TTS
@@ -13,7 +13,13 @@ import { ChatMessageExtra } from './extra';
13
13
  import { ChatFileChunk } from './rag';
14
14
  import { ChatVideoItem } from './video';
15
15
 
16
- export type UIMessageRoleType = 'user' | 'system' | 'assistant' | 'tool' | 'supervisor' | 'group';
16
+ export type UIMessageRoleType =
17
+ | 'user'
18
+ | 'system'
19
+ | 'assistant'
20
+ | 'tool'
21
+ | 'supervisor'
22
+ | 'assistantGroup';
17
23
 
18
24
  export interface ChatFileItem {
19
25
  content?: string;
@@ -34,10 +40,20 @@ export interface AssistantContentBlock {
34
40
  tools?: ChatToolPayloadWithResult[];
35
41
  usage?: ModelUsage;
36
42
  }
43
+ interface UIMessageBranch {
44
+ /** Index of the active branch (0-based) */
45
+ activeBranchIndex: number;
46
+ /** Total number of branches */
47
+ count: number;
48
+ }
37
49
 
38
50
  export interface UIChatMessage {
39
51
  // Group chat fields (alphabetically before other fields)
40
52
  agentId?: string | 'supervisor';
53
+ /**
54
+ * Branch information for user messages with multiple children
55
+ */
56
+ branch?: UIMessageBranch;
41
57
  /**
42
58
  * children messages for grouped display
43
59
  * Used to group tool messages under their parent assistant message
@@ -7,8 +7,8 @@ export interface ChatTTS {
7
7
  }
8
8
 
9
9
  export interface ChatMessageExtra {
10
- fromModel?: string;
11
- fromProvider?: string;
10
+ model?: string;
11
+ provider?: string;
12
12
  // 翻译
13
13
  translate?: ChatTranslate | false | null;
14
14
  // TTS
@@ -16,8 +16,8 @@ export interface CreateMessageParams
16
16
  error?: ChatMessageError | null;
17
17
  fileChunks?: MessageSemanticSearchChunk[];
18
18
  files?: string[];
19
- fromModel?: string;
20
- fromProvider?: string;
19
+ model?: string;
20
+ provider?: string;
21
21
  groupId?: string;
22
22
  role: CreateMessageRoleType;
23
23
  sessionId: string;
@@ -35,10 +35,6 @@ export const UserGuideSchema = z.object({
35
35
  export type UserGuide = z.infer<typeof UserGuideSchema>;
36
36
 
37
37
  export const UserLabSchema = z.object({
38
- /**
39
- * enable assistant message grouping in chat display
40
- */
41
- enableAssistantMessageGroup: z.boolean().optional(),
42
38
  /**
43
39
  * enable multi-agent group chat mode
44
40
  */
@@ -1,15 +1,7 @@
1
+ import { estimateTokenCount } from 'tokenx';
2
+
1
3
  export const encodeAsync = async (str: string): Promise<number> => {
2
4
  if (str.length === 0) return 0;
3
5
 
4
- // use gpt-tokenizer under 10000 str
5
- // use approximation way if large then 10000
6
- if (str.length <= 10_000) {
7
- const { clientEncodeAsync } = await import('./client');
8
-
9
- return await clientEncodeAsync(str);
10
- } else {
11
- const { estimatedEncodeAsync } = await import('./estimated');
12
-
13
- return await estimatedEncodeAsync(str);
14
- }
6
+ return estimateTokenCount(str);
15
7
  };
@@ -8,7 +8,7 @@ import { useChatStore } from '@/store/chat';
8
8
  import { useSend } from '../useSend';
9
9
 
10
10
  const MessageFromUrl = () => {
11
- const updateInputMessage = useChatStore((s) => s.updateInputMessage);
11
+ const updateMessageInput = useChatStore((s) => s.updateMessageInput);
12
12
  const { send: sendMessage } = useSend();
13
13
  const searchParams = useSearchParams();
14
14
 
@@ -21,10 +21,10 @@ const MessageFromUrl = () => {
21
21
  const newUrl = `${window.location.pathname}?${params.toString()}`;
22
22
  window.history.replaceState({}, '', newUrl);
23
23
 
24
- updateInputMessage(message);
24
+ updateMessageInput(message);
25
25
  sendMessage();
26
26
  }
27
- }, [searchParams, updateInputMessage, sendMessage]);
27
+ }, [searchParams, updateMessageInput, sendMessage]);
28
28
 
29
29
  return null;
30
30
  };
@@ -39,7 +39,7 @@ const MobileChatInput = memo(() => {
39
39
  const [loading, value, onInput, onStop] = useChatStore((s) => [
40
40
  messageStateSelectors.isAIGenerating(s),
41
41
  s.inputMessage,
42
- s.updateInputMessage,
42
+ s.updateMessageInput,
43
43
  s.stopGenerateMessage,
44
44
  ]);
45
45
 
@@ -16,9 +16,9 @@ export type UseSendMessageParams = Pick<
16
16
  >;
17
17
 
18
18
  export const useSendMessage = () => {
19
- const [sendMessage, updateInputMessage] = useChatStore((s) => [
19
+ const [sendMessage, updateMessageInput] = useChatStore((s) => [
20
20
  s.sendMessage,
21
- s.updateInputMessage,
21
+ s.updateMessageInput,
22
22
  ]);
23
23
  const { analytics } = useAnalytics();
24
24
  const checkGeminiChineseWarning = useGeminiChineseWarning();
@@ -66,7 +66,7 @@ export const useSendMessage = () => {
66
66
  ...params,
67
67
  });
68
68
 
69
- updateInputMessage('');
69
+ updateMessageInput('');
70
70
  clearChatUploadFileList();
71
71
 
72
72
  // 获取分析数据
@@ -161,14 +161,14 @@ export const useSendGroupMessage = () => {
161
161
  const [
162
162
  isContentEmpty,
163
163
  sendGroupMessage,
164
- updateInputMessage,
164
+ updateMessageInput,
165
165
  stopGenerateMessage,
166
166
  isSendButtonDisabledByMessage,
167
167
  isCreatingMessage,
168
168
  ] = useChatStore((s) => [
169
169
  !s.inputMessage,
170
170
  s.sendGroupMessage,
171
- s.updateInputMessage,
171
+ s.updateMessageInput,
172
172
  s.stopGenerateMessage,
173
173
  messageStateSelectors.isSendButtonDisabledByMessage(s),
174
174
  messageStateSelectors.isCreatingMessage(s),
@@ -255,7 +255,7 @@ export const useSendGroupMessage = () => {
255
255
  mainInputEditor.setExpand(false);
256
256
  mainInputEditor.clearContent();
257
257
  mainInputEditor.focus();
258
- updateInputMessage('');
258
+ updateMessageInput('');
259
259
  // clear mentioned users after sending
260
260
  mentionState.clearMentionedUsers();
261
261
 
@@ -284,7 +284,7 @@ export const useSendGroupMessage = () => {
284
284
  canNotSend,
285
285
  fileList,
286
286
  clearChatUploadFileList,
287
- updateInputMessage,
287
+ updateMessageInput,
288
288
  analytics,
289
289
  checkGeminiChineseWarning,
290
290
  ],
@@ -314,8 +314,8 @@ export const useSendGroupMessage = () => {
314
314
  generating: isSupervisorThinking || isCreatingMessage,
315
315
  send: handleSend,
316
316
  stop,
317
- updateInputMessage,
317
+ updateMessageInput,
318
318
  }),
319
- [canNotSend, isSupervisorThinking, isCreatingMessage, handleSend, stop, updateInputMessage],
319
+ [canNotSend, isSupervisorThinking, isCreatingMessage, handleSend, stop, updateMessageInput],
320
320
  );
321
321
  };
@@ -6,7 +6,7 @@ import { SkeletonList, VirtualizedList } from '@/features/Conversation';
6
6
  import WideScreenContainer from '@/features/Conversation/components/WideScreenContainer';
7
7
  import { useFetchMessages } from '@/hooks/useFetchMessages';
8
8
  import { useChatStore } from '@/store/chat';
9
- import { chatSelectors } from '@/store/chat/selectors';
9
+ import { displayMessageSelectors } from '@/store/chat/selectors';
10
10
 
11
11
  import MainChatItem from './ChatItem';
12
12
  import Welcome from './WelcomeChatItem';
@@ -16,10 +16,12 @@ interface ListProps {
16
16
  }
17
17
 
18
18
  const Content = memo<ListProps>(({ mobile }) => {
19
- const [isCurrentChatLoaded] = useChatStore((s) => [chatSelectors.isCurrentChatLoaded(s)]);
19
+ const [isCurrentChatLoaded] = useChatStore((s) => [
20
+ displayMessageSelectors.isCurrentDisplayChatLoaded(s),
21
+ ]);
20
22
 
21
23
  useFetchMessages();
22
- const data = useChatStore(chatSelectors.mainDisplayChatIDs);
24
+ const data = useChatStore(displayMessageSelectors.mainDisplayChatIDs);
23
25
 
24
26
  const itemContent = useCallback(
25
27
  (index: number, id: string) => <MainChatItem id={id} index={index} />,
@@ -40,7 +40,7 @@ interface OpeningQuestionsProps {
40
40
 
41
41
  const OpeningQuestions = memo<OpeningQuestionsProps>(({ mobile, questions }) => {
42
42
  const { t } = useTranslation('welcome');
43
- const [updateInputMessage] = useChatStore((s) => [s.updateInputMessage]);
43
+ const [updateMessageInput] = useChatStore((s) => [s.updateMessageInput]);
44
44
 
45
45
  const { styles } = useStyles();
46
46
  const { send: sendMessage } = useSend();
@@ -56,7 +56,7 @@ const OpeningQuestions = memo<OpeningQuestionsProps>(({ mobile, questions }) =>
56
56
  clickable
57
57
  key={question}
58
58
  onClick={() => {
59
- updateInputMessage(question);
59
+ updateMessageInput(question);
60
60
  sendMessage({ isWelcomeQuestion: true });
61
61
  }}
62
62
  paddingBlock={8}
@@ -77,7 +77,7 @@ const getFallbackActivities = (t: any) => {
77
77
  const GroupUsageSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
78
78
  const { t } = useTranslation('welcome');
79
79
  const { styles } = useStyles();
80
- const { updateInputMessage, send } = useSendGroupMessage();
80
+ const { updateMessageInput, send } = useSendGroupMessage();
81
81
  const templateMatch = useTemplateMatching();
82
82
 
83
83
  const itemsPerPage = mobile ? 2 : 4;
@@ -139,7 +139,7 @@ const GroupUsageSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
139
139
  horizontal
140
140
  key={activityKey}
141
141
  onClick={() => {
142
- updateInputMessage(prompt);
142
+ updateMessageInput(prompt);
143
143
  send();
144
144
  }}
145
145
  variant={'outlined'}
@@ -24,13 +24,11 @@ const LabsPage = memo(() => {
24
24
  const [
25
25
  isPreferenceInit,
26
26
  enableInputMarkdown,
27
- enableAssistantMessageGroup,
28
27
  // enableGroupChat,
29
28
  updateLab,
30
29
  ] = useUserStore((s) => [
31
30
  preferenceSelectors.isPreferenceInit(s),
32
31
  labPreferSelectors.enableInputMarkdown(s),
33
- labPreferSelectors.enableAssistantMessageGroup(s),
34
32
  // labPreferSelectors.enableGroupChat(s),
35
33
  s.updateLab,
36
34
  ]);
@@ -43,13 +41,6 @@ const LabsPage = memo(() => {
43
41
  key: 'enableInputMarkdown',
44
42
  title: t('features.inputMarkdown.title'),
45
43
  },
46
- {
47
- checked: enableAssistantMessageGroup,
48
- cover: 'https://github.com/user-attachments/assets/ba517751-1f3b-4269-979e-f8471e3ebb89',
49
- desc: t('features.assistantMessageGroup.desc'),
50
- key: 'enableAssistantMessageGroup',
51
- title: t('features.assistantMessageGroup.title'),
52
- },
53
44
  // {
54
45
  // checked: enableGroupChat,
55
46
  // cover: 'https://github.com/user-attachments/assets/72894d24-a96a-4d7c-a823-ff9e6a1a8b6d',
@@ -42,9 +42,9 @@ const BrowserSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
42
42
  const [error, setError] = useState<ChatMessageError>();
43
43
  const { t } = useTranslation('chat');
44
44
 
45
- const [loading, updateInputMessage] = useChatStore((s) => [
45
+ const [loading, updateMessageInput] = useChatStore((s) => [
46
46
  messageStateSelectors.isAIGenerating(s),
47
- s.updateInputMessage,
47
+ s.updateMessageInput,
48
48
  ]);
49
49
 
50
50
  const setDefaultError = useCallback(
@@ -76,7 +76,7 @@ const BrowserSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
76
76
  },
77
77
  onTextChange: (text) => {
78
78
  if (loading) stop();
79
- if (text) updateInputMessage(text);
79
+ if (text) updateMessageInput(text);
80
80
  },
81
81
  });
82
82
 
@@ -53,9 +53,9 @@ const OpenaiSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
53
53
  const [error, setError] = useState<ChatMessageError>();
54
54
  const { t } = useTranslation('chat');
55
55
 
56
- const [loading, updateInputMessage] = useChatStore((s) => [
56
+ const [loading, updateMessageInput] = useChatStore((s) => [
57
57
  messageStateSelectors.isAIGenerating(s),
58
- s.updateInputMessage,
58
+ s.updateMessageInput,
59
59
  ]);
60
60
 
61
61
  const setDefaultError = useCallback(
@@ -87,7 +87,7 @@ const OpenaiSTT = memo<{ mobile?: boolean }>(({ mobile }) => {
87
87
  },
88
88
  onTextChange: (text) => {
89
89
  if (loading) stop();
90
- if (text) updateInputMessage(text);
90
+ if (text) updateMessageInput(text);
91
91
  },
92
92
  });
93
93
 
@@ -19,7 +19,7 @@ const AccessCodeForm = memo<AccessCodeFormProps>(({ id }) => {
19
19
  keyVaultsConfigSelectors.password(s),
20
20
  s.updateKeyVaults,
21
21
  ]);
22
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
22
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
23
23
 
24
24
  return (
25
25
  <>
@@ -13,7 +13,7 @@ interface ChatInvalidAPIKeyProps {
13
13
  const ChatInvalidAPIKey = memo<ChatInvalidAPIKeyProps>(({ id, provider }) => {
14
14
  const { t } = useTranslation('modelProvider');
15
15
  const { t: modelProviderErrorT } = useTranslation(['modelProvider', 'error']);
16
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
16
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
17
17
  const providerName = useProviderName(provider as GlobalLLMProviderKey);
18
18
 
19
19
  return (
@@ -15,7 +15,7 @@ const ClerkLogin = memo<{ id: string }>(({ id }) => {
15
15
  const [openSignIn, isSignedIn] = useUserStore((s) => [s.openLogin, s.isSignedIn]);
16
16
  const greeting = useGreeting();
17
17
  const nickName = useUserStore(userProfileSelectors.nickName);
18
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
18
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
19
19
 
20
20
  return (
21
21
  <ErrorActionContainer>
@@ -18,7 +18,7 @@ const OAuthForm = memo<{ id: string }>(({ id }) => {
18
18
  const user = useUserStore(userProfileSelectors.userProfile);
19
19
  const isOAuthLoggedIn = useUserStore(authSelectors.isLoginWithAuth);
20
20
 
21
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
21
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
22
22
 
23
23
  const { message, modal } = App.useApp();
24
24
 
@@ -12,7 +12,6 @@ import { useProviderName } from '@/hooks/useProviderName';
12
12
  import ChatInvalidAPIKey from './ChatInvalidApiKey';
13
13
  import ClerkLogin from './ClerkLogin';
14
14
  import ErrorJsonViewer from './ErrorJsonViewer';
15
- import InvalidAccessCode from './InvalidAccessCode';
16
15
  import { ErrorActionContainer } from './style';
17
16
 
18
17
  interface ErrorMessageData {
@@ -118,10 +117,6 @@ const ErrorMessageExtra = memo<ErrorExtraProps>(({ data, block }) => {
118
117
  return <ClerkLogin id={data.id} />;
119
118
  }
120
119
 
121
- case ChatErrorType.InvalidAccessCode: {
122
- return <InvalidAccessCode id={data.id} provider={data.error?.body?.provider} />;
123
- }
124
-
125
120
  case AgentRuntimeErrorType.NoOpenAIAPIKey: {
126
121
  {
127
122
  return <ChatInvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
8
8
  import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
9
9
  import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
10
10
  import { useChatStore } from '@/store/chat';
11
- import { threadSelectors } from '@/store/chat/selectors';
11
+ import { messageStateSelectors, threadSelectors } from '@/store/chat/selectors';
12
12
  import { useSessionStore } from '@/store/session';
13
13
  import { sessionSelectors } from '@/store/session/selectors';
14
14
 
@@ -23,9 +23,10 @@ interface AssistantActionsProps {
23
23
  }
24
24
  export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, index }) => {
25
25
  const { error, tools } = data;
26
- const [isThreadMode, hasThread] = useChatStore((s) => [
26
+ const [isThreadMode, hasThread, isRegenerating] = useChatStore((s) => [
27
27
  !!s.activeThreadId,
28
28
  threadSelectors.hasThreadBySourceMsgId(id)(s),
29
+ messageStateSelectors.isMessageRegenerating(id)(s),
29
30
  ]);
30
31
  const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
31
32
  const [showShareModal, setShareModal] = useState(false);
@@ -42,7 +43,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
42
43
  share,
43
44
  tts,
44
45
  translate,
45
- } = useChatListActionsBar({ hasThread });
46
+ } = useChatListActionsBar({ hasThread, isRegenerating });
46
47
 
47
48
  const hasTools = !!tools;
48
49
 
@@ -52,17 +53,19 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
52
53
  const items = useMemo(() => {
53
54
  if (hasTools) return [delAndRegenerate, copy];
54
55
 
55
- return [edit, copy, inThread || isGroupSession ? null : branching].filter(
56
- Boolean,
57
- ) as ActionIconGroupItemType[];
58
- }, [inThread, hasTools, isGroupSession]);
56
+ return [
57
+ edit,
58
+ copy,
59
+ // inThread || isGroupSession ? null : branching
60
+ ].filter(Boolean) as ActionIconGroupItemType[];
61
+ }, [inThread, hasTools, isGroupSession, delAndRegenerate, copy, edit, branching]);
59
62
 
60
63
  const { t } = useTranslation('common');
61
64
  const searchParams = useSearchParams();
62
65
  const topic = searchParams.get('topic');
63
66
  const [
64
67
  deleteMessage,
65
- regenerateMessage,
68
+ regenerateAssistantMessage,
66
69
  translateMessage,
67
70
  ttsMessage,
68
71
  delAndRegenerateMessage,
@@ -73,7 +76,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
73
76
  toggleMessageEditing,
74
77
  ] = useChatStore((s) => [
75
78
  s.deleteMessage,
76
- s.regenerateMessage,
79
+ s.regenerateAssistantMessage,
77
80
  s.translateMessage,
78
81
  s.ttsMessage,
79
82
  s.delAndRegenerateMessage,
@@ -120,7 +123,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
120
123
  case 'regenerate': {
121
124
  if (inPortalThread) {
122
125
  resendThreadMessage(id);
123
- } else regenerateMessage(id);
126
+ } else regenerateAssistantMessage(id);
124
127
 
125
128
  // if this message is an error message, we need to delete it
126
129
  if (data.error) deleteMessage(id);
@@ -46,20 +46,15 @@ describe('AssistantMessageExtra', () => {
46
46
  expect(screen.queryByText('Translate Component')).toBeNull();
47
47
  });
48
48
 
49
- it('should not render content if extra is defined but does not contain fromModel, tts, or translate', async () => {
49
+ it('should not render content if extra is defined but does not contain model, tts, or translate', async () => {
50
50
  render(<AssistantMessageExtra {...mockData} extra={{}} />);
51
51
  expect(screen.queryByText('Usage Component')).toBeNull();
52
52
  expect(screen.queryByText('TTS Component')).toBeNull();
53
53
  expect(screen.queryByText('Translate Component')).toBeNull();
54
54
  });
55
55
 
56
- it('should render Usage component if extra.fromModel exists', async () => {
57
- render(
58
- <AssistantMessageExtra
59
- {...mockData}
60
- extra={{ fromModel: 'gpt-4', fromProvider: 'openai' }}
61
- />,
62
- );
56
+ it('should render Usage component if extra.model exists', async () => {
57
+ render(<AssistantMessageExtra {...mockData} extra={{ model: 'gpt-4', provider: 'openai' }} />);
63
58
 
64
59
  expect(screen.getByText('Usage Component')).toBeInTheDocument();
65
60
  });
@@ -24,12 +24,8 @@ export const AssistantMessageExtra = memo<AssistantMessageExtraProps>(
24
24
 
25
25
  return (
26
26
  <Flexbox gap={8} style={{ marginTop: !!tools?.length ? 8 : 4 }}>
27
- {content !== LOADING_FLAT && extra?.fromModel && (
28
- <Usage
29
- metadata={metadata || {}}
30
- model={extra?.fromModel}
31
- provider={extra.fromProvider!}
32
- />
27
+ {content !== LOADING_FLAT && extra?.model && (
28
+ <Usage metadata={metadata || {}} model={extra?.model} provider={extra.provider!} />
33
29
  )}
34
30
  <>
35
31
  {!!extra?.tts && (
@@ -58,15 +58,13 @@ export const AssistantMessageContent = memo<
58
58
  {isIntentUnderstanding ? (
59
59
  <IntentUnderstanding />
60
60
  ) : (
61
- content && (
62
- <DefaultMessage
63
- addIdOnDOM={false}
64
- content={content}
65
- id={id}
66
- isToolCallGenerating={isToolCallGenerating}
67
- {...props}
68
- />
69
- )
61
+ <DefaultMessage
62
+ addIdOnDOM={false}
63
+ content={content}
64
+ id={id}
65
+ isToolCallGenerating={isToolCallGenerating}
66
+ {...props}
67
+ />
70
68
  )}
71
69
  {showImageItems && <ImageFileListViewer items={imageList} />}
72
70
  {tools && (
@@ -2,7 +2,7 @@ import { Highlighter } from '@lobehub/ui';
2
2
  import { memo, useMemo } from 'react';
3
3
 
4
4
  import { useChatStore } from '@/store/chat';
5
- import { chatSelectors } from '@/store/chat/selectors';
5
+ import { dbMessageSelectors } from '@/store/chat/selectors';
6
6
 
7
7
  export interface FunctionMessageProps {
8
8
  toolCallId: string;
@@ -10,7 +10,7 @@ export interface FunctionMessageProps {
10
10
  }
11
11
 
12
12
  const PluginResult = memo<FunctionMessageProps>(({ toolCallId, variant }) => {
13
- const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(toolCallId));
13
+ const toolMessage = useChatStore(dbMessageSelectors.getDbMessageByToolCallId(toolCallId));
14
14
 
15
15
  const { data, language } = useMemo(() => {
16
16
  try {