@lobehub/chat 0.115.1 → 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 (91) hide show
  1. package/.env.example +1 -1
  2. package/CHANGELOG.md +50 -0
  3. package/Dockerfile +1 -1
  4. package/README.md +6 -6
  5. package/README.zh-CN.md +6 -6
  6. package/docs/Deployment/Environment-Variable.md +2 -2
  7. package/docs/Deployment/Environment-Variable.zh-CN.md +2 -2
  8. package/next.config.mjs +0 -4
  9. package/package.json +1 -1
  10. package/src/app/api/config/route.ts +15 -0
  11. package/src/app/api/market/AgentMarket.ts +25 -0
  12. package/src/app/api/market/[id]/route.ts +6 -3
  13. package/src/app/api/market/route.ts +6 -3
  14. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +2 -1
  15. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +2 -1
  16. package/src/app/settings/agent/Agent.tsx +2 -1
  17. package/src/app/settings/common/Common.tsx +2 -1
  18. package/src/app/settings/llm/LLM/index.tsx +2 -1
  19. package/src/app/settings/tts/TTS/index.tsx +2 -1
  20. package/src/components/FileList/EditableFileList.tsx +1 -1
  21. package/src/components/FileList/index.tsx +3 -45
  22. package/src/components/FileList/style.ts +0 -23
  23. package/src/config/__tests__/client.test.ts +0 -23
  24. package/src/config/__tests__/server.test.ts +26 -1
  25. package/src/config/client.ts +4 -15
  26. package/src/config/server.ts +14 -1
  27. package/src/const/settings.ts +0 -3
  28. package/src/const/url.ts +1 -18
  29. package/src/features/AgentSetting/AgentConfig/index.tsx +2 -1
  30. package/src/features/AgentSetting/AgentMeta/index.tsx +2 -1
  31. package/src/features/AgentSetting/AgentTTS/index.tsx +3 -2
  32. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +2 -1
  33. package/src/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -1
  34. package/src/features/Conversation/ChatList/Extras/TTS/index.tsx +3 -2
  35. package/src/features/Conversation/index.tsx +2 -1
  36. package/src/features/PluginDevModal/LocalForm.tsx +3 -2
  37. package/src/hooks/useSTT.ts +2 -1
  38. package/src/hooks/useTTS.ts +2 -1
  39. package/src/layout/GlobalLayout/StoreHydration.tsx +5 -0
  40. package/src/services/__tests__/chat.test.ts +1 -1
  41. package/src/services/__tests__/global.test.ts +49 -32
  42. package/src/services/_header.ts +2 -2
  43. package/src/services/_url.ts +1 -0
  44. package/src/services/config.ts +2 -1
  45. package/src/services/global.ts +9 -0
  46. package/src/services/market.ts +21 -0
  47. package/src/store/chat/initialState.ts +9 -47
  48. package/src/store/chat/selectors.ts +3 -0
  49. package/src/store/chat/{actions/enhance.test.ts → slices/enchance/action.test.ts} +2 -2
  50. package/src/store/chat/{actions/message.test.ts → slices/message/action.test.ts} +1 -1
  51. package/src/store/chat/{actions/message.ts → slices/message/action.ts} +2 -2
  52. package/src/store/chat/slices/message/initialState.ts +29 -0
  53. package/src/store/chat/{reducers/message.test.ts → slices/message/reducer.test.ts} +2 -2
  54. package/src/store/chat/{selectors/__tests__/chat.test.ts → slices/message/selectors.test.ts} +1 -1
  55. package/src/store/chat/{selectors/chat.ts → slices/message/selectors.ts} +2 -2
  56. package/src/store/chat/{actions/share.ts → slices/share/action.ts} +2 -2
  57. package/src/store/chat/slices/share/initialState.ts +7 -0
  58. package/src/store/chat/{actions/plugin.ts → slices/tool/action.ts} +1 -3
  59. package/src/store/chat/slices/tool/initialState.ts +7 -0
  60. package/src/store/chat/{actions/topic.test.ts → slices/topic/action.test.ts} +1 -1
  61. package/src/store/chat/{actions/topic.ts → slices/topic/action.ts} +2 -1
  62. package/src/store/chat/slices/topic/initialState.ts +23 -0
  63. package/src/store/chat/{selectors/__tests__/topic.test.ts → slices/topic/selectors.test.ts} +1 -1
  64. package/src/store/chat/{selectors/topic.ts → slices/topic/selectors.ts} +1 -1
  65. package/src/store/chat/store.ts +5 -5
  66. package/src/store/global/helpers.ts +1 -1
  67. package/src/store/global/index.ts +0 -1
  68. package/src/store/global/initialState.ts +6 -59
  69. package/src/store/global/selectors.ts +2 -2
  70. package/src/store/global/slices/{common.ts → common/action.ts} +12 -3
  71. package/src/store/global/slices/common/initialState.ts +55 -0
  72. package/src/store/global/{selectors/preference.ts → slices/common/selectors.ts} +1 -1
  73. package/src/store/global/{selectors/__snapshots__/settings.test.ts.snap → slices/settings/__snapshots__/selectors.test.ts.snap} +0 -1
  74. package/src/store/global/slices/{settings.ts → settings/action.ts} +12 -4
  75. package/src/store/global/slices/settings/initialState.ts +15 -0
  76. package/src/store/global/{selectors/settings.test.ts → slices/settings/selectors.test.ts} +9 -6
  77. package/src/store/global/{selectors/settings.ts → slices/settings/selectors.ts} +4 -4
  78. package/src/store/global/store.ts +4 -18
  79. package/src/store/market/action.ts +14 -10
  80. package/src/store/session/slices/agent/selectors.ts +2 -1
  81. package/src/store/session/slices/session/action.ts +2 -1
  82. package/src/tools/dalle/Render/ToolBar.tsx +2 -1
  83. package/src/types/settings.ts +4 -29
  84. package/src/components/FileList/FileGrid.tsx +0 -36
  85. package/src/services/agentMarket.ts +0 -21
  86. package/src/store/chat/selectors/index.ts +0 -3
  87. package/src/store/global/slices/agent.ts +0 -34
  88. /package/src/store/chat/{actions/enhance.ts → slices/enchance/action.ts} +0 -0
  89. /package/src/store/chat/{selectors/enchance.ts → slices/enchance/selectors.ts} +0 -0
  90. /package/src/store/chat/{reducers/message.ts → slices/message/reducer.ts} +0 -0
  91. /package/src/store/chat/{actions/plugin.test.ts → slices/tool/action.test.ts} +0 -0
