@lobehub/chat 0.102.1 → 0.102.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 (39) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +119 -38
  3. package/README.zh-CN.md +99 -44
  4. package/docs/Development-Guide/Data-Store/Selectors.zh-CN.md +68 -0
  5. package/package.json +3 -3
  6. package/src/app/api/openai/errorResponse.test.ts +55 -0
  7. package/src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx +3 -2
  8. package/src/app/chat/features/ChatHeader/PluginTag/index.tsx +2 -1
  9. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -1
  10. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +86 -0
  11. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +5 -1
  12. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +2 -1
  13. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/index.tsx +2 -1
  14. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +2 -1
  15. package/src/app/chat/features/Conversation/index.tsx +3 -3
  16. package/src/app/chat/features/Conversation/usePluginsInit.ts +14 -0
  17. package/src/app/settings/llm/LLM/Checker.tsx +2 -2
  18. package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +4 -3
  19. package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +4 -3
  20. package/src/features/AgentSetting/AgentPlugin/PluginSettings.tsx +3 -2
  21. package/src/features/AgentSetting/store/action.ts +5 -5
  22. package/src/features/PluginDevModal/MetaForm.tsx +2 -1
  23. package/src/features/PluginSettingsModal/index.tsx +2 -1
  24. package/src/services/chat.ts +84 -0
  25. package/src/services/plugin.ts +15 -33
  26. package/src/store/plugin/index.ts +0 -1
  27. package/src/store/plugin/selectors.test.ts +185 -0
  28. package/src/store/plugin/selectors.ts +6 -3
  29. package/src/store/plugin/slices/customPlugin/action.ts +3 -3
  30. package/src/store/plugin/slices/plugin/action.ts +44 -15
  31. package/src/store/session/slices/chat/actions/message.ts +2 -2
  32. package/src/store/session/slices/chat/actions/plugin.ts +2 -2
  33. package/src/store/session/slices/chat/actions/topic.ts +2 -2
  34. package/src/store/session/slices/chat/actions/translate.ts +9 -7
  35. package/src/utils/fetch.ts +0 -3
  36. package/tests/setup.ts +6 -1
  37. package/src/app/chat/features/Conversation/ChatList/Extras/AudioPlayer.tsx +0 -139
  38. package/src/services/chatModel.ts +0 -48
  39. package/src/services/pluginMarket.ts +0 -13
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { ChatErrorType, ErrorResponse, ErrorType } from '@/types/fetch';
4
+
5
+ import { createErrorResponse } from './errorResponse';
6
+
7
+ describe('createErrorResponse', () => {
8
+ // 测试各种错误类型的状态码
9
+ it('returns a 401 status for NoAPIKey error type', () => {
10
+ const errorType = ChatErrorType.NoAPIKey;
11
+ const response = createErrorResponse(errorType);
12
+ expect(response.status).toBe(401);
13
+ });
14
+
15
+ it('returns a 401 status for InvalidAccessCode error type', () => {
16
+ const errorType = ChatErrorType.InvalidAccessCode;
17
+ const response = createErrorResponse(errorType);
18
+ expect(response.status).toBe(401);
19
+ });
20
+
21
+ it('returns a 577 status for OpenAIBizError error type', () => {
22
+ const errorType = ChatErrorType.OpenAIBizError;
23
+ const response = createErrorResponse(errorType);
24
+ expect(response.status).toBe(577);
25
+ });
26
+
27
+ // 测试默认情况
28
+ it('returns the same error type as status for unknown error types', () => {
29
+ const errorType = 500; // 假设500是一个未知的错误类型
30
+ const response = createErrorResponse(errorType);
31
+ expect(response.status).toBe(errorType);
32
+ });
33
+
34
+ // 测试返回的Response对象是否包含正确的body和errorType
35
+ it('returns a Response object with the correct body and errorType', () => {
36
+ const errorType = ChatErrorType.NoAPIKey;
37
+ const body = { message: 'No API key provided' };
38
+ const response = createErrorResponse(errorType, body);
39
+ return response.json().then((data) => {
40
+ expect(data).toEqual({
41
+ body,
42
+ errorType,
43
+ });
44
+ });
45
+ });
46
+
47
+ // 测试没有提供body时,返回的Response对象的body是否为undefined
48
+ it('returns a Response object with an undefined body when no body is provided', () => {
49
+ const errorType = ChatErrorType.NoAPIKey;
50
+ const response = createErrorResponse(errorType);
51
+ return response.json().then((data) => {
52
+ expect(data.body).toBeUndefined();
53
+ });
54
+ });
55
+ });
@@ -6,7 +6,8 @@ import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import ManifestPreviewer from '@/components/ManifestPreviewer';
9
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
9
+ import { usePluginStore } from '@/store/plugin';
10
+ import { pluginSelectors } from '@/store/plugin/selectors';
10
11
  import { useSessionStore } from '@/store/session';
