@lobehub/chat 0.105.2 → 0.107.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 (224) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +18 -0
  3. package/README.zh-CN.md +18 -0
  4. package/docs/Environment-Variable.md +4 -2
  5. package/docs/Environment-Variable.zh-CN.md +4 -2
  6. package/locales/en_US/common.json +19 -0
  7. package/locales/en_US/migration.json +45 -0
  8. package/locales/en_US/setting.json +7 -7
  9. package/locales/ja_JP/common.json +19 -0
  10. package/locales/ja_JP/migration.json +45 -0
  11. package/locales/ja_JP/setting.json +2 -2
  12. package/locales/ko_KR/common.json +19 -0
  13. package/locales/ko_KR/migration.json +45 -0
  14. package/locales/ko_KR/setting.json +2 -2
  15. package/locales/ru_RU/common.json +19 -0
  16. package/locales/ru_RU/migration.json +45 -0
  17. package/locales/ru_RU/setting.json +2 -2
  18. package/locales/zh_CN/common.json +19 -0
  19. package/locales/zh_CN/migration.json +45 -0
  20. package/locales/zh_CN/setting.json +1 -1
  21. package/locales/zh_TW/common.json +19 -0
  22. package/locales/zh_TW/migration.json +45 -0
  23. package/locales/zh_TW/setting.json +2 -2
  24. package/package.json +2 -1
  25. package/src/app/api/openai/chat/createChatCompletion.test.ts +4 -4
  26. package/src/app/api/openai/chat/createChatCompletion.ts +4 -10
  27. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  28. package/src/app/api/openai/errorResponse.test.ts +1 -1
  29. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +2 -1
  30. package/src/app/chat/(mobile)/features/ChatInput/SendButton.tsx +2 -2
  31. package/src/app/chat/features/ChatHeader/ShareButton/Inner.tsx +2 -5
  32. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +2 -2
  33. package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
  34. package/src/app/chat/features/ChatInput/ActionBar/ModelSwitch.tsx +1 -1
  35. package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +8 -2
  36. package/src/app/chat/features/ChatInput/InputAreaInner/index.tsx +2 -2
  37. package/src/app/chat/features/ChatInput/STT/index.tsx +2 -2
  38. package/src/app/chat/features/ChatInput/Topic/index.tsx +2 -2
  39. package/src/app/chat/features/ChatInput/useSend.ts +3 -3
  40. package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +2 -2
  41. package/src/app/chat/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -2
  42. package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +2 -2
  43. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -2
  44. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +4 -4
  45. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +30 -31
  46. package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +2 -2
  47. package/src/app/chat/features/Conversation/ChatList/Extras/Translate.tsx +2 -2
  48. package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +8 -5
  49. package/src/app/chat/features/Conversation/ChatList/Extras/index.ts +3 -3
  50. package/src/app/chat/features/Conversation/ChatList/Extras/type.ts +5 -0
  51. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +10 -6
  52. package/src/app/chat/features/Conversation/ChatList/Messages/Default.tsx +7 -3
  53. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +7 -6
  54. package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +7 -7
  55. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +6 -6
  56. package/src/app/chat/features/Conversation/ChatList/index.tsx +16 -24
  57. package/src/app/chat/features/Conversation/ScrollAnchor/index.tsx +2 -2
  58. package/src/app/chat/features/Conversation/index.tsx +3 -3
  59. package/src/app/chat/features/Conversation/useInitConversation.ts +43 -0
  60. package/src/app/chat/features/Migration/ExportConfigButton.tsx +33 -0
  61. package/src/app/chat/features/Migration/Failed.tsx +115 -0
  62. package/src/app/chat/features/Migration/Modal.tsx +80 -0
  63. package/src/app/chat/features/Migration/Start.tsx +106 -0
  64. package/src/app/chat/features/Migration/UpgradeButton.tsx +74 -0
  65. package/src/app/chat/features/Migration/const.ts +15 -0
  66. package/src/app/chat/features/Migration/index.tsx +44 -0
  67. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +54 -0
  68. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +5 -4
  69. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +22 -28
  70. package/src/app/chat/features/SessionListContent/SearchMode.tsx +15 -0
  71. package/src/app/chat/features/SessionListContent/index.tsx +4 -44
  72. package/src/app/chat/features/SessionSearchBar/index.tsx +11 -3
  73. package/src/app/chat/features/TopicListContent/Header.tsx +4 -4
  74. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +11 -9
  75. package/src/app/chat/features/TopicListContent/Topic/TopicItem.tsx +15 -13
  76. package/src/app/chat/features/TopicListContent/Topic/index.tsx +9 -16
  77. package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +13 -4
  78. package/src/app/chat/features/TopicListContent/index.tsx +1 -2
  79. package/src/app/chat/page.tsx +6 -1
  80. package/src/app/chat/settings/features/HeaderContent.tsx +3 -3
  81. package/src/app/home/Redirect.tsx +13 -7
  82. package/src/app/settings/(mobile)/features/ExtraList.tsx +12 -4
  83. package/src/app/settings/common/Common.tsx +16 -4
  84. package/src/app/welcome/features/Banner/index.tsx +9 -10
  85. package/src/chains/summaryTitle.ts +5 -2
  86. package/src/components/DataStyleModal/index.tsx +64 -0
  87. package/src/config/client.ts +5 -0
  88. package/src/const/session.ts +21 -0
  89. package/src/const/settings.ts +1 -1
  90. package/src/database/core/__tests__/db.test.ts +8 -5
  91. package/src/database/core/__tests__/model.test.ts +3 -3
  92. package/src/database/core/db.ts +38 -10
  93. package/src/database/core/index.ts +1 -0
  94. package/src/database/core/model.ts +121 -10
  95. package/src/database/core/schemas.ts +22 -0
  96. package/src/database/core/types/db.ts +13 -0
  97. package/src/database/models/__DEBUG.ts +119 -0
  98. package/src/database/models/{file.test.ts → __tests__/file.test.ts} +19 -6
  99. package/src/database/models/__tests__/message.test.ts +314 -0
  100. package/src/database/models/__tests__/session.test.ts +117 -0
  101. package/src/database/models/__tests__/topic.test.ts +250 -0
  102. package/src/database/models/file.ts +9 -5
  103. package/src/database/models/message.ts +186 -0
  104. package/src/database/models/session.ts +160 -0
  105. package/src/database/models/topic.ts +196 -0
  106. package/src/{types/database → database/schemas}/files.ts +2 -2
  107. package/src/database/schemas/message.ts +37 -0
  108. package/src/database/schemas/session.ts +56 -0
  109. package/src/database/schemas/topic.ts +12 -0
  110. package/src/features/AgentSetting/AgentConfig/index.tsx +8 -1
  111. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  112. package/src/features/AgentSetting/store/action.ts +1 -1
  113. package/src/features/AgentSetting/store/initialState.ts +1 -1
  114. package/src/features/AgentSetting/store/reducers/config.ts +1 -1
  115. package/src/features/DataImporter/index.tsx +240 -0
  116. package/src/features/DebugUI/index.tsx +34 -0
  117. package/src/features/SideBar/BottomActions.tsx +8 -14
  118. package/src/hooks/useImportConfig.ts +13 -30
  119. package/src/hooks/useTTS.ts +1 -1
  120. package/src/layout/GlobalLayout/index.tsx +16 -1
  121. package/src/locales/default/common.ts +19 -0
  122. package/src/locales/default/migration.ts +47 -0
  123. package/src/locales/default/setting.ts +1 -1
  124. package/src/locales/resources/en_US.ts +2 -0
  125. package/src/locales/resources/ja_JP.ts +2 -0
  126. package/src/locales/resources/ko_KR.ts +2 -0
  127. package/src/locales/resources/ru_RU.ts +2 -0
  128. package/src/locales/resources/zh_CN.ts +2 -0
  129. package/src/locales/resources/zh_TW.ts +2 -0
  130. package/src/migrations/FromV0ToV1.ts +2 -4
  131. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +191 -0
  132. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +202 -0
  133. package/src/migrations/FromV1ToV2/index.ts +82 -0
  134. package/src/migrations/FromV1ToV2/migrations.test.ts +224 -0
  135. package/src/migrations/FromV1ToV2/types/v1.ts +78 -0
  136. package/src/migrations/FromV1ToV2/types/v2.ts +52 -0
  137. package/src/{utils → migrations}/VersionController.test.ts +1 -3
  138. package/src/{utils → migrations}/VersionController.ts +5 -2
  139. package/src/migrations/index.ts +17 -3
  140. package/src/services/__tests__/file.test.ts +3 -3
  141. package/src/services/chat.ts +66 -5
  142. package/src/services/config.ts +184 -0
  143. package/src/services/debug.ts +13 -0
  144. package/src/services/file.ts +6 -2
  145. package/src/services/github.ts +25 -0
  146. package/src/services/message.ts +79 -0
  147. package/src/services/session.ts +63 -0
  148. package/src/services/topic.ts +56 -0
  149. package/src/store/chat/actions/enhance.ts +95 -0
  150. package/src/store/{session/slices/chat → chat}/actions/message.ts +225 -247
  151. package/src/store/{session/slices/chat → chat}/actions/plugin.ts +29 -34
  152. package/src/store/{session/slices/chat → chat}/actions/share.ts +9 -13
  153. package/src/store/chat/actions/topic.ts +197 -0
  154. package/src/store/chat/helpers.ts +23 -0
  155. package/src/store/chat/index.ts +3 -0
  156. package/src/store/chat/initialState.ts +48 -0
  157. package/src/store/chat/reducers/message.test.ts +83 -0
  158. package/src/store/chat/reducers/message.ts +83 -0
  159. package/src/store/chat/selectors/__tests__/topic.test.ts +43 -0
  160. package/src/store/chat/selectors/chat.ts +119 -0
  161. package/src/store/chat/selectors/index.ts +2 -0
  162. package/src/store/chat/selectors/topic.ts +28 -0
  163. package/src/store/chat/store.ts +63 -0
  164. package/src/store/files/slices/images/action.ts +6 -4
  165. package/src/store/global/selectors/__snapshots__/settings.test.ts.snap +99 -0
  166. package/src/store/global/selectors/settings.test.ts +51 -0
  167. package/src/store/global/selectors/settings.ts +36 -5
  168. package/src/store/global/store.ts +1 -1
  169. package/src/store/middleware/createHyperStorage/index.ts +40 -24
  170. package/src/store/middleware/createHyperStorage/keyMapper.ts +8 -2
  171. package/src/store/middleware/createHyperStorage/type.ts +10 -8
  172. package/src/store/plugin/slices/plugin/action.ts +1 -1
  173. package/src/store/plugin/store.ts +2 -4
  174. package/src/store/session/helpers.ts +1 -1
  175. package/src/store/session/hooks/useSessionHydrated.ts +6 -10
  176. package/src/store/session/initialState.ts +26 -7
  177. package/src/store/session/selectors.ts +1 -2
  178. package/src/store/session/slices/agent/action.ts +26 -35
  179. package/src/store/session/slices/agent/selectors.ts +32 -37
  180. package/src/store/session/slices/session/action.ts +81 -88
  181. package/src/store/session/slices/session/helpers.ts +19 -0
  182. package/src/store/session/slices/session/initialState.ts +7 -10
  183. package/src/store/session/slices/session/selectors/export.ts +12 -9
  184. package/src/store/session/slices/session/selectors/index.ts +2 -35
  185. package/src/store/session/slices/session/selectors/list.test.ts +25 -203
  186. package/src/store/session/slices/session/selectors/list.ts +40 -78
  187. package/src/store/session/store.ts +11 -17
  188. package/src/types/agent/index.ts +61 -0
  189. package/src/types/chatMessage.ts +11 -20
  190. package/src/types/exportConfig.ts +7 -2
  191. package/src/types/files.ts +2 -2
  192. package/src/types/llm.ts +1 -1
  193. package/src/types/meta.ts +24 -12
  194. package/src/types/openai/chat.ts +9 -1
  195. package/src/types/openai/functionCall.ts +4 -0
  196. package/src/types/session.ts +10 -94
  197. package/src/types/settings.ts +2 -0
  198. package/src/types/translate.ts +2 -1
  199. package/src/utils/config.ts +5 -11
  200. package/src/utils/fetch.ts +8 -2
  201. package/src/app/chat/features/Conversation/usePluginsInit.ts +0 -14
  202. package/src/database/core/schema.ts +0 -9
  203. package/src/helpers/export.ts +0 -69
  204. package/src/hooks/useExportConfig.ts +0 -6
  205. package/src/store/session/slices/chat/actions/index.ts +0 -32
  206. package/src/store/session/slices/chat/actions/topic.ts +0 -171
  207. package/src/store/session/slices/chat/actions/translate.ts +0 -113
  208. package/src/store/session/slices/chat/helpers.ts +0 -9
  209. package/src/store/session/slices/chat/initialState.ts +0 -13
  210. package/src/store/session/slices/chat/reducers/message.test.ts +0 -319
  211. package/src/store/session/slices/chat/reducers/message.ts +0 -156
  212. package/src/store/session/slices/chat/reducers/topic.test.ts +0 -179
  213. package/src/store/session/slices/chat/reducers/topic.ts +0 -81
  214. package/src/store/session/slices/chat/selectors/chat.ts +0 -111
  215. package/src/store/session/slices/chat/selectors/index.ts +0 -26
  216. package/src/store/session/slices/chat/selectors/topic.ts +0 -30
  217. package/src/store/session/slices/chat/selectors/utils.test.ts +0 -385
  218. package/src/store/session/slices/chat/selectors/utils.ts +0 -91
  219. package/src/store/session/slices/chat/utils.ts +0 -13
  220. package/src/store/session/slices/session/reducers/session.test.ts +0 -577
  221. package/src/store/session/slices/session/reducers/session.ts +0 -182
  222. package/src/types/database/db.ts +0 -4
  223. /package/src/store/{session/slices/chat → chat}/reducers/files.test.ts +0 -0
  224. /package/src/store/{session/slices/chat → chat}/reducers/files.ts +0 -0
