@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
@@ -154,36 +154,6 @@ describe('chatSelectors', () => {
154
154
  });
155
155
  });
156
156
 
157
- describe('getMessageByToolCallId', () => {
158
- it('should return undefined if the message with the given id does not exist', () => {
159
- const message = chatSelectors.getMessageByToolCallId('non-existent-id')(initialStore);
160
- expect(message).toBeUndefined();
161
- });
162
-
163
- it('should return the message object with the matching tool_call_id', () => {
164
- const toolMessage = {
165
- id: 'msg3',
166
- content: 'Function Message',
167
- role: 'tool',
168
- tool_call_id: 'ttt',
169
- plugin: {
170
- arguments: 'arg1',
171
- identifier: 'func1',
172
- apiName: 'ttt',
173
- type: 'default',
174
- },
175
- } as UIChatMessage;
176
- const state = merge(initialStore, {
177
- messagesMap: {
178
- [messageMapKey('abc')]: [...mockMessages, toolMessage],
179
- },
180
- activeId: 'abc',
181
- });
182
- const message = chatSelectors.getMessageByToolCallId('ttt')(state);
183
- expect(message).toMatchObject(toolMessage);
184
- });
185
- });
186
-
187
157
  describe('currentChatsWithHistoryConfig', () => {
188
158
  it('should slice the messages according to the current agent config', () => {
189
159
  const state = merge(initialStore, {
@@ -255,19 +225,6 @@ describe('chatSelectors', () => {
255
225
  });
256
226
  });
257
227
 
258
- describe('mainDisplayChats', () => {
259
- it('should return existing messages except tool message', () => {
260
- const state = merge(initialStore, {
261
- messagesMap: {
262
- [messageMapKey('someActiveId')]: mockMessages,
263
- },
264
- activeId: 'someActiveId',
265
- });
266
- const chats = chatSelectors.mainDisplayChats(state);
267
- expect(chats).toEqual(mockedChats.slice(0, 2));
268
- });
269
- });
270
-
271
228
  describe('chatsMessageString', () => {
272
229
  it('should concatenate the contents of all messages returned by currentChatsWithHistoryConfig', () => {
273
230
  // Prepare a state with a few messages
@@ -353,6 +310,9 @@ describe('chatSelectors', () => {
353
310
  ] as UIChatMessage[];
354
311
  const state: Partial<ChatStore> = {
355
312
  activeId: 'test-id',
313
+ dbMessagesMap: {
314
+ [messageMapKey('test-id')]: messages,
315
+ },
356
316
  messagesMap: {
357
317
  [messageMapKey('test-id')]: messages,
358
318
  },
@@ -369,6 +329,9 @@ describe('chatSelectors', () => {
369
329
  ] as UIChatMessage[];
370
330
  const state: Partial<ChatStore> = {
371
331
  activeId: 'test-id',
332
+ dbMessagesMap: {
333
+ [messageMapKey('test-id')]: messages,
334
+ },
372
335
  messagesMap: {
373
336
  [messageMapKey('test-id')]: messages,
374
337
  },
@@ -446,7 +409,7 @@ describe('chatSelectors', () => {
446
409
  it('should return the last child without tools', () => {
447
410
  const groupMessage = {
448
411
  id: 'group-1',
449
- role: 'group',
412
+ role: 'assistantGroup',
450
413
  content: '',
451
414
  children: [
452
415
  {
@@ -490,7 +453,7 @@ describe('chatSelectors', () => {
490
453
  it('should return null if the last child has tools', () => {
491
454
  const groupMessage = {
492
455
  id: 'group-2',
493
- role: 'group',
456
+ role: 'assistantGroup',
494
457
  content: '',
495
458
  children: [
496
459
  {
@@ -527,7 +490,7 @@ describe('chatSelectors', () => {
527
490
  it('should return the last child when it has empty tools array', () => {
528
491
  const groupMessage = {
529
492
  id: 'group-3',
530
- role: 'group',
493
+ role: 'assistantGroup',
531
494
  content: '',
532
495
  children: [
533
496
  {
@@ -585,7 +548,7 @@ describe('chatSelectors', () => {
585
548
  it('should return null for group messages without children', () => {
586
549
  const groupMessage = {
587
550
  id: 'group-4',
588
- role: 'group',
551
+ role: 'assistantGroup',
589
552
  content: '',
590
553
  children: undefined,
591
554
  } as UIChatMessage;
@@ -604,7 +567,7 @@ describe('chatSelectors', () => {
604
567
  it('should return null for group messages with empty children array', () => {
605
568
  const groupMessage = {
606
569
  id: 'group-5',
607
- role: 'group',
570
+ role: 'assistantGroup',
608
571
  content: '',
609
572
  children: [],
610
573
  } as unknown as UIChatMessage;
@@ -623,7 +586,7 @@ describe('chatSelectors', () => {
623
586
  it('should return null if all children have tools', () => {
624
587
  const groupMessage = {
625
588
  id: 'group-6',
626
- role: 'group',
589
+ role: 'assistantGroup',
627
590
  content: '',
628
591
  children: [
629
592
  {
@@ -669,7 +632,7 @@ describe('chatSelectors', () => {
669
632
  it('should handle empty tools array as no tools', () => {
670
633
  const groupMessage = {
671
634
  id: 'group-7',
672
- role: 'group',
635
+ role: 'assistantGroup',
673
636
  content: '',
674
637
  children: [
675
638
  {
@@ -1,272 +1,108 @@
1
- import { ChatFileItem, 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 { messageMapKey } from '@/store/chat/utils/messageMapKey';
8
- import { useSessionStore } from '@/store/session';
9
- import { sessionMetaSelectors } from '@/store/session/selectors';
10
- import { useUserStore } from '@/store/user';
11
- import { userProfileSelectors } from '@/store/user/selectors';
1
+ /**
2
+ * Chat Selectors - Backward Compatibility Layer
3
+ *
4
+ * This file provides backward compatibility by re-exporting selectors from:
5
+ * - dbMessage.ts: Raw database message selectors (for data operations)
6
+ * - displayMessage.ts: Display message selectors (for UI rendering)
7
+ *
8
+ * MIGRATION GUIDE:
9
+ * ================
10
+ *
11
+ * For Data Operations (create, update, delete):
12
+ * ----------------------------------------------
13
+ * Use dbMessageSelectors from './dbMessage'
14
+ *
15
+ * Before: chatSelectors.getMessageById(id)(state)
16
+ * After: dbMessageSelectors.getDbMessageById(id)(state)
17
+ *
18
+ * Before: chatSelectors.latestMessage(state)
19
+ * After: dbMessageSelectors.latestDbMessage(state)
20
+ *
21
+ * Before: chatSelectors.currentToolMessages(state)
22
+ * After: dbMessageSelectors.dbToolMessages(state)
23
+ *
24
+ * For UI Rendering:
25
+ * -----------------
26
+ * Use displayMessageSelectors from './displayMessage'
27
+ *
28
+ * Before: chatSelectors.mainDisplayChats(state)
29
+ * After: displayMessageSelectors.mainDisplayChats(state)
30
+ *
31
+ * Before: chatSelectors.getMessageById(id)(state) // For display
32
+ * After: displayMessageSelectors.getDisplayMessageById(id)(state)
33
+ */
34
+ import { ChatFileItem } from '@lobechat/types';
12
35
 
13
- import { chatHelpers } from '../../../helpers';
14
36
  import type { ChatStoreState } from '../../../initialState';
37
+ import { dbMessageSelectors } from './dbMessage';
38
+ import { displayMessageSelectors } from './displayMessage';
15
39
 
16
- const getMeta = (message: UIChatMessage) => {
17
- switch (message.role) {
18
- case 'user': {
19
- return {
20
- avatar: userProfileSelectors.userAvatar(useUserStore.getState()) || DEFAULT_USER_AVATAR,
21
- };
22
- }
23
-
24
- case 'system': {
25
- return message.meta;
26
- }
27
-
28
- default: {
29
- // For group chat, get meta from agent session
30
- if (message.groupId && message.agentId) {
31
- return sessionMetaSelectors.getAgentMetaByAgentId(message.agentId)(
32
- useSessionStore.getState(),
33
- );
34
- }
35
-
36
- // Otherwise, use the current session's agent meta for single agent chat
37
- return sessionMetaSelectors.currentAgentMeta(useSessionStore.getState());
38
- }
39
- }
40
- };
41
-
42
- const getBaseChatsByKey =
43
- (key: string) =>
44
- (s: ChatStoreState): UIChatMessage[] => {
45
- const messages = s.messagesMap[key] || [];
40
+ // Re-export new selector groups
41
+ export { dbMessageSelectors } from './dbMessage';
42
+ export { displayMessageSelectors } from './displayMessage';
46
43
 
47
- return messages.map((i) => ({ ...i, meta: getMeta(i) }));
48
- };
49
-
50
- export const currentChatKey = (s: ChatStoreState) => messageMapKey(s.activeId, s.activeTopicId);
44
+ // ============= Backward Compatibility Exports ========== //
45
+ // These maintain the old API for gradual migration
46
+ // Prefer using dbMessageSelectors or displayMessageSelectors directly
51
47
 
52
48
  /**
53
- * Current active raw message list, include thread messages
49
+ * @deprecated Use displayMessageSelectors.currentDisplayChatKey instead
54
50
  */
55
- const activeBaseChats = (s: ChatStoreState): UIChatMessage[] => {
56
- if (!s.activeId) return [];
57
-
58
- return getBaseChatsByKey(currentChatKey(s))(s);
59
- };
51
+ export const currentChatKey = displayMessageSelectors.currentDisplayChatKey;
60
52
 
61
53
  /**
62
- * 排除掉所有 tool 消息,在展示时需要使用
54
+ * @deprecated Use displayMessageSelectors.mainDisplayChatIDs instead
63
55
  */
64
- const activeBaseChatsWithoutTool = (s: ChatStoreState) => {
65
- const messages = activeBaseChats(s);
66
-
67
- return messages.filter((m) => m.role !== 'tool');
68
- };
69
-
70
- const getChatsWithThread = (s: ChatStoreState, messages: UIChatMessage[]) => {
71
- // 如果没有 activeThreadId,则返回所有的主消息
72
- if (!s.activeThreadId) return messages.filter((m) => !m.threadId);
73
-
74
- const thread = s.threadMaps[s.activeTopicId!]?.find((t) => t.id === s.activeThreadId);
75
-
76
- if (!thread) return messages.filter((m) => !m.threadId);
77
-
78
- const sourceIndex = messages.findIndex((m) => m.id === thread.sourceMessageId);
79
- const sliced = messages.slice(0, sourceIndex + 1);
80
-
81
- return [...sliced, ...messages.filter((m) => m.threadId === s.activeThreadId)];
82
- };
83
-
84
- // ============= Main Display Chats ========== //
85
- // =========================================== //
86
- const mainDisplayChats = (s: ChatStoreState): UIChatMessage[] => {
87
- const displayChats = activeBaseChatsWithoutTool(s);
88
-
89
- return getChatsWithThread(s, displayChats);
90
- };
91
-
92
- export const mainDisplayChatIDs = (s: ChatStoreState) => mainDisplayChats(s).map((s) => s.id);
93
-
94
- const mainAIChats = (s: ChatStoreState): UIChatMessage[] => {
95
- const messages = activeBaseChats(s);
96
-
97
- return getChatsWithThread(s, messages);
98
- };
99
-
100
- const mainAIChatsWithHistoryConfig = (s: ChatStoreState): UIChatMessage[] => {
101
- const chats = mainAIChats(s);
102
- const enableHistoryCount = agentChatConfigSelectors.enableHistoryCount(useAgentStore.getState());
103
- const historyCount = agentChatConfigSelectors.historyCount(useAgentStore.getState());
104
-
105
- return chatHelpers.getSlicedMessages(chats, {
106
- enableHistoryCount,
107
- historyCount,
108
- });
109
- };
110
-
111
- const mainAIChatsMessageString = (s: ChatStoreState): string => {
112
- const chats = mainAIChatsWithHistoryConfig(s);
113
- return chats.map((m) => m.content).join('');
114
- };
115
-
116
- const mainAILatestMessageReasoningContent = (s: ChatStoreState) =>
117
- mainAIChats(s).at(-1)?.reasoning?.content;
56
+ export const mainDisplayChatIDs = displayMessageSelectors.mainDisplayChatIDs;
118
57
 
119
- const currentToolMessages = (s: ChatStoreState) => {
120
- const messages = activeBaseChats(s);
121
-
122
- return messages.filter((m) => m.role === 'tool');
123
- };
124
-
125
- const currentUserMessages = (s: ChatStoreState) => {
126
- const messages = activeBaseChats(s);
127
-
128
- return messages.filter((m) => m.role === 'user');
129
- };
130
-
131
- const currentUserFiles = (s: ChatStoreState) => {
132
- const userMessages = currentUserMessages(s);
133
-
134
- return userMessages
135
- .filter((m) => m.fileList && m.fileList?.length > 0)
136
- .flatMap((m) => m.fileList)
137
- .filter(Boolean) as ChatFileItem[];
138
- };
139
-
140
- const showInboxWelcome = (s: ChatStoreState): boolean => {
141
- const isInbox = s.activeId === INBOX_SESSION_ID;
142
- if (!isInbox) return false;
143
-
144
- const data = activeBaseChats(s);
145
- return data.length === 0;
146
- };
147
-
148
- const getMessageById = (id: string) => (s: ChatStoreState) =>
149
- chatHelpers.getMessageById(activeBaseChats(s), id);
150
-
151
- const countMessagesByThreadId = (id: string) => (s: ChatStoreState) => {
152
- const messages = activeBaseChats(s).filter((m) => m.threadId === id);
153
-
154
- return messages.length;
155
- };
156
-
157
- export const getMessageByToolCallId = (id: string) => (s: ChatStoreState) => {
158
- const messages = activeBaseChats(s);
159
- return messages.find((m) => m.tool_call_id === id);
160
- };
161
- const getTraceIdByMessageId = (id: string) => (s: ChatStoreState) => getMessageById(id)(s)?.traceId;
58
+ /**
59
+ * Legacy chatSelectors object for backward compatibility
60
+ * @deprecated Import dbMessageSelectors or displayMessageSelectors directly
61
+ */
62
+ export const chatSelectors = {
63
+ // Display message selectors (from messagesMap)
64
+ activeBaseChats: displayMessageSelectors.activeDisplayMessages,
65
+ // DB message selectors (from dbMessagesMap)
66
+ countMessagesByThreadId: dbMessageSelectors.countDbMessagesByThreadId,
162
67
 
163
- const latestMessage = (s: ChatStoreState) => activeBaseChats(s).at(-1);
68
+ currentChatKey: displayMessageSelectors.currentDisplayChatKey,
164
69
 
165
- const currentChatLoadingState = (s: ChatStoreState) => !s.messagesInit;
70
+ currentChatLoadingState: displayMessageSelectors.currentChatLoadingState,
166
71
 
167
- const isCurrentChatLoaded = (s: ChatStoreState) => !!s.messagesMap[currentChatKey(s)];
72
+ currentToolMessages: dbMessageSelectors.dbToolMessages,
168
73
 
169
- const inboxActiveTopicMessages = (state: ChatStoreState) => {
170
- const activeTopicId = state.activeTopicId;
171
- return state.messagesMap[messageMapKey(INBOX_SESSION_ID, activeTopicId)] || [];
172
- };
74
+ currentUserFiles: (s: ChatStoreState) =>
75
+ dbMessageSelectors.dbUserFiles(s) as unknown as ChatFileItem[],
173
76
 
174
- /**
175
- * Gets messages between the current user and a specific agent (thread messages)
176
- * This is like a DM (Direct Message) view between user and agent
177
- */
178
- const getThreadMessages =
179
- (agentId: string) =>
180
- (s: ChatStoreState): UIChatMessage[] => {
181
- if (!agentId) return [];
77
+ getBaseChatsByKey: displayMessageSelectors.getDisplayMessagesByKey,
182
78
 
183
- const allMessages = activeBaseChats(s);
79
+ getGroupLatestMessageWithoutTools: displayMessageSelectors.getGroupLatestMessageWithoutTools,
184
80
 
185
- // Filter messages to only include:
186
- // 1. User messages sent TO the specific agent (role: 'user' && targetId matches agentId)
187
- // 2. Assistant messages FROM the specific agent sent TO user (role: 'assistant' && agentId matches && targetId is 'user')
188
- return allMessages.filter((message) => {
189
- if (message.role === 'user' && message.targetId === agentId) {
190
- return true; // Include user messages sent to the specific agent
191
- }
81
+ getMessageById: displayMessageSelectors.getDisplayMessageById,
192
82
 
193
- if (
194
- message.role === 'assistant' &&
195
- message.agentId === agentId &&
196
- message.targetId === 'user'
197
- ) {
198
- return true; // Include messages from the specific agent sent to user
199
- }
83
+ getSupervisorTodos: displayMessageSelectors.getSupervisorTodos,
200
84
 
201
- return false; // Exclude all other messages
202
- });
203
- };
85
+ getThreadMessageIDs: displayMessageSelectors.getThreadMessageIDs,
204
86
 
205
- /**
206
- * Gets thread message IDs for a specific agent
207
- */
208
- const getThreadMessageIDs =
209
- (agentId: string) =>
210
- (s: ChatStoreState): string[] => {
211
- return getThreadMessages(agentId)(s).map((message) => message.id);
212
- };
87
+ getThreadMessages: displayMessageSelectors.getThreadMessages,
213
88
 
214
- const isSupervisorLoading = (groupId: string) => (s: ChatStoreState) =>
215
- s.supervisorDecisionLoading.includes(groupId);
89
+ getTraceIdByMessageId: dbMessageSelectors.getTraceIdByDbMessageId,
216
90
 
217
- const getSupervisorTodos = (groupId?: string, topicId?: string | null) => (s: ChatStoreState) => {
218
- if (!groupId) return [];
219
- return s.supervisorTodos[messageMapKey(groupId, topicId)] || [];
220
- };
91
+ inboxActiveTopicMessages: displayMessageSelectors.inboxActiveTopicDisplayMessages,
221
92
 
222
- /**
223
- * Gets the latest message block from a group message that doesn't contain tools
224
- * Returns null if the last block contains tools or if message is not a group message
225
- */
226
- const getGroupLatestMessageWithoutTools = (id: string) => (s: ChatStoreState) => {
227
- const message = getMessageById(id)(s);
93
+ isCurrentChatLoaded: displayMessageSelectors.isCurrentDisplayChatLoaded,
228
94
 
229
- if (!message || message.role !== 'group' || !message.children || message.children.length === 0)
230
- return;
95
+ isSupervisorLoading: displayMessageSelectors.isSupervisorLoading,
231
96
 
232
- // Get the last child
233
- const lastChild = message.children.at(-1);
97
+ latestMessage: dbMessageSelectors.latestDbMessage,
234
98
 
235
- if (!lastChild) return;
99
+ mainAIChats: displayMessageSelectors.mainAIChats,
236
100
 
237
- // Return the last child only if it doesn't have tools
238
- if (!lastChild.tools || lastChild.tools.length === 0) {
239
- return lastChild;
240
- }
101
+ mainAIChatsMessageString: displayMessageSelectors.mainAIChatsMessageString,
241
102
 
242
- return;
243
- };
244
-
245
- export const chatSelectors = {
246
- activeBaseChats,
247
- activeBaseChatsWithoutTool,
248
- countMessagesByThreadId,
249
- currentChatKey,
250
- currentChatLoadingState,
251
- currentToolMessages,
252
- currentUserFiles,
253
- getBaseChatsByKey,
254
- getGroupLatestMessageWithoutTools,
255
- getMessageById,
256
- getMessageByToolCallId,
257
- getSupervisorTodos,
258
- getThreadMessageIDs,
259
- getThreadMessages,
260
- getTraceIdByMessageId,
261
- inboxActiveTopicMessages,
262
- isCurrentChatLoaded,
263
- isSupervisorLoading,
264
- latestMessage,
265
- mainAIChats,
266
- mainAIChatsMessageString,
267
- mainAIChatsWithHistoryConfig,
268
- mainAILatestMessageReasoningContent,
269
- mainDisplayChatIDs,
270
- mainDisplayChats,
271
- showInboxWelcome,
103
+ mainAIChatsWithHistoryConfig: displayMessageSelectors.mainAIChatsWithHistoryConfig,
104
+ mainAILatestMessageReasoningContent: displayMessageSelectors.mainAILatestMessageReasoningContent,
105
+ mainDisplayChatIDs: displayMessageSelectors.mainDisplayChatIDs,
106
+ mainDisplayChats: displayMessageSelectors.mainDisplayChats,
107
+ showInboxWelcome: displayMessageSelectors.showInboxWelcome,
272
108
  };
@@ -0,0 +1,140 @@
1
+ import { UIChatMessage } from '@lobechat/types';
2
+
3
+ import { chatHelpers } from '../../../helpers';
4
+ import type { ChatStoreState } from '../../../initialState';
5
+ import { messageMapKey } from '../../../utils/messageMapKey';
6
+
7
+ /**
8
+ * DB Message Selectors
9
+ *
10
+ * These selectors access raw messages from dbMessagesMap (database source).
11
+ * Use these selectors when you need to:
12
+ * - Operate on raw message data (create, update, delete)
13
+ * - Access original message structure before processing
14
+ * - Perform data mutations or service calls
15
+ *
16
+ * DO NOT use these for UI rendering - use displayMessage.ts selectors instead.
17
+ */
18
+
19
+ // ============= Basic DB Message Access ========== //
20
+
21
+ /**
22
+ * Get the current chat key for accessing dbMessagesMap
23
+ */
24
+ export const currentDbChatKey = (s: ChatStoreState) => messageMapKey(s.activeId, s.activeTopicId);
25
+
26
+ /**
27
+ * Get raw messages from database by key
28
+ */
29
+ const getDbMessagesByKey =
30
+ (key: string) =>
31
+ (s: ChatStoreState): UIChatMessage[] => {
32
+ return s.dbMessagesMap[key] || [];
33
+ };
34
+
35
+ /**
36
+ * Get current active session's raw messages from database
37
+ */
38
+ const activeDbMessages = (s: ChatStoreState): UIChatMessage[] => {
39
+ if (!s.activeId) return [];
40
+ return getDbMessagesByKey(currentDbChatKey(s))(s);
41
+ };
42
+
43
+ // ============= DB Message Queries ========== //
44
+
45
+ /**
46
+ * Get raw message by ID from database
47
+ * This searches in dbMessagesMap, which contains flat message structure
48
+ */
49
+ const getDbMessageById = (id: string) => (s: ChatStoreState) =>
50
+ chatHelpers.getMessageById(activeDbMessages(s), id);
51
+
52
+ /**
53
+ * Get raw message by tool_call_id from database
54
+ */
55
+ export const getDbMessageByToolCallId = (id: string) => (s: ChatStoreState) => {
56
+ const messages = activeDbMessages(s);
57
+ return messages.find((m) => m.tool_call_id === id);
58
+ };
59
+
60
+ /**
61
+ * Get traceId from a message by ID
62
+ */
63
+ const getTraceIdByDbMessageId = (id: string) => (s: ChatStoreState) =>
64
+ getDbMessageById(id)(s)?.traceId;
65
+
66
+ /**
67
+ * Get latest raw message from database
68
+ */
69
+ const latestDbMessage = (s: ChatStoreState) => activeDbMessages(s).at(-1);
70
+
71
+ // ============= DB Message Filtering ========== //
72
+
73
+ /**
74
+ * Get all tool messages from database
75
+ */
76
+ const dbToolMessages = (s: ChatStoreState) => {
77
+ const messages = activeDbMessages(s);
78
+ return messages.filter((m) => m.role === 'tool');
79
+ };
80
+
81
+ /**
82
+ * Get all user messages from database
83
+ */
84
+ const dbUserMessages = (s: ChatStoreState) => {
85
+ const messages = activeDbMessages(s);
86
+ return messages.filter((m) => m.role === 'user');
87
+ };
88
+
89
+ /**
90
+ * Get all file attachments from user messages
91
+ */
92
+ const dbUserFiles = (s: ChatStoreState) => {
93
+ const userMessages = dbUserMessages(s);
94
+ return userMessages
95
+ .filter((m) => m.fileList && m.fileList.length > 0)
96
+ .flatMap((m) => m.fileList)
97
+ .filter(Boolean);
98
+ };
99
+
100
+ // ============= DB Message Counting ========== //
101
+
102
+ /**
103
+ * Count messages in a specific thread
104
+ */
105
+ const countDbMessagesByThreadId = (id: string) => (s: ChatStoreState) => {
106
+ const messages = activeDbMessages(s).filter((m) => m.threadId === id);
107
+ return messages.length;
108
+ };
109
+
110
+ // ============= DB Message Status ========== //
111
+
112
+ /**
113
+ * Check if current chat is loaded in dbMessagesMap
114
+ */
115
+ const isCurrentDbChatLoaded = (s: ChatStoreState) => !!s.dbMessagesMap[currentDbChatKey(s)];
116
+
117
+ /**
118
+ * Get inbox active topic raw messages from database
119
+ */
120
+ const inboxActiveTopicDbMessages = (state: ChatStoreState) => {
121
+ const activeTopicId = state.activeTopicId;
122
+ const key = messageMapKey('inbox', activeTopicId);
123
+ return state.dbMessagesMap[key] || [];
124
+ };
125
+
126
+ export const dbMessageSelectors = {
127
+ activeDbMessages,
128
+ countDbMessagesByThreadId,
129
+ currentDbChatKey,
130
+ dbToolMessages,
131
+ dbUserFiles,
132
+ dbUserMessages,
133
+ getDbMessageById,
134
+ getDbMessageByToolCallId,
135
+ getDbMessagesByKey,
136
+ getTraceIdByDbMessageId,
137
+ inboxActiveTopicDbMessages,
138
+ isCurrentDbChatLoaded,
139
+ latestDbMessage,
140
+ };