@lobehub/chat 0.115.2 → 0.115.3

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 (62) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +2 -1
  4. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +2 -1
  5. package/src/app/settings/agent/Agent.tsx +2 -1
  6. package/src/app/settings/common/Common.tsx +2 -1
  7. package/src/app/settings/llm/LLM/index.tsx +2 -1
  8. package/src/app/settings/tts/TTS/index.tsx +2 -1
  9. package/src/features/AgentSetting/AgentConfig/index.tsx +2 -1
  10. package/src/features/AgentSetting/AgentMeta/index.tsx +2 -1
  11. package/src/features/AgentSetting/AgentTTS/index.tsx +3 -2
  12. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +2 -1
  13. package/src/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -1
  14. package/src/features/Conversation/ChatList/Extras/TTS/index.tsx +3 -2
  15. package/src/features/Conversation/index.tsx +2 -1
  16. package/src/features/PluginDevModal/LocalForm.tsx +3 -2
  17. package/src/hooks/useSTT.ts +2 -1
  18. package/src/hooks/useTTS.ts +2 -1
  19. package/src/services/_header.ts +2 -2
  20. package/src/services/config.ts +2 -1
  21. package/src/store/chat/initialState.ts +9 -47
  22. package/src/store/chat/selectors.ts +3 -0
  23. package/src/store/chat/{actions/enhance.test.ts → slices/enchance/action.test.ts} +2 -2
  24. package/src/store/chat/{actions/message.test.ts → slices/message/action.test.ts} +1 -1
  25. package/src/store/chat/{actions/message.ts → slices/message/action.ts} +2 -2
  26. package/src/store/chat/slices/message/initialState.ts +29 -0
  27. package/src/store/chat/{reducers/message.test.ts → slices/message/reducer.test.ts} +2 -2
  28. package/src/store/chat/{selectors/__tests__/chat.test.ts → slices/message/selectors.test.ts} +1 -1
  29. package/src/store/chat/{selectors/chat.ts → slices/message/selectors.ts} +2 -2
  30. package/src/store/chat/{actions/share.ts → slices/share/action.ts} +2 -2
  31. package/src/store/chat/slices/share/initialState.ts +7 -0
  32. package/src/store/chat/{actions/plugin.ts → slices/tool/action.ts} +1 -3
  33. package/src/store/chat/slices/tool/initialState.ts +7 -0
  34. package/src/store/chat/{actions/topic.test.ts → slices/topic/action.test.ts} +1 -1
  35. package/src/store/chat/{actions/topic.ts → slices/topic/action.ts} +2 -1
  36. package/src/store/chat/slices/topic/initialState.ts +23 -0
  37. package/src/store/chat/{selectors/__tests__/topic.test.ts → slices/topic/selectors.test.ts} +1 -1
  38. package/src/store/chat/{selectors/topic.ts → slices/topic/selectors.ts} +1 -1
  39. package/src/store/chat/store.ts +5 -5
  40. package/src/store/global/helpers.ts +1 -1
  41. package/src/store/global/index.ts +0 -1
  42. package/src/store/global/initialState.ts +6 -61
  43. package/src/store/global/selectors.ts +2 -2
  44. package/src/store/global/slices/{common.ts → common/action.ts} +3 -3
  45. package/src/store/global/slices/common/initialState.ts +55 -0
  46. package/src/store/global/{selectors/preference.ts → slices/common/selectors.ts} +1 -1
  47. package/src/store/global/slices/{settings.ts → settings/action.ts} +12 -4
  48. package/src/store/global/slices/settings/initialState.ts +15 -0
  49. package/src/store/global/{selectors/settings.test.ts → slices/settings/selectors.test.ts} +2 -2
  50. package/src/store/global/{selectors/settings.ts → slices/settings/selectors.ts} +3 -4
  51. package/src/store/global/store.ts +4 -18
  52. package/src/store/session/slices/agent/selectors.ts +2 -1
  53. package/src/store/session/slices/session/action.ts +2 -1
  54. package/src/tools/dalle/Render/ToolBar.tsx +2 -1
  55. package/src/types/settings.ts +0 -29
  56. package/src/store/chat/selectors/index.ts +0 -3
  57. package/src/store/global/slices/agent.ts +0 -34
  58. /package/src/store/chat/{actions/enhance.ts → slices/enchance/action.ts} +0 -0
  59. /package/src/store/chat/{selectors/enchance.ts → slices/enchance/selectors.ts} +0 -0
  60. /package/src/store/chat/{reducers/message.ts → slices/message/reducer.ts} +0 -0
  61. /package/src/store/chat/{actions/plugin.test.ts → slices/tool/action.test.ts} +0 -0
  62. /package/src/store/global/{selectors/__snapshots__/settings.test.ts.snap → slices/settings/__snapshots__/selectors.test.ts.snap} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.115.3](https://github.com/lobehub/lobe-chat/compare/v0.115.2...v0.115.3)
6
+
7
+ <sup>Released on **2023-12-23**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Refactor and clean global store and chat store.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Refactor and clean global store and chat store, closes [#774](https://github.com/lobehub/lobe-chat/issues/774) ([6120042](https://github.com/lobehub/lobe-chat/commit/6120042))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 0.115.2](https://github.com/lobehub/lobe-chat/compare/v0.115.1...v0.115.2)
6
31
 
7
32
  <sup>Released on **2023-12-23**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.115.2",
3
+ "version": "0.115.3",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -3,7 +3,8 @@ import isEqual from 'fast-deep-equal';
3
3
  import { memo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
- import { preferenceSelectors, useGlobalStore } from '@/store/global';
6
+ import { useGlobalStore } from '@/store/global';
7
+ import { preferenceSelectors } from '@/store/global/selectors';
7
8
  import { useSessionStore } from '@/store/session';
8
9
  import { sessionSelectors } from '@/store/session/selectors';
9
10
 
@@ -5,7 +5,8 @@ import { Flexbox } from 'react-layout-kit';
5
5
  import { shallow } from 'zustand/shallow';
6
6
 
7
7
  import { useChatStore } from '@/store/chat';
8
- import { settingsSelectors, useGlobalStore } from '@/store/global';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { settingsSelectors } from '@/store/global/selectors';
9
10
  import { useSessionStore } from '@/store/session';
10
11
  import { sessionHelpers } from '@/store/session/helpers';
11
12
  import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
@@ -2,9 +2,10 @@ import isEqual from 'fast-deep-equal';
2
2
  import { memo } from 'react';
3
3
 
4
4
  import AgentSetting from '@/features/AgentSetting';
5
- import { settingsSelectors, useGlobalStore } from '@/store/global';
5
+ import { useGlobalStore } from '@/store/global';
6
6
  import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
7
7
  import { SettingsTabs } from '@/store/global/initialState';
8
+ import { settingsSelectors } from '@/store/global/selectors';
8
9
 
9
10
  const Agent = memo(() => {
10
11
  useSwitchSideBarOnInit(SettingsTabs.Agent);
@@ -12,7 +12,8 @@ import AvatarWithUpload from '@/features/AvatarWithUpload';
12
12
  import { localeOptions } from '@/locales/options';
13
13
  import { useChatStore } from '@/store/chat';
14
14
  import { useFileStore } from '@/store/file';
15
- import { settingsSelectors, useGlobalStore } from '@/store/global';
15
+ import { useGlobalStore } from '@/store/global';
16
+ import { settingsSelectors } from '@/store/global/selectors';
16
17
  import { useSessionStore } from '@/store/session';
17
18
  import { useToolStore } from '@/store/tool';
18
19
  import { switchLang } from '@/utils/switchLang';
@@ -7,7 +7,8 @@ import { memo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
9
  import { FORM_STYLE } from '@/const/layoutTokens';
10
- import { settingsSelectors, useEffectAfterGlobalHydrated, useGlobalStore } from '@/store/global';
10
+ import { useEffectAfterGlobalHydrated, useGlobalStore } from '@/store/global';
11
+ import { settingsSelectors } from '@/store/global/selectors';
11
12
 
12
13
  import Checker from './Checker';
13
14
 
@@ -7,7 +7,8 @@ import { memo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
9
  import { FORM_STYLE } from '@/const/layoutTokens';
10
- import { settingsSelectors, useGlobalStore } from '@/store/global';
10
+ import { useGlobalStore } from '@/store/global';
11
+ import { settingsSelectors } from '@/store/global/selectors';
11
12
 
12
13
  import { opeanaiSTTOptions, opeanaiTTSOptions, sttOptions } from './options';
13
14
 
@@ -8,7 +8,8 @@ import { memo, useEffect } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
10
  import { FORM_STYLE } from '@/const/layoutTokens';
11
- import { settingsSelectors, useGlobalStore } from '@/store/global';
11
+ import { useGlobalStore } from '@/store/global';
12
+ import { settingsSelectors } from '@/store/global/selectors';
12
13
 
13
14
  import { useStore } from '../store';
14
15
 
@@ -8,7 +8,8 @@ import { memo, useMemo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
10
  import { FORM_STYLE } from '@/const/layoutTokens';
11
- import { settingsSelectors, useGlobalStore } from '@/store/global';
11
+ import { useGlobalStore } from '@/store/global';
12
+ import { settingsSelectors } from '@/store/global/selectors';
12
13
 
13
14
  import { useStore } from '../store';
14
15
  import { SessionLoadingState } from '../store/initialState';
@@ -8,10 +8,11 @@ import { memo, useEffect } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
10
  import { FORM_STYLE } from '@/const/layoutTokens';
11
- import SelectWithTTSPreview from '@/features/AgentSetting/AgentTTS/SelectWithTTSPreview';
12
- import { settingsSelectors, useGlobalStore } from '@/store/global';
11
+ import { useGlobalStore } from '@/store/global';
12
+ import { settingsSelectors } from '@/store/global/selectors';
13
13
 
14
14
  import { useStore } from '../store';
15
+ import SelectWithTTSPreview from './SelectWithTTSPreview';
15
16
  import { ttsOptions } from './options';
16
17
 
17
18
  const TTS_SETTING_KEY = 'tts';
@@ -5,7 +5,8 @@ import { BrainCog } from 'lucide-react';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
- import { settingsSelectors, useGlobalStore } from '@/store/global';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { settingsSelectors } from '@/store/global/selectors';
9
10
  import { useSessionStore } from '@/store/session';
10
11
  import { agentSelectors } from '@/store/session/selectors';
11
12
  import { LanguageModel } from '@/types/llm';
@@ -6,7 +6,8 @@ import { useTranslation } from 'react-i18next';
6
6
  import { Center, Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import { useChatStore } from '@/store/chat';
9
- import { settingsSelectors, useGlobalStore } from '@/store/global';
9
+ import { useGlobalStore } from '@/store/global';
10
+ import { settingsSelectors } from '@/store/global/selectors';
10
11
 
11
12
  import { FormAction } from './style';
12
13
 
@@ -1,9 +1,10 @@
1
1
  import { memo, useMemo } from 'react';
2
2
  import { Md5 } from 'ts-md5';
3
3
 
4
- import { settingsSelectors, useGlobalStore } from '@/store/global';
4
+ import { useGlobalStore } from '@/store/global';
5
+ import { settingsSelectors } from '@/store/global/selectors';
5
6
  import { useSessionStore } from '@/store/session';
6
- import { agentSelectors } from '@/store/session/slices/agent';
7
+ import { agentSelectors } from '@/store/session/selectors';
7
8
 
8
9
  import FilePlayer from './FilePlayer';
9
10
  import InitPlayer, { TTSProps } from './InitPlayer';
@@ -5,7 +5,8 @@ import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import SafeSpacing from '@/components/SafeSpacing';
8
- import { settingsSelectors, useGlobalStore } from '@/store/global';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { settingsSelectors } from '@/store/global/selectors';
9
10
 
10
11
  import ChatList from './ChatList';
11
12
  import ChatScrollAnchor from './ScrollAnchor';
@@ -4,9 +4,10 @@ import dynamic from 'next/dynamic';
4
4
  import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
- import { settingsSelectors, useGlobalStore } from '@/store/global';
7
+ import { useGlobalStore } from '@/store/global';
8
+ import { settingsSelectors } from '@/store/global/selectors';
8
9
  import { useToolStore } from '@/store/tool';
9
- import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
10
+ import { pluginSelectors } from '@/store/tool/selectors';
10
11
 
11
12
  const EmojiPicker = dynamic(() => import('@lobehub/ui/es/EmojiPicker'), { ssr: false });
12
13
 
@@ -10,7 +10,8 @@ import { SWRConfiguration } from 'swr';
10
10
 
11
11
  import { createHeaderWithOpenAI } from '@/services/_header';
12
12
  import { OPENAI_URLS } from '@/services/_url';
13
- import { settingsSelectors, useGlobalStore } from '@/store/global';
13
+ import { useGlobalStore } from '@/store/global';
14
+ import { settingsSelectors } from '@/store/global/selectors';
14
15
  import { useSessionStore } from '@/store/session';
15
16
  import { agentSelectors } from '@/store/session/selectors';
16
17
 
@@ -11,7 +11,8 @@ import isEqual from 'fast-deep-equal';
11
11
 
12
12
  import { createHeaderWithOpenAI } from '@/services/_header';
13
13
  import { OPENAI_URLS, TTS_URL } from '@/services/_url';
14
- import { settingsSelectors, useGlobalStore } from '@/store/global';
14
+ import { useGlobalStore } from '@/store/global';
15
+ import { settingsSelectors } from '@/store/global/selectors';
15
16
  import { useSessionStore } from '@/store/session';
16
17
  import { agentSelectors } from '@/store/session/selectors';
17
18
  import { TTSServer } from '@/types/agent';
@@ -11,8 +11,8 @@ import { useGlobalStore } from '@/store/global';
11
11
  export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {
12
12
  const openai = useGlobalStore.getState().settings.languageModel.openAI;
13
13
 
14
- const apiKey = openai.OPENAI_API_KEY || useGlobalStore.getState().settings.OPENAI_API_KEY || '';
15
- const endpoint = openai.endpoint || useGlobalStore.getState().settings.endpoint || '';
14
+ const apiKey = openai.OPENAI_API_KEY || '';
15
+ const endpoint = openai.endpoint || '';
16
16
 
17
17
  // eslint-disable-next-line no-undef
18
18
  const result: HeadersInit = {
@@ -1,7 +1,8 @@
1
1
  import { messageService } from '@/services/message';
2
2
  import { sessionService } from '@/services/session';
3
3
  import { topicService } from '@/services/topic';
4
- import { settingsSelectors, useGlobalStore } from '@/store/global';
4
+ import { useGlobalStore } from '@/store/global';
5
+ import { settingsSelectors } from '@/store/global/selectors';
5
6
  import { useSessionStore } from '@/store/session';
6
7
  import { sessionExportSelectors, sessionSelectors } from '@/store/session/selectors';
7
8
  import { ChatMessage } from '@/types/chatMessage';
@@ -1,51 +1,13 @@
1
- import { ChatMessage } from '@/types/chatMessage';
2
- import { ChatTopic } from '@/types/topic';
1
+ import { ChatMessageState, initialMessageState } from './slices/message/initialState';
2
+ import { ChatShareState, initialShareState } from './slices/share/initialState';
3
+ import { ChatToolState, initialToolState } from './slices/tool/initialState';
4
+ import { ChatTopicState, initialTopicState } from './slices/topic/initialState';
3
5
 
4
- export interface ChatStoreState {
5
- abortController?: AbortController;
6
- /**
7
- * @title 当前活动的会话
8
- * @description 当前正在编辑或查看的会话
9
- */
10
- activeId: string;
11
- activeTopicId?: string;
12
- /**
13
- * @deprecated
14
- */
15
- chatLoadingId?: string;
16
- dalleImageLoading: Record<string, boolean>;
17
-
18
- inputMessage: string;
19
- isSearchingTopic: boolean;
20
- messageLoadingIds: [];
21
- messages: ChatMessage[];
22
- /**
23
- * whether messages have fetched
24
- */
25
- messagesInit: boolean;
26
-
27
- searchTopics: ChatTopic[];
28
- shareLoading?: boolean;
29
- topicLoadingId?: string;
30
- topicRenamingId?: string;
31
- topicSearchKeywords: string;
32
- topics: ChatTopic[];
33
- /**
34
- * whether topics have fetched
35
- */
36
- topicsInit: boolean;
37
- }
6
+ export type ChatStoreState = ChatTopicState & ChatMessageState & ChatToolState & ChatShareState;
38
7
 
39
8
  export const initialState: ChatStoreState = {
40
- activeId: 'inbox',
41
- dalleImageLoading: {},
42
- inputMessage: '',
43
- isSearchingTopic: false,
44
- messageLoadingIds: [],
45
- messages: [],
46
- messagesInit: false,
47
- searchTopics: [],
48
- topicSearchKeywords: '',
49
- topics: [],
50
- topicsInit: false,
9
+ ...initialMessageState,
10
+ ...initialTopicState,
11
+ ...initialToolState,
12
+ ...initialShareState,
51
13
  };
@@ -0,0 +1,3 @@
1
+ export { chatEnhanceSelectors } from './slices/enchance/selectors';
2
+ export { chatSelectors } from './slices/message/selectors';
3
+ export { topicSelectors } from './slices/topic/selectors';
@@ -1,4 +1,4 @@
1
- import { act, renderHook, waitFor } from '@testing-library/react';
1
+ import { act, renderHook } from '@testing-library/react';
2
2
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
4
  import { chainLangDetect } from '@/chains/langDetect';
@@ -6,7 +6,7 @@ import { chainTranslate } from '@/chains/translate';
6
6
  import { chatService } from '@/services/chat';
7
7
  import { messageService } from '@/services/message';
8
8
 
9
- import { useChatStore } from '../store';
9
+ import { useChatStore } from '../../store';
10
10
 
11
11
  // Mock messageService 和 chatService
12
12
  vi.mock('@/services/message', () => ({
@@ -9,7 +9,7 @@ import { topicService } from '@/services/topic';
9
9
  import { chatSelectors } from '@/store/chat/selectors';
10
10
  import { ChatMessage } from '@/types/chatMessage';
11
11
 
12
- import { useChatStore } from '../store';
12
+ import { useChatStore } from '../../store';
13
13
 
14
14
  // Mock service
15
15
  vi.mock('@/services/message', () => ({
@@ -22,8 +22,8 @@ import { fetchSSE } from '@/utils/fetch';
22
22
  import { isFunctionMessageAtStart, testFunctionMessageAtEnd } from '@/utils/message';
23
23
  import { setNamespace } from '@/utils/storeDebug';
24
24
 
25
- import { MessageDispatch, messagesReducer } from '../reducers/message';
26
- import { chatSelectors } from '../selectors';
25
+ import { chatSelectors } from '../../selectors';
26
+ import { MessageDispatch, messagesReducer } from './reducer';
27
27
 
28
28
  const n = setNamespace('message');
29
29
 
@@ -0,0 +1,29 @@
1
+ import { ChatMessage } from '@/types/chatMessage';
2
+
3
+ export interface ChatMessageState {
4
+ abortController?: AbortController;
5
+ /**
6
+ * @title 当前活动的会话
7
+ * @description 当前正在编辑或查看的会话
8
+ */
9
+ activeId: string;
10
+ /**
11
+ * @deprecated
12
+ */
13
+ chatLoadingId?: string;
14
+ inputMessage: string;
15
+ messageLoadingIds: [];
16
+ messages: ChatMessage[];
17
+ /**
18
+ * whether messages have fetched
19
+ */
20
+ messagesInit: boolean;
21
+ }
22
+
23
+ export const initialMessageState: ChatMessageState = {
24
+ activeId: 'inbox',
25
+ inputMessage: '',
26
+ messageLoadingIds: [],
27
+ messages: [],
28
+ messagesInit: false,
29
+ };
@@ -1,6 +1,6 @@
1
- import { ChatMessage, ChatMessageMap } from '@/types/chatMessage';
1
+ import { ChatMessage } from '@/types/chatMessage';
2
2
 
3
- import { MessageDispatch, messagesReducer } from './message';
3
+ import { MessageDispatch, messagesReducer } from './reducer';
4
4
 
5
5
  describe('messagesReducer', () => {
6
6
  let initialState: ChatMessage[];
@@ -9,7 +9,7 @@ import { ChatMessage } from '@/types/chatMessage';
9
9
  import { MetaData } from '@/types/meta';
10
10
  import { merge } from '@/utils/merge';
11
11
 
12
- import { chatSelectors } from '../chat';
12
+ import { chatSelectors } from './selectors';
13
13
 
14
14
  vi.mock('i18next', () => ({
15
15
  t: vi.fn((key) => key), // Simplified mock return value
@@ -10,8 +10,8 @@ import { ChatMessage } from '@/types/chatMessage';
10
10
  import { MetaData } from '@/types/meta';
11
11
  import { merge } from '@/utils/merge';
12
12
 
13
- import { chatHelpers } from '../helpers';
14
- import type { ChatStore } from '../store';
13
+ import { chatHelpers } from '../../helpers';
14
+ import type { ChatStore } from '../../store';
15
15
 
16
16
  const getMeta = (message: ChatMessage) => {
17
17
  switch (message.role) {
@@ -8,8 +8,8 @@ import { useSessionStore } from '@/store/session';
8
8
  import { agentSelectors } from '@/store/session/selectors';
9
9
  import { ShareGPTConversation } from '@/types/share';
10
10
 
11
- import { chatSelectors } from '../selectors';
12
- import { ChatStore } from '../store';
11
+ import { chatSelectors } from '../../selectors';
12
+ import { ChatStore } from '../../store';
13
13
 
14
14
  interface ShareMessage {
15
15
  from: 'human' | 'gpt';
@@ -0,0 +1,7 @@
1
+ export interface ChatShareState {
2
+ shareLoading?: boolean;
3
+ }
4
+
5
+ export const initialShareState: ChatShareState = {
6
+ shareLoading: false,
7
+ };
@@ -11,7 +11,7 @@ import { ChatPluginPayload } from '@/types/chatMessage';
11
11
  import { OpenAIFunctionCall } from '@/types/openai/functionCall';
12
12
  import { setNamespace } from '@/utils/storeDebug';
13
13
 
14
- import { chatSelectors } from '../selectors';
14
+ import { chatSelectors } from '../../selectors';
15
15
 
16
16
  const n = setNamespace('plugin');
17
17
 
@@ -85,7 +85,6 @@ export const chatPlugin: StateCreator<
85
85
  const chats = chatSelectors.currentChats(get());
86
86
  await coreProcessMessage(chats, id);
87
87
  },
88
-
89
88
  triggerFunctionCall: async (id) => {
90
89
  const { invokeDefaultTypePlugin, invokeBuiltinTool, refreshMessages } = get();
91
90
 
@@ -146,7 +145,6 @@ export const chatPlugin: StateCreator<
146
145
  }
147
146
  }
148
147
  },
149
-
150
148
  updatePluginState: async (id, key, value) => {
151
149
  const { refreshMessages } = get();
152
150
 
@@ -0,0 +1,7 @@
1
+ export interface ChatToolState {
2
+ dalleImageLoading: Record<string, boolean>;
3
+ }
4
+
5
+ export const initialToolState: ChatToolState = {
6
+ dalleImageLoading: {},
7
+ };
@@ -9,7 +9,7 @@ import { topicService } from '@/services/topic';
9
9
  import { ChatMessage } from '@/types/chatMessage';
10
10
  import { ChatTopic } from '@/types/topic';
11
11
 
12
- import { useChatStore } from '../store';
12
+ import { useChatStore } from '../../store';
13
13
 
14
14
  // Mock topicService 和 messageService
15
15
  vi.mock('@/services/topic', () => ({
@@ -16,7 +16,8 @@ import { ChatMessage } from '@/types/chatMessage';
16
16
  import { ChatTopic } from '@/types/topic';
17
17
  import { setNamespace } from '@/utils/storeDebug';
18
18
 
19
- import { chatSelectors, topicSelectors } from '../selectors';
19
+ import { chatSelectors } from '../message/selectors';
20
+ import { topicSelectors } from './selectors';
20
21
 
21
22
  const n = setNamespace('topic');
22
23
 
@@ -0,0 +1,23 @@
1
+ import { ChatTopic } from '@/types/topic';
2
+
3
+ export interface ChatTopicState {
4
+ activeTopicId?: string;
5
+ isSearchingTopic: boolean;
6
+ searchTopics: ChatTopic[];
7
+ topicLoadingId?: string;
8
+ topicRenamingId?: string;
9
+ topicSearchKeywords: string;
10
+ topics: ChatTopic[];
11
+ /**
12
+ * whether topics have fetched
13
+ */
14
+ topicsInit: boolean;
15
+ }
16
+
17
+ export const initialTopicState: ChatTopicState = {
18
+ isSearchingTopic: false,
19
+ searchTopics: [],
20
+ topicSearchKeywords: '',
21
+ topics: [],
22
+ topicsInit: false,
23
+ };
@@ -4,7 +4,7 @@ import { ChatStore } from '@/store/chat';
4
4
  import { initialState } from '@/store/chat/initialState';
5
5
  import { merge } from '@/utils/merge';
6
6
 
7
- import { topicSelectors } from '..';
7
+ import { topicSelectors } from '../../selectors';
8
8
 
9
9
  const initialStore = initialState as ChatStore;
10
10
 
@@ -1,6 +1,6 @@
1
1
  import { ChatTopic } from '@/types/topic';
2
2
 
3
- import { ChatStore } from '../store';
3
+ import { ChatStore } from '../../store';
4
4
 
5
5
  const currentTopics = (s: ChatStore): ChatTopic[] => s.topics;
6
6
 
@@ -6,12 +6,12 @@ import { StateCreator } from 'zustand/vanilla';
6
6
  import { createHyperStorage } from '@/store/middleware/createHyperStorage';
7
7
  import { isDev } from '@/utils/env';
8
8
 
9
- import { ChatEnhanceAction, chatEnhance } from './actions/enhance';
10
- import { ChatMessageAction, chatMessage } from './actions/message';
11
- import { ChatPluginAction, chatPlugin } from './actions/plugin';
12
- import { ShareAction, chatShare } from './actions/share';
13
- import { ChatTopicAction, chatTopic } from './actions/topic';
14
9
  import { ChatStoreState, initialState } from './initialState';
10
+ import { ChatEnhanceAction, chatEnhance } from './slices/enchance/action';
11
+ import { ChatMessageAction, chatMessage } from './slices/message/action';
12
+ import { ShareAction, chatShare } from './slices/share/action';
13
+ import { ChatPluginAction, chatPlugin } from './slices/tool/action';
14
+ import { ChatTopicAction, chatTopic } from './slices/topic/action';
15
15
 
16
16
  export interface ChatStoreAction
17
17
  extends ChatMessageAction,
@@ -1,4 +1,4 @@
1
- import { settingsSelectors } from './selectors';
1
+ import { settingsSelectors } from './slices/settings/selectors';
2
2
  import { useGlobalStore } from './store';
3
3
 
4
4
  const getCurrentLanguage = () => settingsSelectors.currentLanguage(useGlobalStore.getState());
@@ -1,3 +1,2 @@
1
1
  export * from './hooks';
2
- export * from './selectors';
3
2
  export * from './store';
@@ -1,66 +1,11 @@
1
- import { DEFAULT_SETTINGS } from '@/const/settings';
2
- import type { GlobalServerConfig, GlobalSettings } from '@/types/settings';
1
+ import { GlobalCommonState, initialCommonState } from './slices/common/initialState';
2
+ import { GlobalSettingsState, initialSettingsState } from './slices/settings/initialState';
3
3
 
4
- export enum SidebarTabKey {
5
- Chat = 'chat',
6
- Market = 'market',
7
- Setting = 'settings',
8
- }
4
+ export { SettingsTabs, SidebarTabKey } from './slices/common/initialState';
9
5
 
10
- export enum SettingsTabs {
11
- Agent = 'agent',
12
- Common = 'common',
13
- LLM = 'llm',
14
- TTS = 'tts',
15
- }
16
-
17
- export interface Guide {
18
- // Topic 引导
19
- topic?: boolean;
20
- }
21
-
22
- export interface GlobalPreference {
23
- guide?: Guide;
24
- inputHeight: number;
25
- mobileShowTopic?: boolean;
26
- sessionGroupKeys: string[];
27
- sessionsWidth: number;
28
- showChatSideBar?: boolean;
29
- showSessionPanel?: boolean;
30
- showSystemRole?: boolean;
31
- }
32
-
33
- export interface GlobalState {
34
- hasNewVersion?: boolean;
35
- latestVersion?: string;
36
- /**
37
- * 用户偏好的 UI 状态
38
- * @localStorage
39
- */
40
- preference: GlobalPreference;
41
- serverConfig: GlobalServerConfig;
42
- /**
43
- * @localStorage
44
- * 用户设置
45
- */
46
- settings: GlobalSettings;
47
- settingsTab: SettingsTabs;
48
- sidebarKey: SidebarTabKey;
49
- }
6
+ export type GlobalState = GlobalCommonState & GlobalSettingsState;
50
7
 
51
8
  export const initialState: GlobalState = {
52
- preference: {
53
- guide: {},
54
- inputHeight: 200,
55
- mobileShowTopic: false,
56
- sessionGroupKeys: ['pinned', 'sessionList'],
57
- sessionsWidth: 320,
58
- showChatSideBar: true,
59
- showSessionPanel: true,
60
- showSystemRole: false,
61
- },
62
- serverConfig: {},
63
- settings: DEFAULT_SETTINGS,
64
- settingsTab: SettingsTabs.Common,
65
- sidebarKey: SidebarTabKey.Chat,
9
+ ...initialCommonState,
10
+ ...initialSettingsState,
66
11
  };
@@ -1,2 +1,2 @@
1
- export * from './selectors/preference';
2
- export * from './selectors/settings';
1
+ export * from './slices/common/selectors';
2
+ export * from './slices/settings/selectors';
@@ -5,12 +5,12 @@ import type { StateCreator } from 'zustand/vanilla';
5
5
 
6
6
  import { CURRENT_VERSION } from '@/const/version';
7
7
  import { globalService } from '@/services/global';
8
+ import type { GlobalStore } from '@/store/global';
8
9
  import type { GlobalServerConfig } from '@/types/settings';
9
10
  import { merge } from '@/utils/merge';
10
11
  import { setNamespace } from '@/utils/storeDebug';
11
12
 
12
- import type { GlobalPreference, GlobalState, Guide, SidebarTabKey } from '../initialState';
13
- import type { GlobalStore } from '../store';
13
+ import type { GlobalCommonState, GlobalPreference, Guide, SidebarTabKey } from './initialState';
14
14
 
15
15
  const n = setNamespace('settings');
16
16
 
@@ -66,7 +66,7 @@ export const createCommonSlice: StateCreator<
66
66
  },
67
67
  updatePreference: (preference, action) => {
68
68
  set(
69
- produce((draft: GlobalState) => {
69
+ produce((draft: GlobalCommonState) => {
70
70
  draft.preference = merge(draft.preference, preference);
71
71
  }),
72
72
  false,
@@ -0,0 +1,55 @@
1
+ export enum SidebarTabKey {
2
+ Chat = 'chat',
3
+ Market = 'market',
4
+ Setting = 'settings',
5
+ }
6
+
7
+ export enum SettingsTabs {
8
+ Agent = 'agent',
9
+ Common = 'common',
10
+ LLM = 'llm',
11
+ TTS = 'tts',
12
+ }
13
+
14
+ export interface Guide {
15
+ // Topic 引导
16
+ topic?: boolean;
17
+ }
18
+
19
+ export interface GlobalPreference {
20
+ guide?: Guide;
21
+ inputHeight: number;
22
+ mobileShowTopic?: boolean;
23
+ sessionGroupKeys: string[];
24
+ sessionsWidth: number;
25
+ showChatSideBar?: boolean;
26
+ showSessionPanel?: boolean;
27
+ showSystemRole?: boolean;
28
+ }
29
+
30
+ export interface GlobalCommonState {
31
+ hasNewVersion?: boolean;
32
+ latestVersion?: string;
33
+ /**
34
+ * 用户偏好的 UI 状态
35
+ * @localStorage
36
+ */
37
+ preference: GlobalPreference;
38
+ settingsTab: SettingsTabs;
39
+ sidebarKey: SidebarTabKey;
40
+ }
41
+
42
+ export const initialCommonState: GlobalCommonState = {
43
+ preference: {
44
+ guide: {},
45
+ inputHeight: 200,
46
+ mobileShowTopic: false,
47
+ sessionGroupKeys: ['pinned', 'sessionList'],
48
+ sessionsWidth: 320,
49
+ showChatSideBar: true,
50
+ showSessionPanel: true,
51
+ showSystemRole: false,
52
+ },
53
+ settingsTab: SettingsTabs.Common,
54
+ sidebarKey: SidebarTabKey.Chat,
55
+ };
@@ -1,6 +1,6 @@
1
1
  import { GlobalStore } from '@/store/global';
2
2
 
3
- import { initialState } from '../initialState';
3
+ import { initialState } from '../../initialState';
4
4
 
5
5
  const sessionGroupKeys = (s: GlobalStore): string[] =>
6
6
  s.preference.sessionGroupKeys || initialState.preference.sessionGroupKeys;
@@ -5,13 +5,13 @@ import { DeepPartial } from 'utility-types';
5
5
  import type { StateCreator } from 'zustand/vanilla';
6
6
 
7
7
  import { DEFAULT_AGENT, DEFAULT_SETTINGS } from '@/const/settings';
8
+ import type { GlobalStore } from '@/store/global';
8
9
  import { SettingsTabs } from '@/store/global/initialState';
10
+ import { LobeAgentSettings } from '@/types/session';
9
11
  import type { GlobalSettings, OpenAIConfig } from '@/types/settings';
10
12
  import { merge } from '@/utils/merge';
11
13
  import { setNamespace } from '@/utils/storeDebug';
12
14
 
13
- import type { GlobalStore } from '../store';
14
-
15
15
  const n = setNamespace('settings');
16
16
 
17
17
  /**
@@ -24,6 +24,7 @@ export interface SettingsAction {
24
24
  */
25
25
  resetSettings: () => void;
26
26
  setOpenAIConfig: (config: Partial<OpenAIConfig>) => void;
27
+
27
28
  /**
28
29
  * 设置部分配置设置
29
30
  * @param settings - 部分配置设置
@@ -35,6 +36,7 @@ export interface SettingsAction {
35
36
  * @param themeMode - 主题模式
36
37
  */
37
38
  switchThemeMode: (themeMode: ThemeMode) => void;
39
+ updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => void;
38
40
  }
39
41
 
40
42
  export const createSettingsSlice: StateCreator<
@@ -46,7 +48,7 @@ export const createSettingsSlice: StateCreator<
46
48
  importAppSettings: (importAppSettings) => {
47
49
  const { setSettings } = get();
48
50
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
- const { OPENAI_API_KEY: _, password: __, ...settings } = importAppSettings;
51
+ const { password: _, ...settings } = importAppSettings;
50
52
 
51
53
  setSettings({
52
54
  ...settings,
@@ -78,8 +80,14 @@ export const createSettingsSlice: StateCreator<
78
80
  switchSettingTabs: (tab) => {
79
81
  set({ settingsTab: tab });
80
82
  },
81
-
82
83
  switchThemeMode: (themeMode) => {
83
84
  get().setSettings({ themeMode });
84
85
  },
86
+ updateDefaultAgent: (agent) => {
87
+ const settings = produce(get().settings, (draft: GlobalSettings) => {
88
+ draft.defaultAgent = merge(draft.defaultAgent, agent);
89
+ });
90
+
91
+ set({ settings }, false, n('updateDefaultAgent', agent));
92
+ },
85
93
  });
@@ -0,0 +1,15 @@
1
+ import { DEFAULT_SETTINGS } from '@/const/settings';
2
+ import type { GlobalServerConfig, GlobalSettings } from '@/types/settings';
3
+
4
+ export interface GlobalSettingsState {
5
+ serverConfig: GlobalServerConfig;
6
+ /**
7
+ * @localStorage
8
+ */
9
+ settings: GlobalSettings;
10
+ }
11
+
12
+ export const initialSettingsState: GlobalSettingsState = {
13
+ serverConfig: {},
14
+ settings: DEFAULT_SETTINGS,
15
+ };
@@ -1,7 +1,7 @@
1
1
  import { LanguageModel } from '@/types/llm';
2
2
 
3
- import { GlobalStore } from '../store';
4
- import { settingsSelectors } from './settings';
3
+ import { GlobalStore } from '../../store';
4
+ import { settingsSelectors } from './selectors';
5
5
 
6
6
  describe('settingsSelectors', () => {
7
7
  describe('currentSettings', () => {
@@ -12,7 +12,7 @@ import { CustomModels, GlobalSettings } from '@/types/settings';
12
12
  import { isOnServerSide } from '@/utils/env';
13
13
  import { merge } from '@/utils/merge';
14
14
 
15
- import { GlobalStore } from '../store';
15
+ import { GlobalStore } from '../../store';
16
16
 
17
17
  const currentSettings = (s: GlobalStore) => merge(DEFAULT_SETTINGS, s.settings);
18
18
 
@@ -24,8 +24,7 @@ const defaultAgentConfig = (s: GlobalStore) => merge(DEFAULT_AGENT_CONFIG, defau
24
24
 
25
25
  const defaultAgentMeta = (s: GlobalStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta);
26
26
 
27
- const openAIAPIKeySelectors = (s: GlobalStore) =>
28
- s.settings.languageModel.openAI.OPENAI_API_KEY || s.settings.OPENAI_API_KEY;
27
+ const openAIAPIKeySelectors = (s: GlobalStore) => s.settings.languageModel.openAI.OPENAI_API_KEY;
29
28
 
30
29
  const openAIProxyUrlSelectors = (s: GlobalStore) => s.settings.languageModel.openAI.endpoint;
31
30
 
@@ -68,7 +67,7 @@ const modelListSelectors = (s: GlobalStore) => {
68
67
 
69
68
  export const exportSettings = (s: GlobalStore) => {
70
69
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
- const { OPENAI_API_KEY: _, password: __, ...settings } = s.settings;
70
+ const { password: _, ...settings } = s.settings;
72
71
 
73
72
  return settings as GlobalSettings;
74
73
  };
@@ -9,18 +9,16 @@ import { isDev } from '@/utils/env';
9
9
 
10
10
  import { createHyperStorage } from '../middleware/createHyperStorage';
11
11
  import { type GlobalState, initialState } from './initialState';
12
- import { type AgentAction, createAgentSlice } from './slices/agent';
13
- import { type CommonAction, createCommonSlice } from './slices/common';
14
- import { type SettingsAction, createSettingsSlice } from './slices/settings';
12
+ import { type CommonAction, createCommonSlice } from './slices/common/action';
13
+ import { type SettingsAction, createSettingsSlice } from './slices/settings/action';
15
14
 
16
15
  // =============== 聚合 createStoreFn ============ //
17
16
 
18
- export type GlobalStore = CommonAction & GlobalState & AgentAction & SettingsAction;
17
+ export type GlobalStore = CommonAction & GlobalState & SettingsAction;
19
18
 
20
19
  const createStore: StateCreator<GlobalStore, [['zustand/devtools', never]]> = (...parameters) => ({
21
20
  ...initialState,
22
21
  ...createCommonSlice(...parameters),
23
- ...createAgentSlice(...parameters),
24
22
  ...createSettingsSlice(...parameters),
25
23
  });
26
24
 
@@ -38,24 +36,12 @@ const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
38
36
  if (!draft.defaultAgent) {
39
37
  draft.defaultAgent = DEFAULT_AGENT;
40
38
  }
41
- delete draft.enableMaxTokens;
42
- delete draft.enableHistoryCount;
43
- delete draft.historyCount;
44
- delete draft.enableCompressThreshold;
45
- delete draft.compressThreshold;
46
39
 
47
40
  // migration to new data model
48
41
  if (!draft.languageModel) {
49
42
  draft.languageModel = {
50
- openAI: {
51
- ...DEFAULT_LLM_CONFIG.openAI,
52
- OPENAI_API_KEY: draft.OPENAI_API_KEY || DEFAULT_LLM_CONFIG.openAI.OPENAI_API_KEY,
53
- endpoint: draft.endpoint || DEFAULT_LLM_CONFIG.openAI.OPENAI_API_KEY,
54
- },
43
+ openAI: DEFAULT_LLM_CONFIG.openAI,
55
44
  };
56
-
57
- delete draft.OPENAI_API_KEY;
58
- delete draft.endpoint;
59
45
  }
60
46
  }),
61
47
  };
@@ -4,7 +4,8 @@ import { t } from 'i18next';
4
4
  import { DEFAULT_OPENAI_MODEL_LIST, VISION_MODEL_WHITE_LIST } from '@/const/llm';
5
5
  import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR, DEFAULT_INBOX_AVATAR } from '@/const/meta';
6
6
  import { DEFAULT_AGENT_CONFIG, DEFAUTT_AGENT_TTS_CONFIG } from '@/const/settings';
7
- import { settingsSelectors, useGlobalStore } from '@/store/global';
7
+ import { useGlobalStore } from '@/store/global';
8
+ import { settingsSelectors } from '@/store/global/selectors';
8
9
  import { SessionStore } from '@/store/session';
9
10
  import { LobeAgentTTSConfig } from '@/types/agent';
10
11
  import { LanguageModel } from '@/types/llm';
@@ -5,7 +5,8 @@ import { StateCreator } from 'zustand/vanilla';
5
5
  import { INBOX_SESSION_ID } from '@/const/session';
6
6
  import { SESSION_CHAT_URL } from '@/const/url';
7
7
  import { sessionService } from '@/services/session';
8
- import { settingsSelectors, useGlobalStore } from '@/store/global';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { settingsSelectors } from '@/store/global/selectors';
9
10
  import { SessionStore } from '@/store/session';
10
11
  import {
11
12
  LobeAgentSession,
@@ -5,7 +5,8 @@ import { Flexbox } from 'react-layout-kit';
5
5
 
6
6
  import { useChatStore } from '@/store/chat';
7
7
  import { chatEnhanceSelectors } from '@/store/chat/selectors';
8
- import { settingsSelectors, useGlobalStore } from '@/store/global';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { settingsSelectors } from '@/store/global/selectors';
9
10
  import { DallEImageItem } from '@/types/tool/dalle';
10
11
 
11
12
  interface ToolBarProps {
@@ -5,37 +5,8 @@ import { LocaleMode } from '@/types/locale';
5
5
  import type { LobeAgentSession } from '@/types/session';
6
6
 
7
7
  export interface GlobalBaseSettings {
8
- /**
9
- * @deprecated
10
- */
11
- OPENAI_API_KEY?: string;
12
-
13
8
  avatar: string;
14
- /**
15
- * @deprecated
16
- */
17
- compressThreshold?: number;
18
- /**
19
- * @deprecated
20
- */
21
- enableCompressThreshold?: boolean;
22
- /**
23
- * @deprecated
24
- */
25
- enableHistoryCount?: boolean;
26
- /**
27
- * @deprecated
28
- */
29
- enableMaxTokens?: boolean;
30
- /**
31
- * @deprecated
32
- */
33
- endpoint?: string;
34
9
  fontSize: number;
35
- /**
36
- * @deprecated
37
- */
38
- historyCount?: number;
39
10
  language: LocaleMode;
40
11
  neutralColor?: NeutralColors;
41
12
  password: string;
@@ -1,3 +0,0 @@
1
- export { chatSelectors } from './chat';
2
- export { chatEnhanceSelectors } from './enchance';
3
- export { topicSelectors } from './topic';
@@ -1,34 +0,0 @@
1
- import { produce } from 'immer';
2
- import { DeepPartial } from 'utility-types';
3
- import type { StateCreator } from 'zustand/vanilla';
4
-
5
- import { LobeAgentSettings } from '@/types/session';
6
- import type { GlobalSettings } from '@/types/settings';
7
- import { merge } from '@/utils/merge';
8
- import { setNamespace } from '@/utils/storeDebug';
9
-
10
- import type { GlobalStore } from '../store';
11
-
12
- const n = setNamespace('settings');
13
-
14
- /**
15
- * 设置操作
16
- */
17
- export interface AgentAction {
18
- updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => void;
19
- }
20
-
21
- export const createAgentSlice: StateCreator<
22
- GlobalStore,
23
- [['zustand/devtools', never]],
24
- [],
25
- AgentAction
26
- > = (set, get) => ({
27
- updateDefaultAgent: (agent) => {
28
- const settings = produce(get().settings, (draft: GlobalSettings) => {
29
- draft.defaultAgent = merge(draft.defaultAgent, agent);
30
- });
31
-
32
- set({ settings }, false, n('updateDefaultAgent', agent));
33
- },
34
- });