@@ -1,11 +1,30 @@
1
- import { ChatState, initialChatState } from './slices/chat/initialState';
2
- import { SessionState, initialSessionState } from './slices/session/initialState';
1
+ import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
3
2
 
4
- export type SessionStoreState = SessionState & ChatState;
3
+ import { LobeAgentSession } from '@/types/session';
4
+
5
+ export type SessionStoreState = SessionState;
6
+
7
+ export interface SessionState {
8
+ /**
9
+ * @title 当前活动的会话
10
+ * @description 当前正在编辑或查看的会话
11
+ */
12
+ activeId: string;
13
+ fetchSessionsLoading: boolean;
14
+ isMobile?: boolean;
15
+ isSearching: boolean;
16
+ router?: AppRouterInstance;
17
+ searchKeywords: string;
18
+ searchSessions: LobeAgentSession[];
19
+ sessions: LobeAgentSession[];
20
+ }
5
21
 
6
22
  export const initialState: SessionStoreState = {
7
- ...initialSessionState,
8
- ...initialChatState,
23
+ activeId: 'inbox',
24
+ fetchSessionsLoading: true,
25
+ isMobile: false,
26
+ isSearching: false,
27
+ searchKeywords: '',
28
+ searchSessions: [],
29
+ sessions: [],
9
30
  };