package/src/const/url.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import urlJoin from 'url-join';
2
2
 
3
- import { getClientConfig } from '@/config/client';
4
3
  import { Locales } from '@/locales/resources';
5
4
 
6
5
  import pkg from '../../package.json';
@@ -15,7 +14,7 @@ export const ABOUT = pkg.homepage;
15
14
  export const FEEDBACK = pkg.bugs.url;
16
15
  export const DISCORD = 'https://discord.gg/AYFPHvv2jT';
17
16
 
18
- export const { PLUGINS_INDEX_URL, AGENTS_INDEX_URL } = getClientConfig();
17
+ export const PLUGINS_INDEX_URL = 'https://chat-plugins.lobehub.com';
19
18
 
20
19
  export const getPluginIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = PLUGINS_INDEX_URL) => {
21
20
  if (checkLang(lang)) return baseUrl;
@@ -23,22 +22,6 @@ export const getPluginIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = PLUGI
23
22
  return urlJoin(baseUrl, `index.${lang}.json`);
24
23
  };
25
24
 
26
- export const getAgentIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = AGENTS_INDEX_URL) => {
27
- if (checkLang(lang)) return baseUrl;
28
-
29
- return urlJoin(baseUrl, `index.${lang}.json`);
30
- };
31
-
32
- export const getAgentJSON = (
33
- identifier: string,
34
- lang: Locales = DEFAULT_LANG,
35
- baseUrl = AGENTS_INDEX_URL,
36
- ) => {
37
- if (checkLang(lang)) return urlJoin(baseUrl, `${identifier}.json`);
38
-
39
- return urlJoin(baseUrl, `${identifier}.${lang}.json`);
40
- };
41
-
42
25
  export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents';
