@lobehub/chat 0.28.0 → 0.30.0
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/.eslintrc.js +3 -0
- package/CHANGELOG.md +54 -0
- package/package.json +1 -1
- package/src/const/message.ts +2 -0
- package/src/features/SideBar/BottomAction.tsx +1 -1
- package/src/helpers/export.ts +62 -0
- package/src/hooks/useExportConfig.ts +3 -66
- package/src/locales/default/common.ts +2 -0
- package/src/locales/default/setting.ts +4 -3
- package/src/migrations/index.ts +2 -2
- package/src/pages/chat/SessionList/List/SessionItem.tsx +44 -52
- package/src/pages/chat/SessionList/List/style.ts +3 -0
- package/src/pages/chat/[id]/Config/SideBar.tsx +1 -1
- package/src/pages/chat/[id]/Config/Topic/DefaultContent.tsx +29 -0
- package/src/pages/chat/[id]/Config/Topic/TopicContent.tsx +151 -0
- package/src/pages/chat/[id]/Config/Topic/TopicItem.tsx +70 -0
- package/src/pages/chat/[id]/Config/{Topic.tsx → Topic/index.tsx} +1 -8
- package/src/pages/chat/[id]/Config/index.tsx +3 -5
- package/src/pages/chat/[id]/Conversation/Input/index.tsx +7 -10
- package/src/pages/chat/[id]/Header.tsx +3 -2
- package/src/pages/chat/[id]/edit/Header.tsx +14 -1
- package/src/pages/setting/SettingForm.tsx +34 -29
- package/src/store/session/slices/agentConfig/action.ts +0 -11
- package/src/store/session/slices/agentConfig/initialState.ts +0 -4
- package/src/store/session/slices/chat/actions/message.ts +2 -2
- package/src/store/session/slices/chat/actions/topic.ts +28 -2
- package/src/store/session/slices/chat/initialState.ts +1 -0
- package/src/store/session/slices/chat/reducers/topic.ts +23 -1
- package/src/store/session/slices/chat/selectors/index.ts +2 -1
- package/src/store/session/slices/chat/selectors/topic.ts +8 -0
- package/src/store/session/slices/session/action.ts +8 -5
- package/src/store/session/slices/session/selectors/export.ts +23 -0
- package/src/store/session/slices/session/selectors/index.ts +6 -2
- package/src/store/session/slices/session/selectors/list.ts +3 -2
- package/src/store/settings/action.ts +11 -1
- package/src/store/settings/initialState.ts +2 -0
- package/src/store/settings/selectors.ts +10 -0
- package/src/types/exportConfig.ts +34 -14
- package/src/utils/config.ts +54 -1
- package/src/pages/chat/[id]/Config/TopicItem.tsx +0 -136
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createStyles } from 'antd-style';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
import { shallow } from 'zustand/shallow';
|
|
5
|
+
|
|
6
|
+
import { useSessionStore } from '@/store/session';
|
|
7
|
+
|
|
8
|
+
import DefaultContent from './DefaultContent';
|
|
9
|
+
import TopicContent from './TopicContent';
|
|
10
|
+
|
|
11
|
+
const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
|
12
|
+
active: css`
|
|
13
|
+
background: ${isDarkMode ? token.colorFillTertiary : token.colorFillSecondary};
|
|
14
|
+
transition: background 200ms ${token.motionEaseOut};
|
|
15
|
+
|
|
16
|
+
&:hover {
|
|
17
|
+
background: ${token.colorFill};
|
|
18
|
+
}
|
|
19
|
+
`,
|
|
20
|
+
container: css`
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
padding: 12px 8px;
|
|
23
|
+
border-radius: ${token.borderRadius}px;
|
|
24
|
+
|
|
25
|
+
.topic-more {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transition: opacity 400ms ${token.motionEaseOut};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&:hover {
|
|
31
|
+
background: ${token.colorFillSecondary};
|
|
32
|
+
|
|
33
|
+
.topic-more {
|
|
34
|
+
opacity: 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
split: css`
|
|
39
|
+
border-bottom: 1px solid ${token.colorSplit};
|
|
40
|
+
`,
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
export interface ConfigCellProps {
|
|
44
|
+
active?: boolean;
|
|
45
|
+
fav?: boolean;
|
|
46
|
+
id?: string;
|
|
47
|
+
title: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const TopicItem = memo<ConfigCellProps>(({ title, active, id, fav }) => {
|
|
51
|
+
const { styles, cx } = useStyles();
|
|
52
|
+
|
|
53
|
+
const [toggleTopic] = useSessionStore((s) => [s.toggleTopic], shallow);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Flexbox
|
|
57
|
+
align={'center'}
|
|
58
|
+
className={cx(styles.container, active && styles.active)}
|
|
59
|
+
distribution={'space-between'}
|
|
60
|
+
horizontal
|
|
61
|
+
onClick={() => {
|
|
62
|
+
toggleTopic(id);
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{!id ? <DefaultContent /> : <TopicContent fav={fav} id={id} title={title} />}
|
|
66
|
+
</Flexbox>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export default TopicItem;
|
|
@@ -13,14 +13,7 @@ export const Topic = () => {
|
|
|
13
13
|
<TopicItem active={!activeTopicId} fav={false} title={'默认话题'} />
|
|
14
14
|
|
|
15
15
|
{topics.map(({ id, favorite, title }) => (
|
|
16
|
-
<TopicItem
|
|
17
|
-
active={activeTopicId === id}
|
|
18
|
-
fav={favorite}
|
|
19
|
-
id={id}
|
|
20
|
-
key={id}
|
|
21
|
-
showFav
|
|
22
|
-
title={title}
|
|
23
|
-
/>
|
|
16
|
+
<TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
|
|
24
17
|
))}
|
|
25
18
|
</Flexbox>
|
|
26
19
|
);
|
|
@@ -5,7 +5,7 @@ import { shallow } from 'zustand/shallow';
|
|
|
5
5
|
|
|
6
6
|
import HeaderSpacing from '@/components/HeaderSpacing';
|
|
7
7
|
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
|
8
|
-
import {
|
|
8
|
+
import { useSettings } from '@/store/settings';
|
|
9
9
|
|
|
10
10
|
import SideBar from './SideBar';
|
|
11
11
|
|
|
@@ -23,8 +23,8 @@ const useStyles = createStyles(({ cx, css, token, stylish }) => ({
|
|
|
23
23
|
|
|
24
24
|
const Config = () => {
|
|
25
25
|
const { styles } = useStyles();
|
|
26
|
-
const [showAgentSettings, toggleConfig] =
|
|
27
|
-
(s) => [s.
|
|
26
|
+
const [showAgentSettings, toggleConfig] = useSettings(
|
|
27
|
+
(s) => [s.showAgentConfig, s.toggleAgentPanel],
|
|
28
28
|
shallow,
|
|
29
29
|
);
|
|
30
30
|
|
|
@@ -32,11 +32,9 @@ const Config = () => {
|
|
|
32
32
|
<DraggablePanel
|
|
33
33
|
className={styles.drawer}
|
|
34
34
|
expand={showAgentSettings}
|
|
35
|
-
maxWidth={CHAT_SIDEBAR_WIDTH}
|
|
36
35
|
minWidth={CHAT_SIDEBAR_WIDTH}
|
|
37
36
|
onExpandChange={toggleConfig}
|
|
38
37
|
placement={'right'}
|
|
39
|
-
resize={false}
|
|
40
38
|
>
|
|
41
39
|
<HeaderSpacing />
|
|
42
40
|
<DraggablePanelContainer style={{ flex: 'none', minWidth: CHAT_SIDEBAR_WIDTH }}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChatInputArea, DraggablePanel, Icon, Tooltip } from '@lobehub/ui';
|
|
2
2
|
import { Button } from 'antd';
|
|
3
3
|
import { LucideGalleryVerticalEnd } from 'lucide-react';
|
|
4
|
-
import { memo,
|
|
4
|
+
import { memo, useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { shallow } from 'zustand/shallow';
|
|
7
7
|
|
|
@@ -18,18 +18,15 @@ const ChatInput = () => {
|
|
|
18
18
|
const [text, setText] = useState('');
|
|
19
19
|
|
|
20
20
|
const [inputHeight] = useSettings((s) => [s.inputHeight], shallow);
|
|
21
|
-
const [sendMessage, saveToTopic] = useSessionStore(
|
|
22
|
-
(s) => [s.createOrSendMsg, s.saveToTopic],
|
|
21
|
+
const [sendMessage, hasTopic, saveToTopic] = useSessionStore(
|
|
22
|
+
(s) => [s.createOrSendMsg, !!s.activeTopicId, s.saveToTopic],
|
|
23
23
|
shallow,
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
const footer =
|
|
27
|
-
()
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
</Tooltip>
|
|
31
|
-
),
|
|
32
|
-
[],
|
|
26
|
+
const footer = hasTopic ? null : (
|
|
27
|
+
<Tooltip title={t('topic.saveCurrentMessages')}>
|
|
28
|
+
<Button icon={<Icon icon={LucideGalleryVerticalEnd} />} onClick={saveToTopic} />
|
|
29
|
+
</Tooltip>
|
|
33
30
|
);
|
|
34
31
|
|
|
35
32
|
return (
|
|
@@ -8,6 +8,7 @@ import { shallow } from 'zustand/shallow';
|
|
|
8
8
|
|
|
9
9
|
import HeaderTitle from '@/components/HeaderTitle';
|
|
10
10
|
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
11
|
+
import { useSettings } from '@/store/settings';
|
|
11
12
|
|
|
12
13
|
const Header = memo(() => {
|
|
13
14
|
const { t } = useTranslation('common');
|
|
@@ -24,8 +25,8 @@ const Header = memo(() => {
|
|
|
24
25
|
shallow,
|
|
25
26
|
);
|
|
26
27
|
|
|
27
|
-
const [showAgentSettings, toggleConfig] =
|
|
28
|
-
(s) => [s.
|
|
28
|
+
const [showAgentSettings, toggleConfig] = useSettings(
|
|
29
|
+
(s) => [s.showAgentConfig, s.toggleAgentPanel],
|
|
29
30
|
shallow,
|
|
30
31
|
);
|
|
31
32
|
|
|
@@ -6,19 +6,32 @@ import { memo, useMemo } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import HeaderTitle from '@/components/HeaderTitle';
|
|
9
|
+
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
10
|
+
import { useSessionStore } from '@/store/session';
|
|
9
11
|
|
|
10
12
|
const Header = memo(() => {
|
|
11
13
|
const { t } = useTranslation('setting');
|
|
14
|
+
const id = useSessionStore((s) => s.activeId);
|
|
12
15
|
|
|
13
|
-
const items
|
|
16
|
+
const items = useMemo<MenuProps['items']>(
|
|
14
17
|
() => [
|
|
15
18
|
{
|
|
16
19
|
key: 'agent',
|
|
17
20
|
label: <div>{t('exportType.agent', { ns: 'common' })}</div>,
|
|
21
|
+
onClick: () => {
|
|
22
|
+
if (!id) return;
|
|
23
|
+
|
|
24
|
+
exportSingleAgent(id);
|
|
25
|
+
},
|
|
18
26
|
},
|
|
19
27
|
{
|
|
20
28
|
key: 'agentWithMessage',
|
|
21
29
|
label: <div>{t('exportType.agentWithMessage', { ns: 'common' })}</div>,
|
|
30
|
+
onClick: () => {
|
|
31
|
+
if (!id) return;
|
|
32
|
+
|
|
33
|
+
exportSingleSession(id);
|
|
34
|
+
},
|
|
22
35
|
},
|
|
23
36
|
],
|
|
24
37
|
[],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Form, type ItemGroup, ThemeSwitch } from '@lobehub/ui';
|
|
2
|
-
import { Form as AntForm, Button, Input,
|
|
2
|
+
import { Form as AntForm, App, Button, Input, Select, Switch } from 'antd';
|
|
3
3
|
import isEqual from 'fast-deep-equal';
|
|
4
4
|
import { debounce } from 'lodash-es';
|
|
5
5
|
import { AppWindow, BrainCog, MessagesSquare, Palette, Webhook } from 'lucide-react';
|
|
@@ -11,6 +11,7 @@ import { shallow } from 'zustand/shallow';
|
|
|
11
11
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
12
12
|
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
13
13
|
import { options } from '@/locales/options';
|
|
14
|
+
import { useSessionStore } from '@/store/session';
|
|
14
15
|
import { settingsSelectors, useSettings } from '@/store/settings';
|
|
15
16
|
import { DEFAULT_SETTINGS } from '@/store/settings/initialState';
|
|
16
17
|
import { LanguageModel } from '@/types/llm';
|
|
@@ -27,6 +28,7 @@ type SettingItemGroup = ItemGroup & {
|
|
|
27
28
|
const SettingForm = memo(() => {
|
|
28
29
|
const { t } = useTranslation('setting');
|
|
29
30
|
const [form] = AntForm.useForm();
|
|
31
|
+
const clearSessions = useSessionStore((s) => s.clearSessions);
|
|
30
32
|
const settings = useSettings(settingsSelectors.currentSettings, isEqual);
|
|
31
33
|
const { setThemeMode, setSettings, resetSettings } = useSettings(
|
|
32
34
|
(s) => ({
|
|
@@ -37,14 +39,35 @@ const SettingForm = memo(() => {
|
|
|
37
39
|
shallow,
|
|
38
40
|
);
|
|
39
41
|
|
|
42
|
+
const { message, modal } = App.useApp();
|
|
40
43
|
const handleReset = useCallback(() => {
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
modal.confirm({
|
|
45
|
+
cancelText: t('cancel', { ns: 'common' }),
|
|
46
|
+
centered: true,
|
|
47
|
+
okButtonProps: { danger: true },
|
|
48
|
+
okText: t('ok', { ns: 'common' }),
|
|
49
|
+
onOk: () => {
|
|
50
|
+
resetSettings();
|
|
51
|
+
form.setFieldsValue(DEFAULT_SETTINGS);
|
|
52
|
+
},
|
|
53
|
+
title: t('danger.reset.confirm'),
|
|
54
|
+
});
|
|
43
55
|
}, []);
|
|
44
56
|
|
|
45
57
|
const handleClear = useCallback(() => {
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
modal.confirm({
|
|
59
|
+
cancelText: t('cancel', { ns: 'common' }),
|
|
60
|
+
centered: true,
|
|
61
|
+
okButtonProps: {
|
|
62
|
+
danger: true,
|
|
63
|
+
},
|
|
64
|
+
okText: t('ok', { ns: 'common' }),
|
|
65
|
+
onOk: () => {
|
|
66
|
+
clearSessions();
|
|
67
|
+
message.success(t('danger.clear.success'));
|
|
68
|
+
},
|
|
69
|
+
title: t('danger.clear.confirm'),
|
|
70
|
+
});
|
|
48
71
|
}, []);
|
|
49
72
|
|
|
50
73
|
const theme: SettingItemGroup = useMemo(
|
|
@@ -240,18 +263,9 @@ const SettingForm = memo(() => {
|
|
|
240
263
|
},
|
|
241
264
|
{
|
|
242
265
|
children: (
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
okButtonProps={{ danger: true }}
|
|
247
|
-
okText={t('ok', { ns: 'common' })}
|
|
248
|
-
onConfirm={handleReset}
|
|
249
|
-
title={t('danger.reset.confirm')}
|
|
250
|
-
>
|
|
251
|
-
<Button danger type="primary">
|
|
252
|
-
{t('danger.reset.action')}
|
|
253
|
-
</Button>
|
|
254
|
-
</Popconfirm>
|
|
266
|
+
<Button danger onClick={handleReset} type="primary">
|
|
267
|
+
{t('danger.reset.action')}
|
|
268
|
+
</Button>
|
|
255
269
|
),
|
|
256
270
|
desc: t('danger.reset.title'),
|
|
257
271
|
label: t('danger.reset.desc'),
|
|
@@ -259,18 +273,9 @@ const SettingForm = memo(() => {
|
|
|
259
273
|
},
|
|
260
274
|
{
|
|
261
275
|
children: (
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
okButtonProps={{ danger: true }}
|
|
266
|
-
okText={t('ok', { ns: 'common' })}
|
|
267
|
-
onConfirm={handleClear}
|
|
268
|
-
title={t('danger.clear.confirm')}
|
|
269
|
-
>
|
|
270
|
-
<Button danger type="primary">
|
|
271
|
-
{t('danger.clear.action')}
|
|
272
|
-
</Button>
|
|
273
|
-
</Popconfirm>
|
|
276
|
+
<Button danger onClick={handleClear} type="primary">
|
|
277
|
+
{t('danger.clear.action')}
|
|
278
|
+
</Button>
|
|
274
279
|
),
|
|
275
280
|
desc: t('danger.clear.title'),
|
|
276
281
|
label: t('danger.clear.desc'),
|
|
@@ -46,11 +46,6 @@ export interface AgentAction {
|
|
|
46
46
|
internalUpdateAgentMeta: (id: string) => any;
|
|
47
47
|
toggleAgentPlugin: (pluginId: string) => void;
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* 切换配置
|
|
51
|
-
* @param showPanel - 是否显示面板,默认为 true
|
|
52
|
-
*/
|
|
53
|
-
toggleConfig: (showPanel?: boolean) => void;
|
|
54
49
|
/**
|
|
55
50
|
* 更新代理配置
|
|
56
51
|
* @param config - 部分 LobeAgentConfig 的配置
|
|
@@ -214,12 +209,6 @@ export const createAgentSlice: StateCreator<
|
|
|
214
209
|
get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
|
|
215
210
|
},
|
|
216
211
|
|
|
217
|
-
toggleConfig: (newValue) => {
|
|
218
|
-
const showAgentSettings = typeof newValue === 'boolean' ? newValue : !get().showAgentSettings;
|
|
219
|
-
|
|
220
|
-
set({ showAgentSettings });
|
|
221
|
-
},
|
|
222
|
-
|
|
223
212
|
updateAgentConfig: (config) => {
|
|
224
213
|
const { activeId } = get();
|
|
225
214
|
const session = sessionSelectors.currentSession(get());
|
|
@@ -6,8 +6,6 @@ export type SessionLoadingState = Record<Partial<keyof MetaData>, boolean>;
|
|
|
6
6
|
|
|
7
7
|
export interface AgentConfigState {
|
|
8
8
|
autocompleteLoading: SessionLoadingState;
|
|
9
|
-
|
|
10
|
-
showAgentSettings: boolean;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
export const initialLobeAgentConfig: LobeAgentConfig = {
|
|
@@ -32,6 +30,4 @@ export const initialAgentConfigState: AgentConfigState = {
|
|
|
32
30
|
tag: false,
|
|
33
31
|
title: false,
|
|
34
32
|
},
|
|
35
|
-
|
|
36
|
-
showAgentSettings: true,
|
|
37
33
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StateCreator } from 'zustand/vanilla';
|
|
2
2
|
|
|
3
|
+
import { LOADING_FLAT } from '@/const/message';
|
|
3
4
|
import { fetchChatModel } from '@/services/chatModel';
|
|
4
5
|
import { fetchPlugin } from '@/services/plugin';
|
|
5
6
|
import { SessionStore, agentSelectors, chatSelectors, sessionSelectors } from '@/store/session';
|
|
@@ -10,8 +11,6 @@ import { nanoid } from '@/utils/uuid';
|
|
|
10
11
|
|
|
11
12
|
import { MessageDispatch, messagesReducer } from '../reducers/message';
|
|
12
13
|
|
|
13
|
-
const LOADING_FLAT = '...';
|
|
14
|
-
|
|
15
14
|
/**
|
|
16
15
|
* 聊天操作
|
|
17
16
|
*/
|
|
@@ -216,6 +215,7 @@ export const chatMessage: StateCreator<
|
|
|
216
215
|
|
|
217
216
|
await realFetchAIResponse(histories, latestMsg.id);
|
|
218
217
|
},
|
|
218
|
+
|
|
219
219
|
sendMessage: async (message) => {
|
|
220
220
|
const { dispatchMessage, realFetchAIResponse, autocompleteSessionAgentMeta, activeTopicId } =
|
|
221
221
|
get();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { StateCreator } from 'zustand/vanilla';
|
|
2
2
|
|
|
3
|
+
import { LOADING_FLAT } from '@/const/message';
|
|
3
4
|
import { promptSummaryTitle } from '@/prompts/chat';
|
|
4
|
-
import { SessionStore, chatSelectors, sessionSelectors } from '@/store/session';
|
|
5
|
+
import { SessionStore, chatSelectors, sessionSelectors, topicSelectors } from '@/store/session';
|
|
5
6
|
import { fetchPresetTaskResult } from '@/utils/fetch';
|
|
6
7
|
import { nanoid } from '@/utils/uuid';
|
|
7
8
|
|
|
@@ -13,6 +14,11 @@ export interface ChatTopicAction {
|
|
|
13
14
|
* @param payload - 要分发的主题
|
|
14
15
|
*/
|
|
15
16
|
dispatchTopic: (payload: ChatTopicDispatch) => void;
|
|
17
|
+
/**
|
|
18
|
+
* 移除话题
|
|
19
|
+
* @param id
|
|
20
|
+
*/
|
|
21
|
+
removeTopic: (id: string) => void;
|
|
16
22
|
/**
|
|
17
23
|
* 将当前消息保存为主题
|
|
18
24
|
*/
|
|
@@ -28,6 +34,7 @@ export interface ChatTopicAction {
|
|
|
28
34
|
*/
|
|
29
35
|
updateTopicLoading: (id?: string) => void;
|
|
30
36
|
}
|
|
37
|
+
|
|
31
38
|
export const chatTopic: StateCreator<
|
|
32
39
|
SessionStore,
|
|
33
40
|
[['zustand/devtools', never]],
|
|
@@ -43,6 +50,24 @@ export const chatTopic: StateCreator<
|
|
|
43
50
|
|
|
44
51
|
get().dispatchSession({ id: activeId, topics, type: 'updateSessionTopic' });
|
|
45
52
|
},
|
|
53
|
+
removeTopic: (id) => {
|
|
54
|
+
const { dispatchTopic, dispatchMessage, toggleTopic } = get();
|
|
55
|
+
|
|
56
|
+
// 移除关联的 message
|
|
57
|
+
const messages = topicSelectors.getTopicMessages(id)(get());
|
|
58
|
+
for (const m of messages) {
|
|
59
|
+
dispatchMessage({
|
|
60
|
+
id: m.id,
|
|
61
|
+
type: 'deleteMessage',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 最后移除 topic
|
|
66
|
+
dispatchTopic({ id, type: 'deleteChatTopic' });
|
|
67
|
+
|
|
68
|
+
// 切换到默认 topic
|
|
69
|
+
toggleTopic();
|
|
70
|
+
},
|
|
46
71
|
saveToTopic: () => {
|
|
47
72
|
const session = sessionSelectors.currentSession(get());
|
|
48
73
|
if (!session) return;
|
|
@@ -71,6 +96,8 @@ export const chatTopic: StateCreator<
|
|
|
71
96
|
dispatchMessage({ id: m.id, key: 'topicId', type: 'updateMessage', value: topicId });
|
|
72
97
|
}
|
|
73
98
|
|
|
99
|
+
dispatchTopic({ id: topicId, key: 'title', type: 'updateChatTopic', value: LOADING_FLAT });
|
|
100
|
+
|
|
74
101
|
let output = '';
|
|
75
102
|
|
|
76
103
|
// 自动总结话题标题
|
|
@@ -91,7 +118,6 @@ export const chatTopic: StateCreator<
|
|
|
91
118
|
toggleTopic: (id) => {
|
|
92
119
|
set({ activeTopicId: id });
|
|
93
120
|
},
|
|
94
|
-
|
|
95
121
|
updateTopicLoading: (id) => {
|
|
96
122
|
set({ topicLoadingId: id });
|
|
97
123
|
},
|
|
@@ -14,12 +14,22 @@ interface UpdateChatTopicAction {
|
|
|
14
14
|
value: any;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
interface FavorChatTopicAction {
|
|
18
|
+
favorite: boolean;
|
|
19
|
+
id: string;
|
|
20
|
+
type: 'favorChatTopic';
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
interface DeleteChatTopicAction {
|
|
18
24
|
id: string;
|
|
19
25
|
type: 'deleteChatTopic';
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
export type ChatTopicDispatch =
|
|
28
|
+
export type ChatTopicDispatch =
|
|
29
|
+
| AddChatTopicAction
|
|
30
|
+
| UpdateChatTopicAction
|
|
31
|
+
| DeleteChatTopicAction
|
|
32
|
+
| FavorChatTopicAction;
|
|
23
33
|
|
|
24
34
|
export const topicReducer = (state: ChatTopicMap, payload: ChatTopicDispatch): ChatTopicMap => {
|
|
25
35
|
switch (payload.type) {
|
|
@@ -46,6 +56,18 @@ export const topicReducer = (state: ChatTopicMap, payload: ChatTopicDispatch): C
|
|
|
46
56
|
});
|
|
47
57
|
}
|
|
48
58
|
|
|
59
|
+
case 'favorChatTopic': {
|
|
60
|
+
return produce(state, (draftState) => {
|
|
61
|
+
const { favorite, id } = payload;
|
|
62
|
+
|
|
63
|
+
if (!draftState[id]) return;
|
|
64
|
+
|
|
65
|
+
const topic = draftState[id];
|
|
66
|
+
|
|
67
|
+
topic.favorite = favorite;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
49
71
|
case 'deleteChatTopic': {
|
|
50
72
|
return produce(state, (draftState) => {
|
|
51
73
|
delete draftState[payload.id];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { currentChats } from './chat';
|
|
2
2
|
import { chatsTokenCount, systemRoleTokenCount, totalTokenCount } from './token';
|
|
3
|
-
import { currentTopics } from './topic';
|
|
3
|
+
import { currentTopics, getTopicMessages } from './topic';
|
|
4
4
|
|
|
5
5
|
export const chatSelectors = {
|
|
6
6
|
chatsTokenCount,
|
|
@@ -11,4 +11,5 @@ export const chatSelectors = {
|
|
|
11
11
|
|
|
12
12
|
export const topicSelectors = {
|
|
13
13
|
currentTopics,
|
|
14
|
+
getTopicMessages,
|
|
14
15
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// 展示在聊天框中的消息
|
|
2
2
|
import { SessionStore, sessionSelectors } from '@/store/session';
|
|
3
|
+
import { organizeChats } from '@/store/session/slices/chat/selectors/utils';
|
|
3
4
|
import { ChatTopic } from '@/types/topic';
|
|
4
5
|
|
|
5
6
|
export const currentTopics = (s: SessionStore): ChatTopic[] => {
|
|
@@ -14,3 +15,10 @@ export const currentTopics = (s: SessionStore): ChatTopic[] => {
|
|
|
14
15
|
|
|
15
16
|
return [...favTopics, ...defaultTopics];
|
|
16
17
|
};
|
|
18
|
+
|
|
19
|
+
export const getTopicMessages = (topicId: string) => (s: SessionStore) => {
|
|
20
|
+
const session = sessionSelectors.currentSession(s);
|
|
21
|
+
if (!session) return [];
|
|
22
|
+
|
|
23
|
+
return organizeChats(session, { assistant: '', user: '' }, topicId);
|
|
24
|
+
};
|
|
@@ -11,18 +11,20 @@ import { SessionDispatch, sessionsReducer } from './reducers/session';
|
|
|
11
11
|
|
|
12
12
|
export interface SessionAction {
|
|
13
13
|
activeSession: (sessionId: string) => void;
|
|
14
|
+
clearSessions: () => void;
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* @title 添加会话
|
|
16
18
|
* @param session - 会话信息
|
|
17
19
|
* @returns void
|
|
18
20
|
*/
|
|
19
21
|
createSession: () => Promise<string>;
|
|
20
|
-
|
|
21
22
|
/**
|
|
22
23
|
* 变更session
|
|
23
24
|
* @param payload - 聊天记录
|
|
24
25
|
*/
|
|
25
26
|
dispatchSession: (payload: SessionDispatch) => void;
|
|
27
|
+
|
|
26
28
|
/**
|
|
27
29
|
* 导入会话
|
|
28
30
|
* @param sessions
|
|
@@ -34,14 +36,12 @@ export interface SessionAction {
|
|
|
34
36
|
* @returns 压缩后的消息
|
|
35
37
|
*/
|
|
36
38
|
// genShareUrl: () => string;
|
|
37
|
-
|
|
38
39
|
/**
|
|
39
40
|
* @title 删除会话
|
|
40
41
|
* @param index - 会话索引
|
|
41
42
|
* @returns void
|
|
42
43
|
*/
|
|
43
44
|
removeSession: (sessionId: string) => void;
|
|
44
|
-
|
|
45
45
|
/**
|
|
46
46
|
* @title 切换会话
|
|
47
47
|
* @param sessionId - 会话索引
|
|
@@ -60,6 +60,10 @@ export const createSessionSlice: StateCreator<
|
|
|
60
60
|
set({ activeId: sessionId });
|
|
61
61
|
},
|
|
62
62
|
|
|
63
|
+
clearSessions: () => {
|
|
64
|
+
set({ sessions: {} });
|
|
65
|
+
},
|
|
66
|
+
|
|
63
67
|
createSession: async () => {
|
|
64
68
|
const { dispatchSession, switchSession } = get();
|
|
65
69
|
|
|
@@ -100,7 +104,6 @@ export const createSessionSlice: StateCreator<
|
|
|
100
104
|
}),
|
|
101
105
|
});
|
|
102
106
|
},
|
|
103
|
-
|
|
104
107
|
removeSession: (sessionId) => {
|
|
105
108
|
get().dispatchSession({ id: sessionId, type: 'removeSession' });
|
|
106
109
|
|
|
@@ -108,6 +111,7 @@ export const createSessionSlice: StateCreator<
|
|
|
108
111
|
Router.push('/');
|
|
109
112
|
}
|
|
110
113
|
},
|
|
114
|
+
|
|
111
115
|
switchSession: async (sessionId) => {
|
|
112
116
|
if (get().activeId === sessionId) return;
|
|
113
117
|
|
|
@@ -118,7 +122,6 @@ export const createSessionSlice: StateCreator<
|
|
|
118
122
|
// 新会话
|
|
119
123
|
await Router.push(`/chat/${sessionId}`);
|
|
120
124
|
},
|
|
121
|
-
|
|
122
125
|
// genShareUrl: () => {
|
|
123
126
|
// const session = sessionSelectors.currentSession(get());
|
|
124
127
|
// if (!session) return '';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { transform } from 'lodash-es';
|
|
2
|
+
|
|
3
|
+
import { SessionStore } from '@/store/session';
|
|
4
|
+
import { LobeAgentSession, LobeSessions } from '@/types/session';
|
|
5
|
+
|
|
6
|
+
import { getSessionById } from './list';
|
|
7
|
+
|
|
8
|
+
export const exportSessions = (s: SessionStore) => s.sessions;
|
|
9
|
+
|
|
10
|
+
// 排除 chats
|
|
11
|
+
export const exportAgents = (s: SessionStore) => {
|
|
12
|
+
return transform(s.sessions, (result: LobeSessions, value, key) => {
|
|
13
|
+
// 移除 chats 和 topics
|
|
14
|
+
result[key] = { ...value, chats: {}, topics: {} } as LobeAgentSession;
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
// 排除 chats
|
|
18
|
+
export const getExportAgent =
|
|
19
|
+
(id: string) =>
|
|
20
|
+
(s: SessionStore): LobeAgentSession => {
|
|
21
|
+
const session = getSessionById(id)(s);
|
|
22
|
+
return { ...session, chats: {}, topics: {} };
|
|
23
|
+
};
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import { exportAgents, exportSessions, getExportAgent } from './export';
|
|
1
2
|
import {
|
|
3
|
+
currentSession,
|
|
2
4
|
currentSessionSafe,
|
|
3
|
-
currentSessionSel,
|
|
4
5
|
getSessionById,
|
|
5
6
|
getSessionMetaById,
|
|
6
7
|
sessionList,
|
|
7
8
|
} from './list';
|
|
8
9
|
|
|
9
10
|
export const sessionSelectors = {
|
|
10
|
-
currentSession
|
|
11
|
+
currentSession,
|
|
11
12
|
currentSessionSafe,
|
|
13
|
+
exportAgents,
|
|
14
|
+
exportSessions,
|
|
15
|
+
getExportAgent,
|
|
12
16
|
getSessionById,
|
|
13
17
|
getSessionMetaById,
|
|
14
18
|
sessionList,
|
|
@@ -5,13 +5,14 @@ import { filterWithKeywords } from '@/utils/filter';
|
|
|
5
5
|
|
|
6
6
|
import { initLobeSession } from '../initialState';
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
|
|
9
9
|
if (!s.activeId) return;
|
|
10
10
|
|
|
11
11
|
return s.sessions[s.activeId];
|
|
12
12
|
};
|
|
13
|
+
|
|
13
14
|
export const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
|
|
14
|
-
return
|
|
15
|
+
return currentSession(s) || initLobeSession;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export const sessionList = (s: SessionStore) => {
|