10
-
11
- export { initLobeSession } from './slices/session/initialState';
@@ -1,3 +1,2 @@
1
1
  export { agentSelectors } from './slices/agent/selectors';
2
- export { chatSelectors, topicSelectors } from './slices/chat/selectors';
3
- export { sessionSelectors } from './slices/session/selectors';
2
+ export { sessionExportSelectors,sessionSelectors } from './slices/session/selectors';
@@ -1,9 +1,10 @@
1
1
  import { produce } from 'immer';
2
2
  import { StateCreator } from 'zustand/vanilla';
3
3
 
4
+ import { sessionService } from '@/services/session';
4
5
  import { useGlobalStore } from '@/store/global';
6
+ import { LobeAgentConfig } from '@/types/agent';
5
7
  import { MetaData } from '@/types/meta';
6
- import { LobeAgentConfig } from '@/types/session';
7
8
 
8
9
  import { SessionStore } from '../../store';
9
10
  import { sessionSelectors } from '../session/selectors';
@@ -13,10 +14,6 @@ import { sessionSelectors } from '../session/selectors';
13
14
  */
14
15
  export interface AgentAction {
15
16
  removePlugin: (id: string) => void;
16
- /**
17
- * 更新代理配置
18
- * @param config - 部分 LobeAgentConfig 的配置
19
- */
20
17
  updateAgentConfig: (config: Partial<LobeAgentConfig>) => void;
21
18
  updateAgentMeta: (meta: Partial<MetaData>) => void;
22
19
  }
