@lobehub/chat 1.32.4 → 1.32.5

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 (38) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +2 -2
  3. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +39 -0
  4. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +20 -14
  5. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx +44 -0
  6. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/index.tsx +17 -0
  7. package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +1 -13
  8. package/src/components/BrandWatermark/index.tsx +1 -0
  9. package/src/const/message.ts +1 -1
  10. package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +9 -13
  11. package/src/features/Conversation/components/ChatItem/index.tsx +183 -209
  12. package/src/features/Conversation/components/VirtualizedList/index.tsx +75 -84
  13. package/src/features/Conversation/index.ts +0 -1
  14. package/src/features/Portal/Artifacts/index.ts +1 -1
  15. package/src/features/Portal/FilePreview/index.ts +1 -1
  16. package/src/features/Portal/Home/{Header.tsx → Title.tsx} +2 -2
  17. package/src/features/Portal/Home/index.ts +1 -1
  18. package/src/features/Portal/MessageDetail/index.ts +1 -1
  19. package/src/features/Portal/Plugins/index.ts +1 -1
  20. package/src/features/Portal/components/Header.tsx +29 -0
  21. package/src/features/Portal/router.tsx +22 -3
  22. package/src/features/Portal/type.ts +3 -1
  23. package/src/features/{Conversation/components → ShareModal/ShareImage}/ChatList/index.tsx +3 -4
  24. package/src/features/ShareModal/ShareImage/Preview.tsx +1 -1
  25. package/src/features/ShareModal/ShareJSON/index.tsx +1 -1
  26. package/src/features/ShareModal/ShareText/index.tsx +1 -1
  27. package/src/layout/GlobalProvider/Debug.tsx +15 -0
  28. package/src/layout/GlobalProvider/index.tsx +2 -0
  29. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -5
  30. package/src/store/chat/slices/message/action.ts +2 -2
  31. package/src/store/chat/slices/message/selectors.test.ts +0 -86
  32. package/src/store/chat/slices/message/selectors.ts +55 -73
  33. package/src/store/chat/slices/plugin/action.test.ts +2 -2
  34. package/src/store/chat/slices/plugin/action.ts +1 -1
  35. package/src/store/chat/slices/topic/action.ts +2 -2
  36. /package/src/{features/Conversation/components → app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem}/InboxWelcome/AgentsSuggest.tsx +0 -0
  37. /package/src/{features/Conversation/components → app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem}/InboxWelcome/QuestionSuggest.tsx +0 -0
  38. /package/src/{features/Conversation/components → app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem}/InboxWelcome/index.tsx +0 -0
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+
3
+ import { ActionIcon } from '@lobehub/ui';
4
+ import { XIcon } from 'lucide-react';
5
+ import { ReactNode, memo } from 'react';
6
+
7
+ import SidebarHeader from '@/components/SidebarHeader';
8
+ import { useChatStore } from '@/store/chat';
9
+
10
+ const Header = memo<{ title: ReactNode }>(({ title }) => {
11
+ const [toggleInspector] = useChatStore((s) => [s.togglePortal]);
12
+
13
+ return (
14
+ <SidebarHeader
15
+ actions={
16
+ <ActionIcon
17
+ icon={XIcon}
18
+ onClick={() => {
19
+ toggleInspector(false);
20
+ }}
21
+ />
22
+ }
23
+ style={{ paddingBlock: 8, paddingInline: 8 }}
24
+ title={title}
25
+ />
26
+ );
27
+ });
28
+
29
+ export default Header;
@@ -4,13 +4,32 @@ import { memo } from 'react';
4
4
 
5
5
  import { Artifacts } from './Artifacts';
6
6
  import { FilePreview } from './FilePreview';
7
- import { HomeBody, HomeHeader } from './Home';
7
+ import { HomeBody, HomeTitle } from './Home';
8
8
  import { MessageDetail } from './MessageDetail';
9
9
  import { Plugins } from './Plugins';
10
+ import Header from './components/Header';
10
11
  import { PortalImpl } from './type';
11
12
 
12
13
  const items: PortalImpl[] = [MessageDetail, Artifacts, Plugins, FilePreview];
13
14
 
