@lobehub/chat 1.32.3 → 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.
- package/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/locales/ar/models.json +12 -0
- package/locales/ar/providers.json +3 -0
- package/locales/bg-BG/models.json +12 -0
- package/locales/bg-BG/providers.json +3 -0
- package/locales/de-DE/models.json +12 -0
- package/locales/de-DE/providers.json +3 -0
- package/locales/en-US/models.json +12 -0
- package/locales/en-US/providers.json +3 -0
- package/locales/es-ES/models.json +12 -0
- package/locales/es-ES/providers.json +3 -0
- package/locales/fa-IR/models.json +12 -0
- package/locales/fa-IR/providers.json +3 -0
- package/locales/fr-FR/models.json +12 -0
- package/locales/fr-FR/providers.json +3 -0
- package/locales/it-IT/models.json +12 -0
- package/locales/it-IT/providers.json +3 -0
- package/locales/ja-JP/models.json +12 -0
- package/locales/ja-JP/providers.json +3 -0
- package/locales/ko-KR/models.json +12 -0
- package/locales/ko-KR/providers.json +3 -0
- package/locales/nl-NL/models.json +12 -0
- package/locales/nl-NL/providers.json +3 -0
- package/locales/pl-PL/models.json +12 -0
- package/locales/pl-PL/providers.json +3 -0
- package/locales/pt-BR/models.json +12 -0
- package/locales/pt-BR/providers.json +3 -0
- package/locales/ru-RU/models.json +12 -0
- package/locales/ru-RU/providers.json +3 -0
- package/locales/tr-TR/models.json +12 -0
- package/locales/tr-TR/providers.json +3 -0
- package/locales/vi-VN/models.json +12 -0
- package/locales/vi-VN/providers.json +3 -0
- package/locales/zh-CN/models.json +12 -0
- package/locales/zh-CN/providers.json +3 -0
- package/locales/zh-TW/models.json +12 -0
- package/locales/zh-TW/providers.json +3 -0
- package/package.json +2 -2
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +39 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +20 -14
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx +44 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/index.tsx +17 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +1 -13
- package/src/app/metadata.ts +2 -1
- package/src/components/BrandWatermark/index.tsx +1 -0
- package/src/const/message.ts +1 -1
- package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +9 -13
- package/src/features/Conversation/components/ChatItem/index.tsx +183 -209
- package/src/features/Conversation/components/VirtualizedList/index.tsx +75 -84
- package/src/features/Conversation/index.ts +0 -1
- package/src/features/Portal/Artifacts/index.ts +1 -1
- package/src/features/Portal/FilePreview/index.ts +1 -1
- package/src/features/Portal/Home/{Header.tsx → Title.tsx} +2 -2
- package/src/features/Portal/Home/index.ts +1 -1
- package/src/features/Portal/MessageDetail/index.ts +1 -1
- package/src/features/Portal/Plugins/index.ts +1 -1
- package/src/features/Portal/components/Header.tsx +29 -0
- package/src/features/Portal/router.tsx +22 -3
- package/src/features/Portal/type.ts +3 -1
- package/src/features/{Conversation/components → ShareModal/ShareImage}/ChatList/index.tsx +3 -4
- package/src/features/ShareModal/ShareImage/Preview.tsx +1 -1
- package/src/features/ShareModal/ShareJSON/index.tsx +1 -1
- package/src/features/ShareModal/ShareText/index.tsx +1 -1
- package/src/layout/GlobalProvider/Debug.tsx +15 -0
- package/src/layout/GlobalProvider/index.tsx +4 -2
- package/src/locales/resources.ts +5 -2
- package/src/server/ld.ts +2 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -5
- package/src/store/chat/slices/message/action.ts +2 -2
- package/src/store/chat/slices/message/selectors.test.ts +0 -86
- package/src/store/chat/slices/message/selectors.ts +55 -73
- package/src/store/chat/slices/plugin/action.test.ts +2 -2
- package/src/store/chat/slices/plugin/action.ts +1 -1
- package/src/store/chat/slices/topic/action.ts +2 -2
- /package/src/{features/Conversation/components → app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem}/InboxWelcome/AgentsSuggest.tsx +0 -0
- /package/src/{features/Conversation/components → app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem}/InboxWelcome/QuestionSuggest.tsx +0 -0
- /package/src/{features/Conversation/components → app/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem}/InboxWelcome/index.tsx +0 -0
@@ -4,7 +4,7 @@ import { Typography } from 'antd';
|
|
4
4
|
import { memo } from 'react';
|
5
5
|
import { useTranslation } from 'react-i18next';
|
6
6
|
|
7
|
-
const
|
7
|
+
const Title = memo(() => {
|
8
8
|
const { t } = useTranslation('portal');
|
9
9
|
|
10
10
|
return (
|
@@ -14,4 +14,4 @@ const Header = memo(() => {
|
|
14
14
|
);
|
15
15
|
});
|
16
16
|
|
17
|
-
export default
|
17
|
+
export default Title;
|
@@ -1,2 +1,2 @@
|
|
1
1
|
export { default as HomeBody } from './Body';
|
2
|
-
export { default as
|
2
|
+
export { default as HomeTitle } from './Title';
|
@@ -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,
|
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 <
|
48
|
+
return <Header title={<PortalTitle />} />;
|
30
49
|
});
|
31
50
|
|
32
51
|
const PortalBody = memo(() => {
|
@@ -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.
|
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
|
-
<
|
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.
|
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.
|
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;
|
@@ -4,7 +4,7 @@ import { resolveAcceptLanguage } from 'resolve-accept-language';
|
|
4
4
|
|
5
5
|
import { appEnv } from '@/config/app';
|
6
6
|
import { getServerFeatureFlagsValue } from '@/config/featureFlags';
|
7
|
-
import { LOBE_LOCALE_COOKIE } from '@/const/locale';
|
7
|
+
import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
|
8
8
|
import {
|
9
9
|
LOBE_THEME_APPEARANCE,
|
10
10
|
LOBE_THEME_NEUTRAL_COLOR,
|
@@ -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';
|
@@ -36,7 +37,7 @@ const parserFallbackLang = async () => {
|
|
36
37
|
header.get('accept-language') || '',
|
37
38
|
// Invalid locale identifier 'ar'. A valid locale should follow the BCP 47 'language-country' format.
|
38
39
|
locales.map((locale) => (locale === 'ar' ? 'ar-EG' : locale)),
|
39
|
-
|
40
|
+
DEFAULT_LANG,
|
40
41
|
);
|
41
42
|
// if match the ar-EG then fallback to ar
|
42
43
|
if (fallbackLang === 'ar-EG') fallbackLang = 'ar';
|
@@ -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>
|
package/src/locales/resources.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import { DEFAULT_LANG } from '@/const/locale';
|
2
|
+
|
1
3
|
import resources from './default';
|
2
4
|
|
3
5
|
export const locales = [
|
@@ -23,9 +25,10 @@ export type NS = keyof DefaultResources;
|
|
23
25
|
export type Locales = (typeof locales)[number];
|
24
26
|
|
25
27
|
export const normalizeLocale = (locale?: string): string => {
|
26
|
-
if (!locale) return
|
28
|
+
if (!locale) return DEFAULT_LANG;
|
27
29
|
|
28
30
|
if (locale.startsWith('ar')) return 'ar';
|
31
|
+
if (locale.startsWith('fa')) return 'fa-IR';
|
29
32
|
|
30
33
|
if (locale.startsWith('cn')) return 'zh-CN';
|
31
34
|
|
@@ -35,7 +38,7 @@ export const normalizeLocale = (locale?: string): string => {
|
|
35
38
|
}
|
36
39
|
}
|
37
40
|
|
38
|
-
return
|
41
|
+
return DEFAULT_LANG;
|
39
42
|
};
|
40
43
|
|
41
44
|
type LocaleOptions = {
|
package/src/server/ld.ts
CHANGED
@@ -200,7 +200,7 @@ export class Ld {
|
|
200
200
|
'@id': this.getId(fixedUrl, '#primaryimage'),
|
201
201
|
'@type': 'ImageObject',
|
202
202
|
'contentUrl': image,
|
203
|
-
'inLanguage':
|
203
|
+
'inLanguage': DEFAULT_LANG,
|
204
204
|
'url': image,
|
205
205
|
};
|
206
206
|
}
|
@@ -210,7 +210,7 @@ export class Ld {
|
|
210
210
|
'@id': this.getId(OFFICIAL_URL, '#website'),
|
211
211
|
'@type': 'WebSite',
|
212
212
|
'description': pkg.description,
|
213
|
-
'inLanguage':
|
213
|
+
'inLanguage': DEFAULT_LANG,
|
214
214
|
'name': BRANDING_NAME,
|
215
215
|
'publisher': {
|
216
216
|
'@id': this.getId(OFFICIAL_URL, '#organization'),
|
@@ -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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
.
|
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.
|
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 {
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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(
|
121
|
+
chatHelpers.getMessageById(activeBaseChats(s), id);
|
141
122
|
|
142
123
|
const getMessageByToolCallId = (id: string) => (s: ChatStoreState) => {
|
143
|
-
const messages =
|
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) =>
|
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
|
};
|