43
26
  export const AGENTS_INDEX_GITHUB_ISSUE = urlJoin(AGENTS_INDEX_GITHUB, 'issues/new');
44
27
 
@@ -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';
@@ -8,12 +8,16 @@ import { useEffectAfterSessionHydrated, useSessionStore } from '@/store/session'
8
8
  const StoreHydration = memo(() => {
9
9
  const router = useRouter();
10
10
 
11
+ const useFetchGlobalConfig = useGlobalStore((s) => s.useFetchGlobalConfig);
12
+
11
13
  useEffect(() => {
12
14
  // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
13
15
  useSessionStore.persist.rehydrate();
14
16
  useGlobalStore.persist.rehydrate();
15
17
  }, []);
16
18
 
19
+ useFetchGlobalConfig();
20
+
17
21
  const { mobile } = useResponsive();
18
22
 
19
23
  useEffectAfterSessionHydrated(
@@ -29,6 +33,7 @@ const StoreHydration = memo(() => {
29
33
  },
30
34
  [router],
31
35
  );
36
+
32
37
  useEffect(() => {
33
38
  router.prefetch('/chat');
34
39
  router.prefetch('/market');
@@ -1,6 +1,6 @@
1
1
  import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
2
  import { act } from '@testing-library/react';
3
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { describe, expect, it, vi } from 'vitest';
4
4
 
5
5
  import { VISION_MODEL_WHITE_LIST } from '@/const/llm';
6
6
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
@@ -9,49 +9,66 @@ beforeEach(() => {
9
9
  });
10
10
 
11
11
  describe('GlobalService', () => {
12
- it('should return the latest version when fetch is successful', async () => {
13
- // Arrange
14
- const mockVersion = '1.0.0';
15
- (fetch as Mock).mockResolvedValue({
16
- json: () => Promise.resolve({ 'dist-tags': { latest: mockVersion } }),
12
+ describe('getLatestVersion', () => {
13
+ it('should return the latest version when fetch is successful', async () => {
14
+ // Arrange
15
+ const mockVersion = '1.0.0';
16
+ (fetch as Mock).mockResolvedValue({
17
+ json: () => Promise.resolve({ 'dist-tags': { latest: mockVersion } }),
18
+ });
19
+
20
+ // Act
21
+ const version = await globalService.getLatestVersion();
22
+
23
+ // Assert
24
+ expect(fetch).toHaveBeenCalledWith('https://registry.npmmirror.com/@lobehub/chat');
25
+ expect(version).toBe(mockVersion);
17
26
  });
18
27
 
19
- // Act
20
- const version = await globalService.getLatestVersion();
28
+ it('should return undefined if the latest version is not found in the response', async () => {
29
+ // Arrange
30
+ (fetch as Mock).mockResolvedValue({
31
+ json: () => Promise.resolve({}),
32
+ });
21
33
 
22
- // Assert
23
- expect(fetch).toHaveBeenCalledWith('https://registry.npmmirror.com/@lobehub/chat');
24
- expect(version).toBe(mockVersion);
25
- });
34
+ // Act
35
+ const version = await globalService.getLatestVersion();
26
36
 
27
- it('should return undefined if the latest version is not found in the response', async () => {
28
- // Arrange
29
- (fetch as Mock).mockResolvedValue({
30
- json: () => Promise.resolve({}),
37
+ // Assert
38
+ expect(version).toBeUndefined();
31
39
  });
32
40
 
33
- // Act
34
- const version = await globalService.getLatestVersion();
41
+ it('should throw an error when the fetch call fails', async () => {
42
+ // Arrange
43
+ (fetch as Mock).mockRejectedValue(new Error('Network error'));
35
44
 
36
- // Assert
37
- expect(version).toBeUndefined();
38
- });
45
+ // Act & Assert
46
+ await expect(globalService.getLatestVersion()).rejects.toThrow('Network error');
47
+ });
39
48
 
40
- it('should throw an error when the fetch call fails', async () => {
41
- // Arrange
42
- (fetch as Mock).mockRejectedValue(new Error('Network error'));
49
+ it('should handle non-JSON responses gracefully', async () => {
50
+ // Arrange
51
+ (fetch as Mock).mockResolvedValue({
52
+ json: () => Promise.reject(new SyntaxError('Unexpected token < in JSON at position 0')),
53
+ });
43
54
 
44
- // Act & Assert
45
- await expect(globalService.getLatestVersion()).rejects.toThrow('Network error');
55
+ // Act & Assert
56
+ await expect(globalService.getLatestVersion()).rejects.toThrow(SyntaxError);
57
+ });
46
58
  });
47
59
 
48
- it('should handle non-JSON responses gracefully', async () => {
49
- // Arrange
50
- (fetch as Mock).mockResolvedValue({
51
- json: () => Promise.reject(new SyntaxError('Unexpected token < in JSON at position 0')),
52
- });
60
+ describe('ServerConfig', () => {
61
+ it('should return the serverConfig when fetch is successful', async () => {
62
+ // Arrange
63
+ (fetch as Mock).mockResolvedValue({
64
+ json: () => Promise.resolve({ customModelName: 'abc' }),
65
+ });
53
66
 
54
- // Act & Assert
55
- await expect(globalService.getLatestVersion()).rejects.toThrow(SyntaxError);
67
+ // Act
68
+ const config = await globalService.getGlobalConfig();
69
+
70
+ // Assert
71
+ expect(config).toEqual({ customModelName: 'abc' });
72
+ });
56
73
  });
57
74
  });
@@ -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,4 +1,5 @@
1
1
  export const URLS = {
2
+ config: '/api/config',
2
3
  market: '/api/market',
3
4
  plugins: '/api/plugins',
4
5
  };
@@ -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,3 +1,6 @@
1
+ import { URLS } from '@/services/_url';
2
+ import { GlobalServerConfig } from '@/types/settings';
3
+
1
4
  const VERSION_URL = 'https://registry.npmmirror.com/@lobehub/chat';
2
5
 
3
6
  class GlobalService {
@@ -10,6 +13,12 @@ class GlobalService {
10
13
 
11
14
  return data['dist-tags']?.latest;
12
15
  };
16
+
17
+ getGlobalConfig = async (): Promise<GlobalServerConfig> => {
18
+ const res = await fetch(URLS.config);
19
+
20
+ return res.json();
21
+ };
13
22
  }
14
23
 
15
24
  export const globalService = new GlobalService();
@@ -0,0 +1,21 @@
1
+ import { URLS } from '@/services/_url';
2
+ import { LobeChatAgentsMarketIndex } from '@/types/market';
3
+
4
+ class MarketService {
5
+ getAgentList = async (locale: string): Promise<LobeChatAgentsMarketIndex> => {
6
+ const res = await fetch(`${URLS.market}?locale=${locale}`);
7
+
8
+ return res.json();
9
+ };
10
+
11
+ /**
12
+ * 请求助手 manifest
13
+ */
14
+ getAgentManifest = async (identifier: string, locale: string) => {
15
+ if (!identifier) return;
16
+ const res = await fetch(`${URLS.market}/${identifier}?locale=${locale}`);
17
+
18
+ return res.json();
19
+ };
20
+ }
21
+ export const marketService = new MarketService();
@@ -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