15
+ export const PortalTitle = memo(() => {
16
+ const enabledList: boolean[] = [];
17
+
18
+ for (const item of items) {
19
+ const enabled = item.useEnable();
20
+ enabledList.push(enabled);
21
+ }
22
+
23
+ for (const [i, element] of enabledList.entries()) {
24
+ const Title = items[i].Title;
25
+ if (element) {
26
+ return <Title />;
27
+ }
28
+ }
29
+
30
+ return <HomeTitle />;
31
+ });
32
+
14
33
  export const PortalHeader = memo(() => {
15
34
  const enabledList: boolean[] = [];
16
35
 
@@ -21,12 +40,12 @@ export const PortalHeader = memo(() => {
21
40
 
22
41
  for (const [i, element] of enabledList.entries()) {
23
42
  const Header = items[i].Header;
24
- if (element) {
43
+ if (element && Header) {
25
44
  return <Header />;
26
45
  }
27
46
  }
28
47
 
29
- return <HomeHeader />;
48
+ return <Header title={<PortalTitle />} />;
30
49
  });
31
50
 
32
51
  const PortalBody = memo(() => {
@@ -2,6 +2,8 @@ import { FC } from 'react';
2
2
 
3
3
  export interface PortalImpl {
4
4
  Body: FC;
5
- Header: FC;
5
+ Header?: FC;
6
+ Title: FC;
7
+ onClose?: () => void;
6
8
  useEnable: () => boolean;
7
9
  }
@@ -1,18 +1,17 @@
1
1
  import { memo } from 'react';
2
2
  import { Flexbox } from 'react-layout-kit';
3
3
 
4
+ import { ChatItem } from '@/features/Conversation';
4
5
  import { useChatStore } from '@/store/chat';
5
6
  import { chatSelectors } from '@/store/chat/selectors';
6
7
 
7
- import Item from '../ChatItem';
8
-
9
8
  const ChatList = memo(() => {
10
- const ids = useChatStore(chatSelectors.currentChatIDsWithGuideMessage);
9
+ const ids = useChatStore(chatSelectors.mainDisplayChatIDs);
11
10
 
12
11
  return (
13
12
  <Flexbox height={'100%'} style={{ paddingTop: 24, position: 'relative' }}>
14
13
  {ids.map((id, index) => (
15
- <Item id={id} index={index} key={id} />
14
+ <ChatItem id={id} index={index} key={id} />
16
15
  ))}
17
16
  </Flexbox>
18
17
  );
@@ -6,7 +6,6 @@ import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import PluginTag from '@/app/(main)/chat/(workspace)/features/PluginTag';
8
8
  import { ProductLogo } from '@/components/Branding';
9
- import ChatList from '@/features/Conversation/components/ChatList';
10
9
  import { useAgentStore } from '@/store/agent';
11
10
  import { agentSelectors } from '@/store/agent/selectors';
12
11
  import { useSessionStore } from '@/store/session';
@@ -14,6 +13,7 @@ import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selector
14
13
 
15
14
  import pkg from '../../../../package.json';
16
15
  import { useContainerStyles } from '../style';
16
+ import ChatList from './ChatList';
17
17
  import { useStyles } from './style';
18
18
  import { FieldType } from './type';
19
19
 
@@ -46,7 +46,7 @@ const ShareImage = memo(() => {
46
46
  ];
47
47
 
48
48
  const systemRole = useAgentStore(agentSelectors.currentAgentSystemRole);
49
- const messages = useChatStore(chatSelectors.currentChats, isEqual);
49
+ const messages = useChatStore(chatSelectors.activeBaseChats, isEqual);
50
50
  const data = generateMessages({ ...fieldValue, messages, systemRole });
51
51
  const content = JSON.stringify(data, null, 2);
52
52
 
@@ -62,7 +62,7 @@ const ShareText = memo(() => {
62
62
  ];
63
63
 
64
64
  const [systemRole] = useAgentStore((s) => [agentSelectors.currentAgentSystemRole(s)]);
65
- const messages = useChatStore(chatSelectors.currentChats, isEqual);
65
+ const messages = useChatStore(chatSelectors.activeBaseChats, isEqual);
66
66
  const topic = useChatStore(topicSelectors.currentActiveTopic, isEqual);
67
67
 
68
68
  const title = topic?.title || t('shareModal.exportTitle');
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import { useSearchParams } from 'next/navigation';
4
+ import Script from 'next/script';
5
+ import React, { memo } from 'react';
6
+
7
+ const Debug = memo(() => {
8
+ const searchParams = useSearchParams();
9
+
10
+ const debug = searchParams.get('debug');
11
+
12
+ return !!debug && <Script src="https://unpkg.com/react-scan/dist/auto.global.js" />;
13
+ });
14
+
15
+ export default Debug;
@@ -18,6 +18,7 @@ import { getAntdLocale } from '@/utils/locale';
18
18
  import { isMobileDevice } from '@/utils/server/responsive';
19
19
 
20
20
  import AppTheme from './AppTheme';
21
+ import Debug from './Debug';
21
22
  import Locale from './Locale';
22
23
  import QueryProvider from './Query';
23
24
  import StoreInitialization from './StoreInitialization';
@@ -85,6 +86,7 @@ const GlobalLayout = async ({ children }: PropsWithChildren) => {
85
86
  <StoreInitialization />
86
87
  </ServerConfigStoreProvider>
87
88
  <DebugUI />
89
+ <Debug />
88
90
  </AppTheme>
89
91
  </Locale>
90
92
  </StyleRegistry>
@@ -148,7 +148,7 @@ export const generateAIChat: StateCreator<
148
148
  // if autoCreateTopic is enabled, check to whether we need to create a topic
149
149
  if (!onlyAddUserMessage && !activeTopicId && agentConfig.enableAutoCreateTopic) {
150
150
  // check activeTopic and then auto create topic
151
- const chats = chatSelectors.currentChats(get());
151
+ const chats = chatSelectors.activeBaseChats(get());
152
152
 
153
153
  // we will add two messages (user and assistant), so the finial length should +2
154
154
  const featureLength = chats.length + 2;
@@ -207,7 +207,7 @@ export const generateAIChat: StateCreator<
207
207
  }
208
208
 
209
209
  // Get the current messages to generate AI response
210
- const messages = chatSelectors.currentChats(get());
210
+ const messages = chatSelectors.mainDisplayChats(get());
211
211
  const userFiles = chatSelectors.currentUserFiles(get()).map((f) => f.id);
212
212
 
213
213
  await internal_coreProcessMessage(messages, id, {
@@ -223,7 +223,7 @@ export const generateAIChat: StateCreator<
223
223
 
224
224
  // check activeTopic and then auto update topic title
225
225
  if (newTopicId) {
226
- const chats = chatSelectors.currentChats(get());
226
+ const chats = chatSelectors.activeBaseChats(get());
227
227
  await get().summaryTopicTitle(newTopicId, chats);
228
228
  return;
229
229
  }
@@ -231,7 +231,7 @@ export const generateAIChat: StateCreator<
231
231
  const topic = topicSelectors.currentActiveTopic(get());
232
232
 
233
233
  if (topic && !topic.title) {
234
- const chats = chatSelectors.currentChats(get());
234
+ const chats = chatSelectors.activeBaseChats(get());
235
235
  await get().summaryTopicTitle(topic.id, chats);
236
236
  }
237
237
  };
@@ -484,7 +484,7 @@ export const generateAIChat: StateCreator<
484
484
 
485
485
  internal_resendMessage: async (messageId, traceId) => {
486
486
  // 1. 构造所有相关的历史记录
487
- const chats = chatSelectors.currentChats(get());
487
+ const chats = chatSelectors.mainDisplayChats(get());
488
488
 
489
489
  const currentIndex = chats.findIndex((c) => c.id === messageId);
490
490
  if (currentIndex < 0) return;
@@ -128,7 +128,7 @@ export const chatMessage: StateCreator<
128
128
  if (message.tools) {
129
129
  const toolMessageIds = message.tools.flatMap((tool) => {
130
130
  const messages = chatSelectors
131
- .currentChats(get())
131
+ .activeBaseChats(get())
132
132
  .filter((m) => m.tool_call_id === tool.id);
133
133
 
134
134
  return messages.map((m) => m.id);
@@ -252,7 +252,7 @@ export const chatMessage: StateCreator<
252
252
 
253
253
  if (!activeId) return;
254
254
 
255
- const messages = messagesReducer(chatSelectors.currentChats(get()), payload);
255
+ const messages = messagesReducer(chatSelectors.activeBaseChats(get()), payload);
256
256
 
257
257
  const nextMap = { ...get().messagesMap, [chatSelectors.currentChatKey(get())]: messages };
258
258
 
@@ -232,62 +232,6 @@ describe('chatSelectors', () => {
232
232
  });
233
233
  });
234
234
 
235
- describe('currentChatsWithGuideMessage', () => {
236
- it('should return existing messages except tool message', () => {
237
- const state = merge(initialStore, {
238
- messagesMap: {
239
- [messageMapKey('someActiveId')]: mockMessages,
240
- },
241
- activeId: 'someActiveId',
242
- });
243
- const chats = chatSelectors.currentChatsWithGuideMessage({} as MetaData)(state);
244
- expect(chats).toEqual(mockedChats.slice(0, 2));
245
- });
246
-
247
- it('should add a guide message if the chat is brand new', () => {
248
- const state = merge(initialStore, { messages: [], activeId: 'someActiveId' });
249
- const metaData = { title: 'Mock Agent', description: 'Mock Description' };
250
-
251
- const chats = chatSelectors.currentChatsWithGuideMessage(metaData)(state);
252
-
253
- expect(chats).toHaveLength(1);
254
- expect(chats[0].content).toBeDefined();
255
- expect(chats[0].meta.avatar).toEqual(DEFAULT_INBOX_AVATAR);
256
- expect(chats[0].meta).toEqual(expect.objectContaining(metaData));
257
- });
258
-
259
- it('should use inbox message for INBOX_SESSION_ID', () => {
260
- const state = merge(initialStore, { messages: [], activeId: INBOX_SESSION_ID });
261
- const metaData = { title: 'Mock Agent', description: 'Mock Description' };
262
-
263
- const chats = chatSelectors.currentChatsWithGuideMessage(metaData)(state);
264
-
265
- expect(chats[0].content).toEqual(''); // Assuming translation returns a string containing this
266
- });
267
-
268
- it('should use agent default message for non-inbox sessions', () => {
269
- const state = merge(initialStore, { messages: [], activeId: 'someActiveId' });
270
- const metaData = { title: 'Mock Agent' };
271
-
272
- const chats = chatSelectors.currentChatsWithGuideMessage(metaData)(state);
273
-
274
- expect(chats[0].content).toMatch('agentDefaultMessage'); // Assuming translation returns a string containing this
275
- });
276
-
277
- it('should use agent default message without edit button for non-inbox sessions when agent is not editable', () => {
278
- act(() => {
279
- createServerConfigStore().setState({ featureFlags: { edit_agent: false } });
280
- });
281
-
282
- const state = merge(initialStore, { messages: [], activeId: 'someActiveId' });
283
- const metaData = { title: 'Mock Agent' };
284
-
285
- const chats = chatSelectors.currentChatsWithGuideMessage(metaData)(state);
286
-
287
- expect(chats[0].content).toMatch('agentDefaultMessageWithoutEdit');
288
- });
289
- });
290
-
291
235
  describe('chatsMessageString', () => {
292
236
  it('should concatenate the contents of all messages returned by currentChatsWithHistoryConfig', () => {
293
237
  // Prepare a state with a few messages
@@ -415,36 +359,6 @@ describe('chatSelectors', () => {
415
359
  });
416
360
  });
417
361
 
418
- describe('currentChatIDsWithGuideMessage', () => {
419
- it('should return message IDs including guide message for empty chat', () => {
420
- const state: Partial<ChatStore> = {
421
- activeId: 'test-id',
422
- messagesMap: {
423
- [messageMapKey('test-id')]: [],
424
- },
425
- };
426
- const result = chatSelectors.currentChatIDsWithGuideMessage(state as ChatStore);
427
- expect(result).toHaveLength(1);
428
- expect(result[0]).toBe('default');
429
- });
430
-
431
- it('should return existing message IDs for non-empty chat', () => {
432
- const messages = [
433
- { id: '1', role: 'user', content: 'Hello' },
434
- { id: '2', role: 'assistant', content: 'Hi' },
435
- ] as ChatMessage[];
436
- const state: Partial<ChatStore> = {
437
- activeId: 'test-id',
438
- messagesMap: {
439
- [messageMapKey('test-id')]: messages,
440
- },
441
- };
442
- const result = chatSelectors.currentChatIDsWithGuideMessage(state as ChatStore);
443
- expect(result).toHaveLength(2);
444
- expect(result).toEqual(['1', '2']);
445
- });
446
- });
447
-
448
362
  describe('isToolCallStreaming', () => {
449
363
  it('should return true when tool call is streaming for given message and index', () => {
450
364
  const state: Partial<ChatStore> = {
@@ -1,19 +1,13 @@
1
- import { t } from 'i18next';
2
-
3
- import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta';
1
+ import { DEFAULT_USER_AVATAR } from '@/const/meta';
4
2
  import { INBOX_SESSION_ID } from '@/const/session';
5
3
  import { useAgentStore } from '@/store/agent';
6
4
  import { agentSelectors } from '@/store/agent/selectors';
7
5
  import { messageMapKey } from '@/store/chat/utils/messageMapKey';
8
- import { featureFlagsSelectors } from '@/store/serverConfig';
9
- import { createServerConfigStore } from '@/store/serverConfig/store';
10
6
  import { useSessionStore } from '@/store/session';
11
7
  import { sessionMetaSelectors } from '@/store/session/selectors';
12
8
  import { useUserStore } from '@/store/user';
13
9
  import { userProfileSelectors } from '@/store/user/selectors';
14
10
  import { ChatFileItem, ChatMessage } from '@/types/message';
15
- import { MetaData } from '@/types/meta';
16
- import { merge } from '@/utils/merge';
17
11
 
18
12
  import { chatHelpers } from '../../helpers';
19
13
  import type { ChatStoreState } from '../../initialState';
@@ -38,8 +32,10 @@ const getMeta = (message: ChatMessage) => {
38
32
 
39
33
  const currentChatKey = (s: ChatStoreState) => messageMapKey(s.activeId, s.activeTopicId);
40
34
 
41
- // 当前激活的消息列表
42
- const currentChats = (s: ChatStoreState): ChatMessage[] => {
35
+ /**
36
+ * Current active raw message list, include thread messages
37
+ */
38
+ const activeBaseChats = (s: ChatStoreState): ChatMessage[] => {
43
39
  if (!s.activeId) return [];
44
40
 
45
41
  const messages = s.messagesMap[currentChatKey(s)] || [];
@@ -47,14 +43,54 @@ const currentChats = (s: ChatStoreState): ChatMessage[] => {
47
43
  return messages.map((i) => ({ ...i, meta: getMeta(i) }));
48
44
  };
49
45
 
46
+ /**
47
+ * 排除掉所有 tool 消息,在展示时需要使用
48
+ */
49
+ const activeBaseChatsWithoutTool = (s: ChatStoreState) => {
50
+ const messages = activeBaseChats(s);
51
+
52
+ return messages.filter((m) => m.role !== 'tool');
53
+ };
54
+
55
+ /**
56
+ * Main display chats
57
+ * 根据当前不同的状态,返回不同的消息列表
58
+ */
59
+ const mainDisplayChats = (s: ChatStoreState): ChatMessage[] => {
60
+ // 如果没有 activeThreadId,则返回所有的主消息
61
+ return activeBaseChats(s);
62
+ // const mains = activeBaseChats(s).filter((m) => !m.threadId);
63
+ // if (!s.activeThreadId) return mains;
64
+ //
65
+ // const thread = s.threadMaps[s.activeTopicId!]?.find((t) => t.id === s.activeThreadId);
66
+ //
67
+ // if (!thread) return mains;
68
+ //
69
+ // const sourceIndex = mains.findIndex((m) => m.id === thread.sourceMessageId);
70
+ // const sliced = mains.slice(0, sourceIndex + 1);
71
+ //
72
+ // return [...sliced, ...activeBaseChats(s).filter((m) => m.threadId === s.activeThreadId)];
73
+ };
74
+
75
+ const mainDisplayChatIDs = (s: ChatStoreState) => {
76
+ return mainDisplayChats(s).map((s) => s.id);
77
+ };
78
+
79
+ const currentChatsWithHistoryConfig = (s: ChatStoreState): ChatMessage[] => {
80
+ const chats = activeBaseChats(s);
81
+ const config = agentSelectors.currentAgentChatConfig(useAgentStore.getState());
82
+
83
+ return chatHelpers.getSlicedMessagesWithConfig(chats, config);
84
+ };
85
+
50
86
  const currentToolMessages = (s: ChatStoreState) => {
51
- const messages = currentChats(s);
87
+ const messages = activeBaseChats(s);
52
88
 
53
89
  return messages.filter((m) => m.role === 'tool');
54
90
  };
55
91
 
56
92
  const currentUserMessages = (s: ChatStoreState) => {
57
- const messages = currentChats(s);
93
+ const messages = activeBaseChats(s);
58
94
 
59
95
  return messages.filter((m) => m.role === 'user');
60
96
  };
@@ -68,84 +104,29 @@ const currentUserFiles = (s: ChatStoreState) => {
68
104
  .filter(Boolean) as ChatFileItem[];
69
105
  };
70
106
 
71
- const initTime = Date.now();
72
-
73
107
  const showInboxWelcome = (s: ChatStoreState): boolean => {
74
108
  const isInbox = s.activeId === INBOX_SESSION_ID;
75
109
  if (!isInbox) return false;
76
110
 
77
- const data = currentChats(s);
111
+ const data = activeBaseChats(s);
78
112
  return data.length === 0;
79
113
  };
80
114
 
81
- // Custom message for new assistant initialization
82
- const currentChatsWithGuideMessage =
83
- (meta: MetaData) =>
84
- (s: ChatStoreState): ChatMessage[] => {
85
- // skip tool message
86
- const data = currentChats(s).filter((m) => m.role !== 'tool');
87
-
88
- const { isAgentEditable } = featureFlagsSelectors(createServerConfigStore().getState());
89
-
90
- const isBrandNewChat = data.length === 0;
91
-
92
- if (!isBrandNewChat) return data;
93
-
94
- const [activeId, isInbox] = [s.activeId, s.activeId === INBOX_SESSION_ID];
95
-
96
- const inboxMsg = '';
97
- const agentSystemRoleMsg = t('agentDefaultMessageWithSystemRole', {
98
- name: meta.title || t('defaultAgent'),
99
- ns: 'chat',
100
- systemRole: meta.description,
101
- });
102
- const agentMsg = t(isAgentEditable ? 'agentDefaultMessage' : 'agentDefaultMessageWithoutEdit', {
103
- name: meta.title || t('defaultAgent'),
104
- ns: 'chat',
105
- url: `/chat/settings?session=${activeId}`,
106
- });
107
-
108
- const emptyInboxGuideMessage = {
109
- content: isInbox ? inboxMsg : !!meta.description ? agentSystemRoleMsg : agentMsg,
110
- createdAt: initTime,
111
- extra: {},
112
- id: 'default',
113
- meta: merge({ avatar: DEFAULT_INBOX_AVATAR }, meta),
114
- role: 'assistant',
115
- updatedAt: initTime,
116
- } as ChatMessage;
117
-
118
- return [emptyInboxGuideMessage];
119
- };
120
-
121
- const currentChatIDsWithGuideMessage = (s: ChatStoreState) => {
122
- const meta = sessionMetaSelectors.currentAgentMeta(useSessionStore.getState());
123
-
124
- return currentChatsWithGuideMessage(meta)(s).map((s) => s.id);
125
- };
126
-
127
- const currentChatsWithHistoryConfig = (s: ChatStoreState): ChatMessage[] => {
128
- const chats = currentChats(s);
129
- const config = agentSelectors.currentAgentChatConfig(useAgentStore.getState());
130
-
131
- return chatHelpers.getSlicedMessagesWithConfig(chats, config);
132
- };
133
-
134
115
  const chatsMessageString = (s: ChatStoreState): string => {
135
116
  const chats = currentChatsWithHistoryConfig(s);
136
117
  return chats.map((m) => m.content).join('');
137
118
  };
138
119
 
139
120
  const getMessageById = (id: string) => (s: ChatStoreState) =>
140
- chatHelpers.getMessageById(currentChats(s), id);
121
+ chatHelpers.getMessageById(activeBaseChats(s), id);
141
122
 
142
123
  const getMessageByToolCallId = (id: string) => (s: ChatStoreState) => {
143
- const messages = currentChats(s);
124
+ const messages = activeBaseChats(s);
144
125
  return messages.find((m) => m.tool_call_id === id);
145
126
  };
146
127
  const getTraceIdByMessageId = (id: string) => (s: ChatStoreState) => getMessageById(id)(s)?.traceId;
147
128
 
148
- const latestMessage = (s: ChatStoreState) => currentChats(s).at(-1);
129
+ const latestMessage = (s: ChatStoreState) => activeBaseChats(s).at(-1);
149
130
 
150
131
  const currentChatLoadingState = (s: ChatStoreState) => !s.messagesInit;
151
132
 
@@ -187,12 +168,11 @@ const isSendButtonDisabledByMessage = (s: ChatStoreState) =>
187
168
  isInRAGFlow(s);
188
169
 
189
170
  export const chatSelectors = {
171
+ activeBaseChats,
172
+ activeBaseChatsWithoutTool,
190
173
  chatsMessageString,
191
- currentChatIDsWithGuideMessage,
192
174
  currentChatKey,
193
175
  currentChatLoadingState,
194
- currentChats,
195
- currentChatsWithGuideMessage,
196
176
  currentChatsWithHistoryConfig,
197
177
  currentToolMessages,
198
178
  currentUserFiles,
@@ -211,5 +191,7 @@ export const chatSelectors = {
211
191
  isSendButtonDisabledByMessage,
212
192
  isToolCallStreaming,
213
193
  latestMessage,
194
+ mainDisplayChatIDs,
195
+ mainDisplayChats,
214
196
  showInboxWelcome,
215
197
  };
@@ -149,7 +149,7 @@ describe('ChatPluginAction', () => {
149
149
  it('should update message content and trigger the ai message', async () => {
150
150
  // 设置模拟函数的返回值
151
151
  const mockCurrentChats: any[] = [];
152
- vi.spyOn(chatSelectors, 'currentChats').mockReturnValue(mockCurrentChats);
152
+ vi.spyOn(chatSelectors, 'activeBaseChats').mockReturnValue(mockCurrentChats);
153
153
 
154
154
  // 设置初始状态
155
155
  const initialState = {
@@ -184,7 +184,7 @@ describe('ChatPluginAction', () => {
184
184
  it('should update message content and not trigger ai message', async () => {
185
185
  // 设置模拟函数的返回值
186
186
  const mockCurrentChats: any[] = [];
187
- vi.spyOn(chatSelectors, 'currentChats').mockReturnValue(mockCurrentChats);
187
+ vi.spyOn(chatSelectors, 'activeBaseChats').mockReturnValue(mockCurrentChats);
188
188
 
189
189
  // 设置初始状态
190
190
  const initialState = {
@@ -202,7 +202,7 @@ export const chatPlugin: StateCreator<
202
202
 
203
203
  triggerAIMessage: async ({ parentId, traceId }) => {
204
204
  const { internal_coreProcessMessage } = get();
205
- const chats = chatSelectors.currentChats(get());
205
+ const chats = chatSelectors.activeBaseChats(get());
206
206
  await internal_coreProcessMessage(chats, parentId ?? chats.at(-1)!.id, { traceId });
207
207
  },
208
208
 
@@ -79,7 +79,7 @@ export const chatTopic: StateCreator<
79
79
  createTopic: async () => {
80
80
  const { activeId, internal_createTopic } = get();
81
81
 
82
- const messages = chatSelectors.currentChats(get());
82
+ const messages = chatSelectors.activeBaseChats(get());
83
83
 
84
84
  set({ creatingTopic: true }, false, n('creatingTopic/start'));
85
85
  const topicId = await internal_createTopic({
@@ -94,7 +94,7 @@ export const chatTopic: StateCreator<
94
94
 
95
95
  saveToTopic: async () => {
96
96
  // if there is no message, stop
97
- const messages = chatSelectors.currentChats(get());
97
+ const messages = chatSelectors.activeBaseChats(get());
98
98
  if (messages.length === 0) return;
99
99
 
100
100
  const { activeId, summaryTopicTitle, internal_createTopic } = get();