11
12
 
12
13
  interface PluginStatusProps {
@@ -19,7 +20,7 @@ const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
19
20
  const [status, isCustom, fetchPluginManifest] = usePluginStore((s) => [
20
21
  pluginSelectors.getPluginManifestLoadingStatus(id)(s),
21
22
  pluginSelectors.isCustomPlugin(id)(s),
22
- s.fetchPluginManifest,
23
+ s.installPlugin,
23
24
  ]);
24
25
 
25
26
  const manifest = usePluginStore(pluginSelectors.getPluginManifestById(id));
@@ -5,7 +5,8 @@ import isEqual from 'fast-deep-equal';
5
5
  import { LucideToyBrick } from 'lucide-react';
6
6
  import { memo } from 'react';
7
7
 
8
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
8
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
9
+ import { pluginSelectors } from '@/store/plugin/selectors';
9
10
 
10
11
  import PluginStatus from './PluginStatus';
11
12
 
@@ -7,7 +7,8 @@ import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import PluginSettingsConfig from '@/features/PluginSettings';
10
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
10
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
11
+ import { pluginSelectors } from '@/store/plugin/selectors';
11
12
  import { useSessionStore } from '@/store/session';
12
13
 
13
14
  import { ErrorActionContainer, useStyles } from '../style';
@@ -0,0 +1,86 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { useSessionStore } from '@/store/session';
5
+ import { agentSelectors } from '@/store/session/selectors';
6
+ import { ChatMessage } from '@/types/chatMessage';
7
+
8
+ import { AssistantMessageExtra } from './Assistant';
9
+
10
+ // Mock TTS and Translate components
11
+ vi.mock('./TTS', () => ({
12
+ default: vi.fn(() => <div>TTS Component</div>),
13
+ }));
14
+ vi.mock('./Translate', () => ({
15
+ default: vi.fn(() => <div>Translate Component</div>),
16
+ }));
17
+
18
+ // Mock dependencies
19
+ vi.mock('@/store/session', () => ({
20
+ useSessionStore: vi.fn(),
21
+ }));
22
+ vi.mock('@/store/session/selectors', () => ({
23
+ agentSelectors: {
24
+ currentAgentModel: vi.fn(),
25
+ },
26
+ }));
27
+
28
+ const mockData: ChatMessage = {
29
+ content: 'test-content',
30
+ createAt: 0,
31
+ id: 'abc',
32
+ meta: { avatar: '', backgroundColor: '', description: '', tags: [], title: '' },
33
+ role: 'assistant',
34
+ updateAt: 0,
35
+ };
36
+
37
+ describe('AssistantMessageExtra', () => {
38
+ beforeEach(() => {
39
+ // Set default mock return values
40
+ (useSessionStore as unknown as Mock).mockImplementation(() => ({
41
+ chatLoadingId: null,
42
+ }));
43
+ (agentSelectors.currentAgentModel as Mock).mockReturnValue('defaultModel');
44
+ });
45
+
46
+ it('should not render content if extra is undefined', async () => {
47
+ render(<AssistantMessageExtra {...mockData} />);
48
+ expect(screen.queryByText('defaultModel')).toBeNull();
49
+ expect(screen.queryByText('TTS Component')).toBeNull();
50
+ expect(screen.queryByText('Translate Component')).toBeNull();
51
+ });
52
+
53
+ it('should not render content if extra is defined but does not contain fromModel, tts, or translate', async () => {
54
+ render(<AssistantMessageExtra {...mockData} />);
55
+ expect(screen.queryByText('defaultModel')).toBeNull();
56
+ expect(screen.queryByText('TTS Component')).toBeNull();
57
+ expect(screen.queryByText('Translate Component')).toBeNull();
58
+ });
59
+
60
+ it('should render Tag component if extra.fromModel exists and does not match the current model', async () => {
61
+ render(<AssistantMessageExtra {...mockData} extra={{ fromModel: 'otherModel' }} />);
62
+
63
+ expect(screen.getByText('otherModel')).toBeInTheDocument();
64
+ });
65
+
66
+ it('should render TTS component if extra.fromModel and extra.tts coexist', async () => {
67
+ render(<AssistantMessageExtra {...mockData} extra={{ fromModel: 'otherModel', tts: {} }} />);
68
+
69
+ expect(screen.getByText('otherModel')).toBeInTheDocument();
70
+ expect(screen.getByText('TTS Component')).toBeInTheDocument();
71
+ });
72
+
73
+ it('should render Translate component if extra.translate exists', async () => {
74
+ render(<AssistantMessageExtra {...mockData} extra={{ translate: {} }} />);
75
+ expect(screen.getByText('Translate Component')).toBeInTheDocument();
76
+ });
77
+
78
+ it('should receive the correct loading attribute if loading is true for TTS and Translate components', async () => {
79
+ (useSessionStore as unknown as Mock).mockImplementation(() => ({
80
+ chatLoadingId: 'test-id',
81
+ }));
82
+ render(<AssistantMessageExtra {...mockData} extra={{ translate: {}, tts: {} }} />);
83
+ expect(screen.getByText('TTS Component')).toBeInTheDocument();
84
+ expect(screen.getByText('Translate Component')).toBeInTheDocument();
85
+ });
86
+ });
@@ -17,7 +17,11 @@ export const AssistantMessageExtra: RenderMessageExtra = memo<ChatMessage>(
17
17
  const loading = useSessionStore((s) => s.chatLoadingId === id);
18
18
 
19
19
  const showModelTag = extra?.fromModel && model !== extra?.fromModel;
20
- const showExtra = extra?.showModelTag || extra?.translate || extra?.tts;
20
+ const showTranslate = !!extra?.translate;
21
+ const showTTS = !!extra?.tts;
22
+
23
+ const showExtra = showModelTag || showTranslate || showTTS;
24
+
21
25
  if (!showExtra) return;
22
26
 
23
27
  return (
@@ -4,7 +4,8 @@ import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import PluginSettingsModal from '@/features/PluginSettingsModal';
7
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
7
+ import { usePluginStore } from '@/store/plugin';
8
+ import { pluginSelectors } from '@/store/plugin/selectors';
8
9
 
9
10
  const Settings = memo<{ id: string }>(({ id }) => {
10
11
  const item = usePluginStore(pluginSelectors.getPluginManifestById(id));
@@ -14,7 +14,8 @@ import { memo, useState } from 'react';
14
14
  import { useTranslation } from 'react-i18next';
15
15
  import { Flexbox } from 'react-layout-kit';
16
16
 
17
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
17
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
18
+ import { pluginSelectors } from '@/store/plugin/selectors';
18
19
 
19
20
  import PluginResult from './PluginResultJSON';
20
21
  import Settings from './Settings';
@@ -2,7 +2,8 @@ import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
2
  import { Skeleton } from 'antd';
3
3
  import { memo, useRef, useState } from 'react';
4
4
 
5
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
5
+ import { usePluginStore } from '@/store/plugin';
6
+ import { pluginSelectors } from '@/store/plugin/selectors';
6
7
  import { useSessionStore } from '@/store/session';
7
8
  import { chatSelectors } from '@/store/session/selectors';
8
9
 
@@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import SafeSpacing from '@/components/SafeSpacing';
8
- import { usePluginStore } from '@/store/plugin';
9
8
 
10
9
  import ChatList from './ChatList';
11
10
  import ChatScrollAnchor from './ScrollAnchor';
11
+ import { usePluginsInit } from './usePluginsInit';
12
12
 
13
13
  const useStyles = createStyles(
14
14
  ({ css, responsive, stylish }) => css`
@@ -31,8 +31,8 @@ const Conversation = memo<ConversationProps>(({ mobile, chatInput }) => {
31
31
  const { t } = useTranslation('chat');
32
32
  const { styles } = useStyles();
33
33
 
34
- const useFetchPluginList = usePluginStore((s) => s.useFetchPluginList);
35
- useFetchPluginList();
34
+ // fetch plugin list and init plugins
35
+ usePluginsInit();
36
36
 
37
37
  return (
38
38
  <Flexbox flex={1} style={{ position: 'relative' }}>
@@ -0,0 +1,14 @@
1
+ import { usePluginStore } from '@/store/plugin';
2
+ import { useSessionStore } from '@/store/session';
3
+ import { agentSelectors } from '@/store/session/slices/agentConfig';
4
+
5
+ export const usePluginsInit = () => {
6
+ const [plugins] = useSessionStore((s) => [agentSelectors.currentAgentPlugins(s)]);
7
+
8
+ const useFetchPluginStore = usePluginStore((s) => s.useFetchPluginStore);
9
+ useFetchPluginStore();
10
+
11
+ const checkPluginsIsInstalled = usePluginStore((s) => s.useCheckPluginsIsInstalled);
12
+
13
+ checkPluginsIsInstalled(plugins);
14
+ };
@@ -6,8 +6,8 @@ import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
+ import { chatService } from '@/services/chat';
9
10
  import { ChatMessageError } from '@/types/chatMessage';
10
- import { fetchPresetTaskResult } from '@/utils/fetch';
11
11
 
12
12
  import { getModelList } from './getModelList';
13
13
 
@@ -28,7 +28,7 @@ const Checker = memo<CheckerProps>(({ checkModel }) => {
28
28
  getModelList();
29
29
  }
30
30
 
31
- const data = await fetchPresetTaskResult({
31
+ const data = await chatService.fetchPresetTaskResult({
32
32
  onError: (_, rawError) => {
33
33
  setError(rawError);
34
34
  },
@@ -5,7 +5,8 @@ import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import DevModal from '@/features/PluginDevModal';
8
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
8
+ import { usePluginStore } from '@/store/plugin';
9
+ import { pluginSelectors } from '@/store/plugin/selectors';
9
10
 
10
11
  import { useStore } from '../store';
11
12
 
@@ -19,8 +20,8 @@ const MarketList = memo<{ id: string }>(({ id }) => {
19
20
 
20
21
  const [useFetchPluginList, fetchPluginManifest, deleteCustomPlugin, updateCustomPlugin] =
21
22
  usePluginStore((s) => [
22
- s.useFetchPluginList,
23
- s.fetchPluginManifest,
23
+ s.useFetchPluginStore,
24
+ s.installPlugin,
24
25
  s.deleteCustomPlugin,
25
26
  s.updateCustomPlugin,
26
27
  ]);
@@ -9,7 +9,8 @@ import { Flexbox } from 'react-layout-kit';
9
9
  import DevModal from 'src/features/PluginDevModal';
10
10
 
11
11
  import { FORM_STYLE } from '@/const/layoutTokens';
12
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
12
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
13
+ import { pluginSelectors } from '@/store/plugin/selectors';
13
14
 
14
15
  import { useStore } from '../store';
15
16
  import LocalPluginItem from './LocalPluginItem';
@@ -47,8 +48,8 @@ const MarketList = memo(() => {
47
48
 
48
49
  const [useFetchPluginList, fetchPluginManifest, saveToDevList, updateNewDevPlugin] =
49
50
  usePluginStore((s) => [
50
- s.useFetchPluginList,
51
- s.fetchPluginManifest,
51
+ s.useFetchPluginStore,
52
+ s.installPlugin,
52
53
  s.saveToCustomPluginList,
53
54
  s.updateNewCustomPlugin,
54
55
  ]);
@@ -8,7 +8,8 @@ import { useTranslation } from 'react-i18next';
8
8
  import { FORM_STYLE } from '@/const/layoutTokens';
9
9
  import { transformPluginSettings } from '@/features/PluginSettings';
10
10
  import PluginSettingRender from '@/features/PluginSettings/PluginSettingRender';
11
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
11
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
12
+ import { pluginSelectors } from '@/store/plugin/selectors';
12
13
 
13
14
  import { useStore } from '../store';
14
15
 
@@ -30,7 +31,7 @@ const PluginSettings = memo(() => {
30
31
  const [plugins] = useStore((s) => [s.config.plugins || [], !!s.config.plugins]);
31
32
 
32
33
  const [useFetchPluginList, updatePluginSettings] = usePluginStore((s) => [
33
- s.useFetchPluginList,
34
+ s.useFetchPluginStore,
34
35
  s.updatePluginSettings,
35
36
  ]);
36
37
  const pluginList = usePluginStore(pluginSelectors.pluginList);
@@ -4,9 +4,9 @@ import { chainPickEmoji } from '@/chains/pickEmoji';
4
4
  import { chainSummaryAgentName } from '@/chains/summaryAgentName';
5
5
  import { chainSummaryDescription } from '@/chains/summaryDescription';
6
6
  import { chainSummaryTags } from '@/chains/summaryTags';
7
+ import { chatService } from '@/services/chat';
7
8
  import { MetaData } from '@/types/meta';
8
9
  import { LobeAgentConfig } from '@/types/session';
9
- import { fetchPresetTaskResult } from '@/utils/fetch';
10
10
  import { setNamespace } from '@/utils/storeDebug';
11
11
 
12
12
  import { SessionLoadingState } from '../store/initialState';
@@ -74,7 +74,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
74
74
 
75
75
  const systemRole = config.systemRole;
76
76
 
77
- const emoji = await fetchPresetTaskResult({
77
+ const emoji = await chatService.fetchPresetTaskResult({
78
78
  onLoadingChange: (loading) => {
79
79
  get().updateLoadingState('avatar', loading);
80
80
  },
@@ -97,7 +97,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
97
97
  // 替换为 ...
98
98
  dispatchMeta({ type: 'update', value: { description: '...' } });
99
99
 
100
- fetchPresetTaskResult({
100
+ chatService.fetchPresetTaskResult({
101
101
  onError: () => {
102
102
  dispatchMeta({ type: 'update', value: { description: preValue } });
103
103
  },
@@ -120,7 +120,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
120
120
  // 替换为 ...
121
121
  dispatchMeta({ type: 'update', value: { tags: ['...'] } });
122
122
 
123
- fetchPresetTaskResult({
123
+ chatService.fetchPresetTaskResult({
124
124
  onError: () => {
125
125
  dispatchMeta({ type: 'update', value: { tags: preValue } });
126
126
  },
@@ -145,7 +145,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
145
145
  // 替换为 ...
146
146
  dispatchMeta({ type: 'update', value: { title: '...' } });
147
147
 
148
- fetchPresetTaskResult({
148
+ chatService.fetchPresetTaskResult({
149
149
  onError: () => {
150
150
  dispatchMeta({ type: 'update', value: { title: previousTitle } });
151
151
  },
@@ -5,7 +5,8 @@ import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { settingsSelectors, useGlobalStore } from '@/store/global';
8
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
8
+ import { usePluginStore } from '@/store/plugin';
9
+ import { pluginSelectors } from '@/store/plugin/selectors';
9
10
 
10
11
  const EmojiPicker = dynamic(() => import('@lobehub/ui/es/EmojiPicker'), { ssr: false });
11
12
 
@@ -7,7 +7,8 @@ import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import PluginSettingsConfig from '@/features/PluginSettings';
10
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
10
+ import { pluginHelpers, usePluginStore } from '@/store/plugin';
11
+ import { pluginSelectors } from '@/store/plugin/selectors';
11
12
 
12
13
  interface PluginSettingsModalProps {
13
14
  id: string;
@@ -0,0 +1,84 @@
1
+ import { PluginRequestPayload, createHeadersWithPluginSettings } from '@lobehub/chat-plugin-sdk';
2
+ import { merge } from 'lodash-es';
3
+
4
+ import { VISION_MODEL_WHITE_LIST } from '@/const/llm';
5
+ import { usePluginStore } from '@/store/plugin';
6
+ import { pluginSelectors } from '@/store/plugin/selectors';
7
+ import { initialLobeAgentConfig } from '@/store/session/initialState';
8
+ import type { OpenAIChatStreamPayload } from '@/types/openai/chat';
9
+ import { fetchAIFactory, getMessageError } from '@/utils/fetch';
10
+
11
+ import { createHeaderWithOpenAI } from './_header';
12
+ import { OPENAI_URLS, URLS } from './_url';
13
+
14
+ interface FetchOptions {
15
+ signal?: AbortSignal | undefined;
16
+ }
17
+
18
+ class ChatService {
19
+ getChatCompletion = (
20
+ { plugins: enabledPlugins, ...params }: Partial<OpenAIChatStreamPayload>,
21
+ options?: FetchOptions,
22
+ ) => {
23
+ const payload = merge(
24
+ {
25
+ model: initialLobeAgentConfig.model,
26
+ stream: true,
27
+ ...initialLobeAgentConfig.params,
28
+ },
29
+ params,
30
+ );
31
+ // ============ preprocess tools ============ //
32
+
33
+ const filterTools = pluginSelectors.enabledSchema(enabledPlugins)(usePluginStore.getState());
34
+
35
+ // the rule that model can use tools:
36
+ // 1. tools is not empty
37
+ // 2. model is not in vision white list, because vision model can't use tools
38
+ // TODO: we need to find some method to let vision model use tools
39
+ const shouldUseTools =
40
+ filterTools.length > 0 && !VISION_MODEL_WHITE_LIST.includes(payload.model);
41
+
42
+ const functions = shouldUseTools ? filterTools : undefined;
43
+
44
+ return fetch(OPENAI_URLS.chat, {
45
+ body: JSON.stringify({ ...payload, functions }),
46
+ headers: createHeaderWithOpenAI({ 'Content-Type': 'application/json' }),
47
+ method: 'POST',
48
+ signal: options?.signal,
49
+ });
50
+ };
51
+
52
+ /**
53
+ * run the plugin api to get result
54
+ * @param params
55
+ * @param options
56
+ */
57
+ runPluginApi = async (params: PluginRequestPayload, options?: FetchOptions) => {
58
+ const { usePluginStore } = await import('@/store/plugin');
59
+
60
+ const s = usePluginStore.getState();
61
+
62
+ const settings = pluginSelectors.getPluginSettingsById(params.identifier)(s);
63
+ const manifest = pluginSelectors.getPluginManifestById(params.identifier)(s);
64
+
65
+ const gatewayURL = manifest?.gateway;
66
+
67
+ const res = await fetch(gatewayURL ?? URLS.plugins, {
68
+ body: JSON.stringify({ ...params, manifest }),
69
+ headers: createHeadersWithPluginSettings(settings),
70
+ method: 'POST',
71
+ signal: options?.signal,
72
+ });
73
+
74
+ if (!res.ok) {
75
+ throw await getMessageError(res);
76
+ }
77
+
78
+ return await res.text();
79
+ };
80
+
81
+ fetchPresetTaskResult = fetchAIFactory(this.getChatCompletion);
82
+ }
83
+
84
+ export const chatService = new ChatService();
@@ -1,39 +1,21 @@
1
- import { PluginRequestPayload, createHeadersWithPluginSettings } from '@lobehub/chat-plugin-sdk';
1
+ import { LobeChatPluginsMarketIndex } from '@lobehub/chat-plugin-sdk';
2
2
 
3
- import { pluginSelectors, usePluginStore } from '@/store/plugin';
4
- import { getMessageError } from '@/utils/fetch';
3
+ import { getPluginIndexJSON } from '@/const/url';
4
+ import { getCurrentLanguage } from '@/store/global/helpers';
5
5
 
6
- import { URLS } from './_url';
6
+ class PluginService {
7
+ /**
8
+ * get plugin list from store
9
+ */
10
+ getPluginList = async () => {
11
+ const url = getPluginIndexJSON(getCurrentLanguage());
7
12
 
8
- interface FetchChatModelOptions {
9
- signal?: AbortSignal | undefined;
10
- withPlugin?: boolean;
11
- }
12
-
13
- /**
14
- * 请求插件结果´
15
- */
16
- export const fetchPlugin = async (
17
- params: PluginRequestPayload,
18
- options?: FetchChatModelOptions,
19
- ) => {
20
- const s = usePluginStore.getState();
21
-
22
- const settings = pluginSelectors.getPluginSettingsById(params.identifier)(s);
23
- const manifest = pluginSelectors.getPluginManifestById(params.identifier)(s);
13
+ const res = await fetch(url);
24
14
 
25
- const gatewayURL = manifest?.gateway;
15
+ const data: LobeChatPluginsMarketIndex = await res.json();
26
16
 
27
- const res = await fetch(gatewayURL ?? URLS.plugins, {
28
- body: JSON.stringify({ ...params, manifest }),
29
- headers: createHeadersWithPluginSettings(settings),
30
- method: 'POST',
31
- signal: options?.signal,
32
- });
33
-
34
- if (!res.ok) {
35
- throw await getMessageError(res);
36
- }
17
+ return data;
18
+ };
19
+ }
37
20
 
38
- return await res.text();
39
- };
21
+ export const pluginService = new PluginService();
@@ -1,3 +1,2 @@
1
1
  export * from './helpers';
2
- export * from './selectors';
3
2
  export { usePluginStore } from './store';