@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.
Files changed (40) hide show
  1. package/.eslintrc.js +3 -0
  2. package/CHANGELOG.md +54 -0
  3. package/package.json +1 -1
  4. package/src/const/message.ts +2 -0
  5. package/src/features/SideBar/BottomAction.tsx +1 -1
  6. package/src/helpers/export.ts +62 -0
  7. package/src/hooks/useExportConfig.ts +3 -66
  8. package/src/locales/default/common.ts +2 -0
  9. package/src/locales/default/setting.ts +4 -3
  10. package/src/migrations/index.ts +2 -2
  11. package/src/pages/chat/SessionList/List/SessionItem.tsx +44 -52
  12. package/src/pages/chat/SessionList/List/style.ts +3 -0
  13. package/src/pages/chat/[id]/Config/SideBar.tsx +1 -1
  14. package/src/pages/chat/[id]/Config/Topic/DefaultContent.tsx +29 -0
  15. package/src/pages/chat/[id]/Config/Topic/TopicContent.tsx +151 -0
  16. package/src/pages/chat/[id]/Config/Topic/TopicItem.tsx +70 -0
  17. package/src/pages/chat/[id]/Config/{Topic.tsx → Topic/index.tsx} +1 -8
  18. package/src/pages/chat/[id]/Config/index.tsx +3 -5
  19. package/src/pages/chat/[id]/Conversation/Input/index.tsx +7 -10
  20. package/src/pages/chat/[id]/Header.tsx +3 -2
  21. package/src/pages/chat/[id]/edit/Header.tsx +14 -1
  22. package/src/pages/setting/SettingForm.tsx +34 -29
  23. package/src/store/session/slices/agentConfig/action.ts +0 -11
  24. package/src/store/session/slices/agentConfig/initialState.ts +0 -4
  25. package/src/store/session/slices/chat/actions/message.ts +2 -2
  26. package/src/store/session/slices/chat/actions/topic.ts +28 -2
  27. package/src/store/session/slices/chat/initialState.ts +1 -0
  28. package/src/store/session/slices/chat/reducers/topic.ts +23 -1
  29. package/src/store/session/slices/chat/selectors/index.ts +2 -1
  30. package/src/store/session/slices/chat/selectors/topic.ts +8 -0
  31. package/src/store/session/slices/session/action.ts +8 -5
  32. package/src/store/session/slices/session/selectors/export.ts +23 -0
  33. package/src/store/session/slices/session/selectors/index.ts +6 -2
  34. package/src/store/session/slices/session/selectors/list.ts +3 -2
  35. package/src/store/settings/action.ts +11 -1
  36. package/src/store/settings/initialState.ts +2 -0
  37. package/src/store/settings/selectors.ts +10 -0
  38. package/src/types/exportConfig.ts +34 -14
  39. package/src/utils/config.ts +54 -1
  40. 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 { useSessionStore } from '@/store/session';
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] = useSessionStore(
27
- (s) => [s.showAgentSettings, s.toggleConfig],
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, useMemo, useState } from 'react';
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 = useMemo(
27
- () => (
28
- <Tooltip title={t('topic.saveCurrentMessages')}>
29
- <Button icon={<Icon icon={LucideGalleryVerticalEnd} />} onClick={saveToTopic} />
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] = useSessionStore(
28
- (s) => [s.showAgentSettings, s.toggleConfig],
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: MenuProps['items'] = useMemo(
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, Popconfirm, Select, Switch } from 'antd';
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
- resetSettings();
42
- form.setFieldsValue(DEFAULT_SETTINGS);
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
- handleReset();
47
- // TODO: 删除聊天记录
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
- <Popconfirm
244
- arrow={false}
245
- cancelText={t('cancel', { ns: 'common' })}
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
- <Popconfirm
263
- arrow={false}
264
- cancelText={t('cancel', { ns: 'common' })}
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
  },
@@ -1,6 +1,7 @@
1
1
  export interface ChatState {
2
2
  activeTopicId?: string;
3
3
  chatLoadingId?: string;
4
+ renameTopicId?: string;
4
5
  topicLoadingId?: string;
5
6
  }
6
7
 
@@ -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 = AddChatTopicAction | UpdateChatTopicAction | DeleteChatTopicAction;
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: currentSessionSel,
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 currentSessionSel = (s: SessionStore): LobeAgentSession | undefined => {
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 currentSessionSel(s) || initLobeSession;
15
+ return currentSession(s) || initLobeSession;
15
16
  };
16
17
 
17
18
  export const sessionList = (s: SessionStore) => {