@@ -27,51 +24,45 @@ export const createAgentSlice: StateCreator<
27
24
  [],
28
25
  AgentAction
29
26
  > = (set, get) => ({
30
- removePlugin: (id) => {
31
- const { activeId } = get();
27
+ removePlugin: async (id) => {
32
28
  const session = sessionSelectors.currentSession(get());
33
- if (!activeId || !session) return;
29
+ if (!session) return;
30
+
31
+ const { activeId, refreshSessions } = get();
34
32
 
35
33
  const config = produce(session.config, (draft) => {
36
34
  draft.plugins = draft.plugins?.filter((i) => i !== id) || [];
37
35
  });
38
36
 
39
- get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
37
+ await sessionService.updateSessionConfig(activeId, config);
38
+ await refreshSessions();
40
39
  },
41
40
 
42
- updateAgentConfig: (config) => {
43
- const { activeId, dispatchSession } = get();
44
- const session = sessionSelectors.currentSession(get());
45
- if (!activeId || !session) return;
46
-
41
+ updateAgentConfig: async (config) => {
47
42
  // if is the inbox session, update the global config
48
- if (sessionSelectors.isInboxSession(get())) {
43
+ const isInbox = sessionSelectors.isInboxSession(get());
44
+ if (isInbox) {
49
45
  useGlobalStore.getState().updateDefaultAgent({ config });
50
- // NOTE: DON'T ADD RETURN HERE.
51
- // we need to use `dispatchSession` below to update inbox config to trigger the inbox config rerender
46
+ } else {
47
+ const session = sessionSelectors.currentSession(get());
48
+ if (!session) return;
49
+
50
+ const { activeId } = get();
51
+
52
+ await sessionService.updateSessionConfig(activeId, config);
52
53
  }
53
54
 
54
- // Although we use global store to store the default agent config,
55
- // due to the `currentAgentConfig` selector use `useGlobalStore.getState()`
56
- // to get the default agent config which not rerender on global store update
57
- // we need to update the session config here.
58
- dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
55
+ // trigger store rerender
56
+ await get().refreshSessions();
59
57
  },
60
58
 
61
- updateAgentMeta: (meta) => {
62
- const { activeId } = get();
59
+ updateAgentMeta: async (meta) => {
63
60
  const session = sessionSelectors.currentSession(get());
64
- if (!activeId || !session) return;
61
+ if (!session) return;
65
62
 
66
- for (const [key, value] of Object.entries(meta)) {
67
- if (value !== undefined) {
68
- get().dispatchSession({
69
- id: activeId,
70
- key: key as keyof MetaData,
71
- type: 'updateSessionMeta',
72
- value,
73
- });
74
- }
75
- }
63
+ const { activeId, refreshSessions } = get();
64
+
65
+ await sessionService.updateSessionMeta(activeId, meta);
66
+ await refreshSessions();
76
67
  },
77
68
  });
@@ -1,25 +1,18 @@
1
1
  import { t } from 'i18next';
2
2
 
3
3
  import { DEFAULT_OPENAI_MODEL_LIST, VISION_MODEL_WHITE_LIST } from '@/const/llm';
4
- import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
4
+ import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR, DEFAULT_INBOX_AVATAR } from '@/const/meta';
5
5
  import { DEFAULT_AGENT_CONFIG, DEFAUTT_AGENT_TTS_CONFIG } from '@/const/settings';
6
6
  import { settingsSelectors, useGlobalStore } from '@/store/global';
7
7
  import { SessionStore } from '@/store/session';
8
+ import { LobeAgentTTSConfig } from '@/types/agent';
8
9
  import { LanguageModel } from '@/types/llm';
9
10
  import { MetaData } from '@/types/meta';
10
- import { LobeAgentTTSConfig } from '@/types/session';
11
11
  import { merge } from '@/utils/merge';
12
12
 
13
13
  import { sessionSelectors } from '../session/selectors';
14
14
 
15
- const currentAgentMeta = (s: SessionStore): MetaData => {
16
- const session = sessionSelectors.currentSession(s);
17
-
18
- return { avatar: DEFAULT_AVATAR, backgroundColor: DEFAULT_BACKGROUND_COLOR, ...session?.meta };
19
- };
20
-
21
- const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title || t('defaultSession');
22
-
15
+ // ========== Config ============== //
23
16
  const currentAgentConfig = (s: SessionStore) => {
24
17
  const session = sessionSelectors.currentSession(s);
25
18
 
@@ -35,48 +28,47 @@ const currentAgentSystemRole = (s: SessionStore) => {
35
28
  return currentAgentConfig(s).systemRole;
36
29
  };
37
30
 
38
- const currentAgentDescription = (s: SessionStore) =>
39
- currentAgentMeta(s)?.description || currentAgentSystemRole(s) || t('noDescription');
40
-
41
- const currentAgentBackgroundColor = (s: SessionStore) => {
42
- const session = sessionSelectors.currentSession(s);
43
- if (!session) return DEFAULT_BACKGROUND_COLOR;
44
- return session.meta.backgroundColor || DEFAULT_BACKGROUND_COLOR;
45
- };
46
-
47
- const currentAgentAvatar = (s: SessionStore) => {
48
- const session = sessionSelectors.currentSession(s);
49
- if (!session) return DEFAULT_AVATAR;
50
-
51
- return session.meta.avatar || DEFAULT_AVATAR;
52
- };
53
-
54
31
  const currentAgentModel = (s: SessionStore): LanguageModel | string => {
55
32
  const config = currentAgentConfig(s);
56
33
 
57
34
  return config?.model || LanguageModel.GPT3_5;
58
35
  };
59
36
 
60
- const modelHasVisionAbility = (s: SessionStore): boolean => {
61
- const model = currentAgentModel(s);
62
- return VISION_MODEL_WHITE_LIST.includes(model);
63
- };
64
-
65
37
  const currentAgentPlugins = (s: SessionStore) => {
66
38
  const config = currentAgentConfig(s);
67
39
 
68
40
  return config?.plugins || [];
69
41
  };
70
42
 
71
- const hasSystemRole = (s: SessionStore) => {
43
+ const currentAgentTTS = (s: SessionStore): LobeAgentTTSConfig => {
72
44
  const config = currentAgentConfig(s);
73
45
 
74
- return !!config.systemRole;
46
+ return config?.tts || DEFAUTT_AGENT_TTS_CONFIG;
47
+ };
48
+
49
+ // ========== Meta ============== //
50
+ const currentAgentMeta = (s: SessionStore): MetaData => {
51
+ const isInbox = sessionSelectors.isInboxSession(s);
52
+
53
+ const defaultMeta = {
54
+ avatar: isInbox ? DEFAULT_INBOX_AVATAR : DEFAULT_AVATAR,
55
+ backgroundColor: DEFAULT_BACKGROUND_COLOR,
56
+ description: isInbox ? t('inbox.desc') : currentAgentSystemRole(s) || t('noDescription'),
57
+ title: isInbox ? t('inbox') : t('defaultSession'),
58
+ };
59
+
60
+ const session = sessionSelectors.currentSession(s);
61
+
62
+ return merge(defaultMeta, session?.meta);
75
63
  };
76
64
 
65
+ const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s).title;
66
+ const currentAgentDescription = (s: SessionStore) => currentAgentMeta(s).description;
67
+ const currentAgentAvatar = (s: SessionStore) => currentAgentMeta(s).avatar;
68
+ const currentAgentBackgroundColor = (s: SessionStore) => currentAgentMeta(s).backgroundColor;
69
+
77
70
  const getAvatar = (s: MetaData) => s.avatar || DEFAULT_AVATAR;
78
71
  const getTitle = (s: MetaData) => s.title || t('defaultSession', { ns: 'common' });
79
-
80
72
  export const getDescription = (s: MetaData) =>
81
73
  s.description || t('noDescription', { ns: 'common' });
82
74
 
@@ -85,11 +77,14 @@ const showTokenTag = (s: SessionStore) => {
85
77
 
86
78
  return DEFAULT_OPENAI_MODEL_LIST.includes(model);
87
79
  };
88
-
89
- const currentAgentTTS = (s: SessionStore): LobeAgentTTSConfig => {
80
+ const hasSystemRole = (s: SessionStore) => {
90
81
  const config = currentAgentConfig(s);
91
82
 
92
- return config?.tts || DEFAUTT_AGENT_TTS_CONFIG;
83
+ return !!config.systemRole;
84
+ };
85
+ const modelHasVisionAbility = (s: SessionStore): boolean => {
86
+ const model = currentAgentModel(s);
87
+ return VISION_MODEL_WHITE_LIST.includes(model);
93
88
  };
94
89
 
95
90
  export const agentSelectors = {
@@ -1,68 +1,76 @@
1
- import { produce } from 'immer';
1
+ import useSWR, { SWRResponse, mutate } from 'swr';
2
2
  import { DeepPartial } from 'utility-types';
3
3
  import { StateCreator } from 'zustand/vanilla';
4
4
 
5
5
  import { INBOX_SESSION_ID } from '@/const/session';
6
6
  import { SESSION_CHAT_URL } from '@/const/url';
7
+ import { sessionService } from '@/services/session';
7
8
  import { settingsSelectors, useGlobalStore } from '@/store/global';
8
9
  import { SessionStore } from '@/store/session';
9
- import { LobeAgentSession, LobeAgentSettings, LobeSessions } from '@/types/session';
10
+ import {
11
+ LobeAgentSession,
12
+ LobeAgentSettings,
13
+ LobeSessionType,
14
+ LobeSessions,
15
+ } from '@/types/session';
10
16
  import { merge } from '@/utils/merge';
11
17
  import { setNamespace } from '@/utils/storeDebug';
12
- import { uuid } from '@/utils/uuid';
13
18
 
14
- import { initInbox, initLobeSession } from './initialState';
15
- import { SessionDispatch, sessionsReducer } from './reducers/session';
19
+ import { initLobeSession } from './initialState';
16
20
 
17
21
  const t = setNamespace('session');
18
22
 
23
+ const FETCH_SESSIONS_KEY = 'fetchSessions';
24
+
19
25
  export interface SessionAction {
20
- activeSession: (sessionId: string) => void;
21
- clearSessions: () => void;
22
26
  /**
23
- * @title 添加会话
24
- * @param session - 会话信息
25
- * @returns void
27
+ * active the session
28
+ * @param sessionId
26
29
  */
27
- createSession: (agent?: DeepPartial<LobeAgentSettings>) => Promise<string>;
30
+ activeSession: (sessionId: string) => void;
28
31
  /**
29
- * 变更session
30
- * @param payload - 聊天记录
32
+ * reset sessions to default
31
33
  */
32
- dispatchSession: (payload: SessionDispatch) => void;
33
- importInbox: (inbox: LobeAgentSession) => void;
34
+ clearSessions: () => Promise<void>;
34
35
  /**
35
- * 导入会话
36
- * @param sessions
36
+ * create a new session
37
+ * @param agent
38
+ * @returns sessionId
37
39
  */
38
- importSessions: (sessions: LobeSessions) => void;
40
+ createSession: (agent?: DeepPartial<LobeAgentSettings>) => Promise<string>;
39
41
 
40
42
  /**
41
- * 置顶会话
42
- * @param sessionId
43
+ * Pins or unpins a session.
44
+ *
45
+ * @param {string} id - The ID of the session to pin or unpin.
46
+ * @param {boolean} [pinned] - Optional. If set to true, it pins the session. If set to false or omitted, it unpins the session.
47
+ * @returns {Promise<void>} A promise that resolves when the session is successfully pinned or unpinned.
43
48
  */
44
- pinSession: (sessionId: string, pinned?: boolean) => void;
49
+ pinSession: (id: string, pinned?: boolean) => Promise<void>;
45
50
 
46
51
  /**
47
- * 生成压缩后的消息
48
- * @returns 压缩后的消息
52
+ * re-fetch the data
49
53
  */
50
- // genShareUrl: () => string;
54
+ refreshSessions: () => Promise<void>;
55
+
51
56
  /**
52
- * @title 删除会话
53
- * @param index - 会话索引
54
- * @returns void
57
+ * remove session
58
+ * @param id - sessionId
55
59
  */
56
- removeSession: (sessionId: string) => void;
60
+ removeSession: (id: string) => void;
57
61
 
58
62
  switchBackToChat: () => void;
59
63
 
60
64
  /**
61
- * @title 切换会话
62
- * @param sessionId - 会话索引
63
- * @returns void
65
+ * switch session url
64
66
  */
65
67
  switchSession: (sessionId?: string) => void;
68
+ /**
69
+ * A custom hook that uses SWR to fetch sessions data.
70
+ */
71
+ useFetchSessions: () => SWRResponse<any>;
72
+
73
+ useSearchSessions: (keyword?: string) => SWRResponse<any>;
66
74
  }
67
75
 
68
76
  export const createSessionSlice: StateCreator<
@@ -72,17 +80,17 @@ export const createSessionSlice: StateCreator<
72
80
  SessionAction
73
81
  > = (set, get) => ({
74
82
  activeSession: (sessionId) => {
75
- set({ activeId: sessionId, activeTopicId: undefined }, false, t('activeSession'));
83
+ set({ activeId: sessionId }, false, t('activeSession'));
76
84
  },
77
85
 
78
- clearSessions: () => {
79
- set({ inbox: initInbox, sessions: {} }, false, t('clearSessions'));
86
+ clearSessions: async () => {
87
+ await sessionService.removeAllSessions();
88
+
89
+ get().refreshSessions();
80
90
  },
81
91
 
82
92
  createSession: async (agent) => {
83
- const { dispatchSession, switchSession } = get();
84
-
85
- const timestamp = Date.now();
93
+ const { switchSession, refreshSessions } = get();
86
94
 
87
95
  // 合并 settings 里的 defaultAgent
88
96
  const defaultAgent = merge(
@@ -90,66 +98,29 @@ export const createSessionSlice: StateCreator<
90
98
  settingsSelectors.defaultAgent(useGlobalStore.getState()),
91
99
  );
92
100
 
93
- const newSession: LobeAgentSession = merge(defaultAgent, {
94
- ...agent,
95
- createAt: timestamp,
96
- id: uuid(),
97
- updateAt: timestamp,
98
- });
101
+ const newSession: LobeAgentSession = merge(defaultAgent, agent);
99
102
 
100
- dispatchSession({ session: newSession, type: 'addSession' });
103
+ const id = await sessionService.createNewSession(LobeSessionType.Agent, newSession);
104
+ await refreshSessions();
101
105
 
102
- switchSession(newSession.id);
106
+ switchSession(id);
103
107
 
104
- return newSession.id;
108
+ return id;
105
109
  },
106
110
 
107
- dispatchSession: (payload) => {
108
- const { type, ...res } = payload;
109
-
110
- // 如果是 inbox 类型的 session
111
- if ('id' in res && res.id === INBOX_SESSION_ID) {
112
- const nextInbox = sessionsReducer({ inbox: get().inbox }, payload) as {
113
- inbox: LobeAgentSession;
114
- };
115
- set({ inbox: nextInbox.inbox }, false, t(`dispatchInbox/${type}`, res));
116
- } else {
117
- // 常规类型的session
118
- set(
119
- { sessions: sessionsReducer(get().sessions, payload) },
120
- false,
121
- t(`dispatchSessions/${type}`, res),
122
- );
123
- }
124
- },
125
- // TODO:暂时先不实现导入 inbox 的功能
126
- importInbox: () => {},
127
- importSessions: (importSessions) => {
128
- const { sessions } = get();
129
- set(
130
- {
131
- sessions: produce(sessions, (draft) => {
132
- for (const [id, session] of Object.entries(importSessions)) {
133
- // 如果已经存在,则跳过
134
- if (draft[id]) continue;
135
-
136
- draft[id] = session;
137
- }
138
- }),
139
- },
140
- false,
141
- t('importSessions', importSessions),
142
- );
143
- },
111
+ pinSession: async (sessionId, pinned) => {
112
+ await sessionService.updateSessionGroup(sessionId, pinned ? 'pinned' : 'default');
144
113
 
145
- pinSession: (sessionId, pinned) => {
146
- const nextValue = typeof pinned === 'boolean' ? pinned : !get().sessions[sessionId].pinned;
114
+ await get().refreshSessions();
115
+ },
147
116
 
148
- get().dispatchSession({ id: sessionId, pinned: nextValue, type: 'toggleSessionPinned' });
117
+ refreshSessions: async () => {
118
+ await mutate(FETCH_SESSIONS_KEY);
149
119
  },
150
120
 
151
- removeSession: (sessionId) => {
152
- get().dispatchSession({ id: sessionId, type: 'removeSession' });
121
+ removeSession: async (sessionId) => {
122
+ await sessionService.removeSession(sessionId);
123
+ await get().refreshSessions();
153
124
 
154
125
  if (sessionId === get().activeId) {
155
126
  get().switchSession();
@@ -172,4 +143,26 @@ export const createSessionSlice: StateCreator<
172
143
 
173
144
  router?.push(SESSION_CHAT_URL(sessionId, isMobile));
174
145
  },
146
+
147
+ useFetchSessions: () =>
148
+ useSWR<LobeSessions>(FETCH_SESSIONS_KEY, sessionService.getSessions, {
149
+ onSuccess: (data) => {
150
+ set(
151
+ {
152
+ fetchSessionsLoading: false,
153
+ sessions: data,
154
+ },
155
+ false,
156
+ t('useFetchSessions/onSuccess', data),
157
+ );
158
+ },
159
+ }),
160
+
161
+ useSearchSessions: (keyword) =>
162
+ useSWR<LobeSessions>(keyword, sessionService.searchSessions, {
163
+ onSuccess: (data) => {
164
+ set({ searchSessions: data }, false, t('useSearchSessions(success)', data));
165
+ },
166
+ revalidateOnFocus: false,
167
+ }),
175
168
  });
@@ -0,0 +1,19 @@
1
+ import { LobeAgentSession, LobeSessions, SessionGroupDefaultKeys } from '@/types/session';
2
+
3
+ import { initLobeSession } from './initialState';
4
+
5
+ export const getSessionPinned = (session: LobeAgentSession) =>
6
+ session.group === SessionGroupDefaultKeys.Pinned;
7
+
8
+ const getSessionById = (id: string, sessions: LobeSessions): LobeAgentSession => {
9
+ const session = sessions.find((s) => s.id === id);
10
+
11
+ if (!session) return initLobeSession;
12
+
13
+ return session;
14
+ };
15
+
16
+ export const sessionHelpers = {
17
+ getSessionById,
18
+ getSessionPinned,
19
+ };
@@ -11,26 +11,23 @@ export interface SessionState {
11
11
  * @description 当前正在编辑或查看的会话
12
12
  */
13
13
  activeId: string | undefined;
14
- // 默认会话
15
- inbox: LobeAgentSession;
14
+ fetchSessionsLoading: boolean;
16
15
  isMobile?: boolean;
17
16
  router?: AppRouterInstance;
18
17
  searchKeywords: string;
19
- sessions: Record<string, LobeAgentSession>;
18
+ sessions: LobeAgentSession[];
20
19
  topicSearchKeywords: string;
21
20
  }
22
21
 
23
22
  export const initLobeSession: LobeAgentSession = {
24
- chats: {},
25
23
  config: DEFAULT_AGENT_CONFIG,
26
- createAt: Date.now(),
27
- files: [],
24
+ createdAt: Date.now(),
28
25
  id: '',
29
26
  meta: DEFAULT_AGENT_META,
30
27
  type: LobeSessionType.Agent,
31
- updateAt: Date.now(),
28
+ updatedAt: Date.now(),
32
29
  };
33
- export const initInbox: LobeAgentSession = merge(initLobeSession, {
30
+ export const initInboxSession: LobeAgentSession = merge(initLobeSession, {
34
31
  id: 'inbox',
35
32
  meta: {
36
33
  avatar: DEFAULT_INBOX_AVATAR,
@@ -39,9 +36,9 @@ export const initInbox: LobeAgentSession = merge(initLobeSession, {
39
36
 
40
37
  export const initialSessionState: SessionState = {
41
38
  activeId: 'inbox',
42
- inbox: initInbox,
39
+ fetchSessionsLoading: true,
43
40
  isMobile: false,
44
41
  searchKeywords: '',
45
- sessions: {},
42
+ sessions: [],
46
43
  topicSearchKeywords: '',
47
44
  };
@@ -4,14 +4,13 @@ import { SessionStore } from '@/store/session';
4
4
  import { ConfigStateAgents, ConfigStateSessions } from '@/types/exportConfig';
5
5
  import { LobeAgentSession, LobeSessions } from '@/types/session';
6
6
 
7
- import { getSessionById } from './list';
7
+ import { sessionHelpers } from '../helpers';
8
8
 
9
- export const exportSessions = (s: SessionStore): ConfigStateSessions => ({
10
- inbox: s.inbox,
9
+ const exportSessions = (s: SessionStore): Pick<ConfigStateSessions, 'sessions'> => ({
11
10
  sessions: s.sessions,
12
11
  });
13
12
 
14
- export const exportAgents = (s: SessionStore): ConfigStateAgents => {
13
+ const exportAgents = (s: SessionStore): ConfigStateAgents => {
15
14
  return {
16
15
  sessions: transform(s.sessions, (result: LobeSessions, value, key) => {
17
16
  // 移除 chats 和 topics
@@ -20,9 +19,13 @@ export const exportAgents = (s: SessionStore): ConfigStateAgents => {
20
19
  };
21
20
  };
22
21
 
23
- export const getExportAgent =
22
+ const getExportAgent =
24
23
  (id: string) =>
25
- (s: SessionStore): LobeAgentSession => {
26
- const session = getSessionById(id)(s);
27
- return { ...session, chats: {}, topics: {} };
28
- };
24
+ (s: SessionStore): LobeAgentSession =>
25
+ sessionHelpers.getSessionById(id, s.sessions);
26
+
27
+ export const sessionExportSelectors = {
28
+ exportAgents,
29
+ exportSessions,
30
+ getExportAgent,
31
+ };
@@ -1,35 +1,2 @@
1
- import { INBOX_SESSION_ID } from '@/const/session';
2
- import { SessionStore } from '@/store/session';
3
-
4
- import { exportAgents, exportSessions, getExportAgent } from './export';
5
- import {
6
- currentSession,
7
- currentSessionSafe,
8
- getSessionById,
9
- getSessionMetaById,
10
- hasConversion,
11
- hasPinnedSessionList,
12
- hasSessionList,
13
- pinnedSessionList,
14
- sessionList,
15
- unpinnedSessionList,
16
- } from './list';
17
-
18
- const isInboxSession = (s: SessionStore) => s.activeId === INBOX_SESSION_ID;
19
-
20
- export const sessionSelectors = {
21
- currentSession,
22
- currentSessionSafe,
23
- exportAgents,
24
- exportSessions,
25
- getExportAgent,
26
- getSessionById,
27
- getSessionMetaById,
28
- hasConversion,
29
- hasPinnedSessionList,
30
- hasSessionList,
31
- isInboxSession,
32
- pinnedSessionList,
33
- sessionList,
34
- unpinnedSessionList,
35
- };
1
+ export * from './export';
2
+ export * from './list';