@lobehub/chat 0.55.1 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/package.json +3 -3
  3. package/src/const/fetch.ts +2 -0
  4. package/src/const/plugin.ts +1 -0
  5. package/src/features/AgentSetting/AgentPlugin/index.tsx +107 -60
  6. package/src/features/PluginSettings/PluginSettingRender.tsx +65 -0
  7. package/src/features/PluginSettings/index.tsx +65 -0
  8. package/src/hooks/useImportConfig.ts +7 -0
  9. package/src/locales/default/error.ts +30 -2
  10. package/src/locales/default/plugin.ts +1 -1
  11. package/src/locales/default/setting.ts +1 -0
  12. package/src/pages/api/auth.ts +2 -2
  13. package/src/pages/api/error.ts +3 -3
  14. package/src/pages/api/openai.ts +3 -3
  15. package/src/pages/chat/SessionList/List/Item/index.tsx +2 -2
  16. package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginError.tsx +28 -0
  17. package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +61 -0
  18. package/src/pages/chat/features/Conversation/ChatList/Error/index.tsx +42 -0
  19. package/src/pages/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +62 -58
  20. package/src/pages/chat/features/Conversation/ChatList/index.tsx +15 -25
  21. package/src/pages/chat/features/Header/PluginTag.tsx +1 -1
  22. package/src/services/plugin.ts +11 -14
  23. package/src/services/pluginMarket.ts +10 -0
  24. package/src/store/global/selectors.ts +2 -2
  25. package/src/store/global/store.ts +7 -0
  26. package/src/store/plugin/action.ts +18 -7
  27. package/src/store/plugin/initialState.ts +3 -0
  28. package/src/store/plugin/reducers/manifest.ts +2 -2
  29. package/src/store/plugin/selectors.ts +17 -5
  30. package/src/store/plugin/store.ts +3 -1
  31. package/src/store/session/slices/chat/actions/message.ts +30 -21
  32. package/src/store/session/slices/chat/selectors/utils.test.ts +4 -2
  33. package/src/store/session/slices/chat/selectors/utils.ts +2 -2
  34. package/src/types/chatMessage.ts +9 -0
  35. package/src/types/fetch.ts +18 -14
  36. package/src/types/plugin.ts +2 -2
  37. package/src/utils/fetch.ts +24 -18
@@ -0,0 +1,61 @@
1
+ import { Avatar } from '@lobehub/ui';
2
+ import { Button, Divider } from 'antd';
3
+ import { useTheme } from 'antd-style';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Center, Flexbox } from 'react-layout-kit';
7
+
8
+ import PluginSettingsConfig from '@/features/PluginSettings';
9
+ import { pluginSelectors, usePluginStore } from '@/store/plugin';
10
+ import { useSessionStore } from '@/store/session';
11
+
12
+ import { ErrorActionContainer, useStyles } from '../style';
13
+
14
+ interface PluginSettingsProps {
15
+ id: string;
16
+ pluginIdentifier: string;
17
+ }
18
+
19
+ const PluginSettings = memo<PluginSettingsProps>(({ id, pluginIdentifier }) => {
20
+ const { styles } = useStyles();
21
+
22
+ const { t } = useTranslation('error');
23
+ const theme = useTheme();
24
+ const [resend, deleteMessage] = useSessionStore((s) => [s.resendMessage, s.deleteMessage]);
25
+ const plugin = usePluginStore(pluginSelectors.getPluginMetaById(pluginIdentifier));
26
+ const manifest = usePluginStore(pluginSelectors.getPluginManifestSettingsById(pluginIdentifier));
27
+
28
+ return (
29
+ <ErrorActionContainer>
30
+ <Center gap={16} style={{ maxWidth: 400 }}>
31
+ <Avatar
32
+ avatar={plugin?.meta.avatar || '⚙️'}
33
+ background={theme.colorFillContent}
34
+ gap={12}
35
+ size={80}
36
+ />
37
+ <Flexbox style={{ fontSize: 20 }}>
38
+ {t('pluginSettings.title', { name: plugin?.meta.title })}
39
+ </Flexbox>
40
+ <Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
41
+ <Divider style={{ margin: '0 16px' }} />
42
+ {manifest.settings && (
43
+ <PluginSettingsConfig id={manifest.identifier} settings={manifest.settings} />
44
+ )}
45
+ <Button
46
+ block
47
+ onClick={() => {
48
+ resend(id);
49
+ deleteMessage(id);
50
+ }}
51
+ style={{ marginTop: 8 }}
52
+ type={'primary'}
53
+ >
54
+ {t('unlock.confirm')}
55
+ </Button>
56
+ </Center>
57
+ </ErrorActionContainer>
58
+ );
59
+ });
60
+
61
+ export default PluginSettings;
@@ -0,0 +1,42 @@
1
+ import { IPluginErrorType, PluginErrorType } from '@lobehub/chat-plugin-sdk';
2
+ import { RenderErrorMessage } from '@lobehub/ui';
3
+
4
+ import { ChatMessage } from '@/types/chatMessage';
5
+ import { ChatErrorType, ErrorType } from '@/types/fetch';
6
+
7
+ import InvalidAccess from './InvalidAccess';
8
+ import OpenAiBizError from './OpenAiBizError';
9
+ import PluginError from './Plugin/PluginError';
10
+ import PluginSettings from './Plugin/PluginSettings';
11
+
12
+ export const renderErrorMessage: RenderErrorMessage = (error, message: ChatMessage) => {
13
+ switch (error.type as IPluginErrorType) {
14
+ case PluginErrorType.PluginMarketIndexNotFound:
15
+ case PluginErrorType.PluginMarketIndexInvalid:
16
+ case PluginErrorType.PluginMetaInvalid:
17
+ case PluginErrorType.PluginMetaNotFound:
18
+ case PluginErrorType.PluginManifestInvalid:
19
+ case PluginErrorType.PluginManifestNotFound:
20
+ case PluginErrorType.PluginApiNotFound:
21
+ case PluginErrorType.PluginApiParamsError: {
22
+ return <PluginError content={(error as any).body} id={message.id} />;
23
+ }
24
+ case PluginErrorType.PluginSettingsInvalid: {
25
+ return (
26
+ message.plugin?.identifier && (
27
+ <PluginSettings id={message.id} pluginIdentifier={message.plugin?.identifier} />
28
+ )
29
+ );
30
+ }
31
+ }
32
+
33
+ switch (error.type as ErrorType) {
34
+ case ChatErrorType.InvalidAccessCode: {
35
+ return <InvalidAccess id={message.id} />;
36
+ }
37
+
38
+ case ChatErrorType.OpenAIBizError: {
39
+ return <OpenAiBizError content={(error as any).body} id={message.id} />;
40
+ }
41
+ }
42
+ };
@@ -7,77 +7,81 @@ import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { pluginSelectors, usePluginStore } from '@/store/plugin';
10
- import { OpenAIFunctionCall } from '@/types/chatMessage';
11
10
 
12
11
  import PluginResult from './PluginResultRender';
13
12
  import { useStyles } from './style';
14
13
 
15
14
  export interface FunctionCallProps {
15
+ arguments?: string;
16
+ command?: any;
16
17
  content: string;
17
- function_call?: OpenAIFunctionCall;
18
+ id?: string;
18
19
  loading?: boolean;
19
20
  }
20
21
 
21
- const FunctionCall = memo<FunctionCallProps>(({ function_call, loading, content }) => {
22
- const { t } = useTranslation('plugin');
23
- const { styles } = useStyles();
24
- const [open, setOpen] = useState(false);
25
- const item = usePluginStore(pluginSelectors.getPluginMetaByName(function_call?.name || ''));
22
+ const FunctionCall = memo<FunctionCallProps>(
23
+ ({ arguments: requestArgs = '{}', command, loading, content, id = 'unknown' }) => {
24
+ const { t } = useTranslation('plugin');
25
+ const { styles } = useStyles();
26
+ const [open, setOpen] = useState(false);
26
27
 
27
- const avatar = item?.meta.avatar ? (
28
- <Avatar avatar={item?.meta.avatar} size={32} />
29
- ) : (
30
- <Icon icon={LucideToyBrick} />
31
- );
28
+ const item = usePluginStore(pluginSelectors.getPluginMetaById(id));
32
29
 
33
- const args = JSON.stringify(function_call, null, 2);
34
- const params = JSON.stringify(JSON.parse(function_call?.arguments || '{}'), null, 2);
30
+ const avatar = item?.meta.avatar ? (
31
+ <Avatar avatar={item?.meta.avatar} size={32} />
32
+ ) : (
33
+ <Icon icon={LucideToyBrick} />
34
+ );
35
35
 
36
- return (
37
- <Flexbox gap={8}>
38
- <Flexbox
39
- align={'center'}
40
- className={styles.container}
41
- gap={8}
42
- horizontal
43
- onClick={() => {
44
- setOpen(!open);
45
- }}
46
- >
47
- {loading ? (
48
- <div>
49
- <LoadingOutlined />
50
- </div>
51
- ) : (
52
- avatar
36
+ const args = JSON.stringify(command, null, 2);
37
+ const params = JSON.stringify(JSON.parse(requestArgs), null, 2);
38
+
39
+ return (
40
+ <Flexbox gap={8}>
41
+ <Flexbox
42
+ align={'center'}
43
+ className={styles.container}
44
+ gap={8}
45
+ horizontal
46
+ onClick={() => {
47
+ setOpen(!open);
48
+ }}
49
+ >
50
+ {loading ? (
51
+ <div>
52
+ <LoadingOutlined />
53
+ </div>
54
+ ) : (
55
+ avatar
56
+ )}
57
+ {t(`plugins.${id}` as any, { ns: 'plugin' })}
58
+ <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
59
+ </Flexbox>
60
+ {open && (
61
+ <Tabs
62
+ items={[
63
+ {
64
+ children: <Highlighter language={'json'}>{args}</Highlighter>,
65
+ key: 'function_call',
66
+ label: t('debug.function_call'),
67
+ },
68
+ {
69
+ children: <Highlighter language={'json'}>{params}</Highlighter>,
70
+ key: 'arguments',
71
+ label: t('debug.arguments'),
72
+ },
73
+ {
74
+ children: <PluginResult content={content} />,
75
+ key: 'response',
76
+ label: t('debug.response'),
77
+ },
78
+ ]}
79
+ style={{ maxWidth: 800 }}
80
+ />
53
81
  )}
54
- {t(`plugins.${function_call?.name}` as any, { ns: 'plugin' })}
55
- <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
56
82
  </Flexbox>
57
- {open && (
58
- <Tabs
59
- items={[
60
- {
61
- children: <Highlighter language={'json'}>{args}</Highlighter>,
62
- key: 'function_call',
63
- label: t('debug.function_call'),
64
- },
65
- {
66
- children: <Highlighter language={'json'}>{params}</Highlighter>,
67
- key: 'arguments',
68
- label: t('debug.arguments'),
69
- },
70
- {
71
- children: <PluginResult content={content} />,
72
- key: 'response',
73
- label: t('debug.response'),
74
- },
75
- ]}
76
- style={{ maxWidth: 800 }}
77
- />
78
- )}
79
- </Flexbox>
80
- );
81
- });
83
+ );
84
+ },
85
+ );
82
86
 
83
87
  export default FunctionCall;
@@ -1,4 +1,4 @@
1
- import { ChatList, RenderErrorMessage, RenderMessage } from '@lobehub/ui';
1
+ import { ChatList, RenderMessage } from '@lobehub/ui';
2
2
  import isEqual from 'fast-deep-equal';
3
3
  import { memo, useCallback } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
@@ -11,27 +11,14 @@ import {
11
11
  useSessionStore,
12
12
  } from '@/store/session';
13
13
  import { ChatMessage } from '@/types/chatMessage';
14
- import { ErrorType } from '@/types/fetch';
15
14
  import { isFunctionMessage } from '@/utils/message';
16
15
 
17
- import InvalidAccess from './Error/InvalidAccess';
18
- import OpenAiBizError from './Error/OpenAiBizError';
16
+ import { renderErrorMessage } from './Error';
19
17
  import MessageExtra from './MessageExtra';
20
18
  import FunctionCall from './Plugins/FunctionCall';
21
19
  import PluginMessage from './Plugins/PluginMessage';
22
20
  import SkeletonList from './SkeletonList';
23
21
 
24
- const renderErrorMessage: RenderErrorMessage = (error, message) => {
25
- switch (error.type as ErrorType) {
26
- case 'InvalidAccessCode': {
27
- return <InvalidAccess id={message.id} />;
28
- }
29
- case 'OpenAIBizError': {
30
- return <OpenAiBizError content={(error as any).body} id={message.id} />;
31
- }
32
- }
33
- };
34
-
35
22
  const List = () => {
36
23
  const init = useSessionChatInit();
37
24
  const { t } = useTranslation('common');
@@ -49,25 +36,28 @@ const List = () => {
49
36
 
50
37
  const renderMessage: RenderMessage = useCallback(
51
38
  (content, message: ChatMessage) => {
39
+ const id = message.plugin?.identifier || message.function_call?.name;
40
+ const command = message.plugin ?? message.function_call;
41
+ const args = command?.arguments;
42
+ const fcProps = {
43
+ arguments: args,
44
+ command,
45
+ content: message.content,
46
+ id,
47
+ loading: message.id === chatLoadingId,
48
+ };
49
+
52
50
  if (message.role === 'function')
53
51
  return (
54
52
  <Flexbox gap={12}>
55
- <FunctionCall
56
- content={message.content}
57
- function_call={message.function_call}
58
- loading={message.id === chatLoadingId}
59
- />
53
+ <FunctionCall {...fcProps} />
60
54
  <PluginMessage loading={message.id === chatLoadingId} {...message} />
61
55
  </Flexbox>
62
56
  );
63
57
 
64
58
  if (message.role === 'assistant') {
65
59
  return isFunctionMessage(message.content) || !!message.function_call ? (
66
- <FunctionCall
67
- content={message.content}
68
- function_call={message.function_call}
69
- loading={message.id === chatLoadingId}
70
- />
60
+ <FunctionCall {...fcProps} />
71
61
  ) : (
72
62
  content
73
63
  );
@@ -19,7 +19,7 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
19
19
  if (plugins.length === 0) return null;
20
20
 
21
21
  const items: MenuProps['items'] = plugins.map((id) => {
22
- const item = list?.find((p) => p.name === id);
22
+ const item = list?.find((p) => p.identifier === id);
23
23
  return {
24
24
  icon: (
25
25
  <Avatar avatar={item?.meta.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
@@ -1,8 +1,8 @@
1
1
  import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
2
 
3
- import { LOBE_CHAT_ACCESS_CODE } from '@/const/fetch';
4
- import { PLUGINS_INDEX_URL } from '@/const/url';
5
- import { useGlobalStore } from '@/store/global';
3
+ import { LOBE_PLUGIN_SETTINGS } from '@/const/fetch';
4
+ import { usePluginStore } from '@/store/plugin';
5
+ import { getMessageError } from '@/utils/fetch';
6
6
 
7
7
  import { URLS } from './url';
8
8
 
@@ -12,30 +12,27 @@ interface FetchChatModelOptions {
12
12
  }
13
13
 
14
14
  /**
15
- * 请求插件结果
15
+ * 请求插件结果´
16
16
  */
17
17
  export const fetchPlugin = async (
18
18
  params: PluginRequestPayload,
19
19
  options?: FetchChatModelOptions,
20
20
  ) => {
21
+ const settings = usePluginStore.getState().pluginsSettings?.[params.identifier];
22
+
21
23
  const res = await fetch(URLS.plugins, {
22
24
  body: JSON.stringify(params),
23
25
  headers: {
24
26
  'Content-Type': 'application/json',
25
- [LOBE_CHAT_ACCESS_CODE]: useGlobalStore.getState().settings.password || '',
27
+ [LOBE_PLUGIN_SETTINGS]: JSON.stringify(settings) || '',
26
28
  },
27
29
  method: 'POST',
28
30
  signal: options?.signal,
29
31
  });
30
32
 
31
- return await res.text();
32
- };
33
+ if (!res.ok) {
34
+ throw await getMessageError(res);
35
+ }
33
36
 
34
- /**
35
- * 请求插件列表
36
- */
37
- export const getPluginList = async () => {
38
- const res = await fetch(PLUGINS_INDEX_URL);
39
-
40
- return res.json();
37
+ return await res.text();
41
38
  };
@@ -0,0 +1,10 @@
1
+ import { PLUGINS_INDEX_URL } from '@/const/url';
2
+
3
+ /**
4
+ * 请求插件列表
5
+ */
6
+ export const getPluginList = async () => {
7
+ const res = await fetch(PLUGINS_INDEX_URL);
8
+
9
+ return res.json();
10
+ };
@@ -11,10 +11,10 @@ const currentSettings = (s: GlobalStore) => merge({}, DEFAULT_SETTINGS, s.settin
11
11
  const currentDefaultAgent = (s: GlobalStore) => merge({}, DEFAULT_AGENT, s.settings.defaultAgent);
12
12
 
13
13
  const currentAgentConfig = (s: GlobalStore) =>
14
- merge({}, DEFAULT_AGENT_CONFIG, s.settings.defaultAgent.config);
14
+ merge({}, DEFAULT_AGENT_CONFIG, currentDefaultAgent(s).config);
15
15
 
16
16
  const currentAgentMeta = (s: GlobalStore) =>
17
- merge({}, DEFAULT_AGENT_META, s.settings.defaultAgent.meta);
17
+ merge({}, DEFAULT_AGENT_META, currentDefaultAgent(s).meta);
18
18
 
19
19
  export const exportSettings = (s: GlobalStore) => {
20
20
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -3,6 +3,7 @@ import { shallow } from 'zustand/shallow';
3
3
  import { createWithEqualityFn } from 'zustand/traditional';
4
4
  import { StateCreator } from 'zustand/vanilla';
5
5
 
6
+ import { DEFAULT_AGENT } from '@/const/settings';
6
7
  import { isDev } from '@/utils/env';
7
8
 
8
9
  import { type GlobalState, initialState } from './initialState';
@@ -25,6 +26,12 @@ const createStore: StateCreator<GlobalStore, [['zustand/devtools', never]]> = (.
25
26
  type GlobalPersist = Pick<GlobalStore, 'preference' | 'settings'>;
26
27
 
27
28
  const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
29
+ merge: (persistedState, currentState) => {
30
+ if (!(persistedState as GlobalPersist).settings.defaultAgent) {
31
+ currentState.settings.defaultAgent = DEFAULT_AGENT;
32
+ }
33
+ return currentState;
34
+ },
28
35
  name: 'LOBE_SETTINGS',
29
36
  partialize: (s) => ({
30
37
  preference: s.preference,
@@ -1,15 +1,16 @@
1
1
  import {
2
- LobeChatPlugin,
2
+ LobeChatPluginManifest,
3
3
  LobeChatPluginsMarketIndex,
4
4
  pluginManifestSchema,
5
5
  } from '@lobehub/chat-plugin-sdk';
6
6
  import { message } from 'antd';
7
7
  import { produce } from 'immer';
8
- import { uniq } from 'lodash-es';
8
+ import { merge, uniq } from 'lodash-es';
9
9
  import useSWR, { SWRResponse } from 'swr';
10
10
  import { StateCreator } from 'zustand/vanilla';
11
11
 
12
- import { getPluginList } from '@/services/plugin';
12
+ import { getPluginList } from '@/services/pluginMarket';
13
+ import { pluginSelectors } from '@/store/plugin/selectors';
13
14
  import { LobeSessions } from '@/types/session';
14
15
  import { setNamespace } from '@/utils/storeDebug';
15
16
 
@@ -26,6 +27,7 @@ export interface PluginAction {
26
27
  dispatchPluginManifest: (payload: PluginDispatch) => void;
27
28
  fetchPluginManifest: (name: string) => Promise<void>;
28
29
  updateManifestLoadingState: (key: string, value: boolean | undefined) => void;
30
+ updatePluginSettings: <T>(id: string, settings: Partial<T>) => void;
29
31
  useFetchPluginList: () => SWRResponse<LobeChatPluginsMarketIndex>;
30
32
  }
31
33
 
@@ -51,7 +53,6 @@ export const createPluginSlice: StateCreator<
51
53
 
52
54
  await Promise.all(plugins.map((name) => fetchPluginManifest(name)));
53
55
 
54
- console.log('fetched');
55
56
  set({ manifestPrepared: true }, false, t('checkLocalEnabledPlugins'));
56
57
  },
57
58
  dispatchPluginManifest: (payload) => {
@@ -62,7 +63,7 @@ export const createPluginSlice: StateCreator<
62
63
  },
63
64
 
64
65
  fetchPluginManifest: async (name) => {
65
- const plugin = get().pluginList.find((plugin) => plugin.name === name);
66
+ const plugin = pluginSelectors.getPluginMetaById(name)(get());
66
67
  // 1. 校验文件
67
68
 
68
69
  if (!plugin) return;
@@ -74,7 +75,7 @@ export const createPluginSlice: StateCreator<
74
75
 
75
76
  // 2. 发送请求
76
77
  get().updateManifestLoadingState(name, true);
77
- let data: LobeChatPlugin | null;
78
+ let data: LobeChatPluginManifest | null;
78
79
 
79
80
  try {
80
81
  const res = await fetch(plugin.manifest);
@@ -100,7 +101,7 @@ export const createPluginSlice: StateCreator<
100
101
  }
101
102
 
102
103
  // 4. 存储 manifest 信息
103
- get().dispatchPluginManifest({ id: plugin.name, plugin: data, type: 'addManifest' });
104
+ get().dispatchPluginManifest({ id: plugin.identifier, plugin: data, type: 'addManifest' });
104
105
  },
105
106
 
106
107
  updateManifestLoadingState: (key, value) => {
@@ -112,6 +113,16 @@ export const createPluginSlice: StateCreator<
112
113
  t('updateManifestLoadingState'),
113
114
  );
114
115
  },
116
+
117
+ updatePluginSettings: (id, settings) => {
118
+ set(
119
+ produce((draft) => {
120
+ draft.pluginsSettings[id] = merge({}, draft.pluginsSettings[id], settings);
121
+ }),
122
+ false,
123
+ t('updatePluginSettings'),
124
+ );
125
+ },
115
126
  useFetchPluginList: () =>
116
127
  useSWR<LobeChatPluginsMarketIndex>('fetchPluginList', getPluginList, {
117
128
  onSuccess: (pluginMarketIndex) => {
@@ -3,12 +3,14 @@ import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
3
3
  import { PluginManifestMap } from '@/types/plugin';
4
4
 
5
5
  export type PluginManifestLoadingState = Record<string, boolean>;
6
+ export type PluginsSettings = Record<string, any>;
6
7
 
7
8
  export interface PluginStoreState {
8
9
  manifestPrepared: boolean;
9
10
  pluginList: LobeChatPluginMeta[];
10
11
  pluginManifestLoading: PluginManifestLoadingState;
11
12
  pluginManifestMap: PluginManifestMap;
13
+ pluginsSettings: PluginsSettings;
12
14
  }
13
15
 
14
16
  export const initialState: PluginStoreState = {
@@ -16,4 +18,5 @@ export const initialState: PluginStoreState = {
16
18
  pluginList: [],
17
19
  pluginManifestLoading: {},
18
20
  pluginManifestMap: {},
21
+ pluginsSettings: {},
19
22
  };
@@ -1,9 +1,9 @@
1
- import { LobeChatPlugin } from '@lobehub/chat-plugin-sdk';
1
+ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
2
  import { produce } from 'immer';
3
3
 
4
4
  import { PluginManifestMap } from '@/types/plugin';
5
5
 
6
- type AddManifestDispatch = { id: string; plugin: LobeChatPlugin; type: 'addManifest' };
6
+ type AddManifestDispatch = { id: string; plugin: LobeChatPluginManifest; type: 'addManifest' };
7
7
  type DeleteManifestDispatch = { id: string; type: 'deleteManifest' };
8
8
  // type UpdateManifestDispatch = {
9
9
  // id: string;
@@ -1,3 +1,5 @@
1
+ import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
2
+
1
3
  import { PluginStoreState } from './initialState';
2
4
 
3
5
  const enabledSchema =
@@ -9,15 +11,25 @@ const enabledSchema =
9
11
  if (!enabledPlugins) return false;
10
12
 
11
13
  // 如果存在 enabledPlugins,那么只启用 enabledPlugins 中的插件
12
- return enabledPlugins.includes(p.name);
14
+ return enabledPlugins.includes(p.identifier);
13
15
  })
14
- .map((i) => i.schema);
16
+ .flatMap((manifest) =>
17
+ manifest.api.map((m) => ({
18
+ ...m,
19
+ // 将插件的 identifier 作为前缀,避免重复
20
+ name: manifest.identifier + PLUGIN_SCHEMA_SEPARATOR + m.name,
21
+ })),
22
+ );
15
23
  };
16
24
 
17
- const getPluginMetaByName = (name: string) => (s: PluginStoreState) =>
18
- s.pluginList?.find((p) => p.name === name);
25
+ const getPluginMetaById = (id: string) => (s: PluginStoreState) =>
26
+ s.pluginList?.find((p) => p.identifier === id);
27
+
28
+ const getPluginManifestSettingsById = (id: string) => (s: PluginStoreState) =>
29
+ s.pluginManifestMap[id];
19
30
 
20
31
  export const pluginSelectors = {
21
32
  enabledSchema,
22
- getPluginMetaByName,
33
+ getPluginManifestSettingsById,
34
+ getPluginMetaById,
23
35
  };
@@ -19,7 +19,7 @@ const createStore: StateCreator<PluginStore, [['zustand/devtools', never]]> = (.
19
19
 
20
20
  // =============== persist 本地缓存中间件配置 ============ //
21
21
 
22
- type SessionPersist = Pick<PluginStore, 'pluginList'>;
22
+ type SessionPersist = Pick<PluginStore, 'pluginList' | 'pluginManifestMap' | 'pluginsSettings'>;
23
23
 
24
24
  const storeName = 'LOBE_PLUGIN';
25
25
 
@@ -28,6 +28,8 @@ const persistOptions: PersistOptions<PluginStore, SessionPersist> = {
28
28
 
29
29
  partialize: (s) => ({
30
30
  pluginList: s.pluginList,
31
+ pluginManifestMap: s.pluginManifestMap,
32
+ pluginsSettings: s.pluginsSettings,
31
33
  }),
32
34
 
33
35
  // 手动控制 Hydration ,避免 ssr 报错
@@ -1,7 +1,9 @@
1
+ import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
1
2
  import { template } from 'lodash-es';
2
3
  import { StateCreator } from 'zustand/vanilla';
3
4
 
4
5
  import { LOADING_FLAT } from '@/const/message';
6
+ import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
5
7
  import { fetchChatModel } from '@/services/chatModel';
6
8
  import { fetchPlugin } from '@/services/plugin';
7
9
  import { SessionStore } from '@/store/session';
@@ -319,36 +321,43 @@ export const chatMessage: StateCreator<
319
321
  const message = session.chats[id];
320
322
  if (!message) return;
321
323
 
322
- let payload: OpenAIFunctionCall = { name: '' };
324
+ let payload: PluginRequestPayload = { apiName: '', identifier: '' };
325
+ // 识别到内容是 function_call 的情况下
326
+ // 将 function_call 转换为 plugin request payload
323
327
  if (message.content) {
324
- const { function_call } = JSON.parse(message.content);
325
- dispatchMessage({ id, key: 'function_call', type: 'updateMessage', value: function_call });
328
+ const { function_call } = JSON.parse(message.content) as {
329
+ function_call: OpenAIFunctionCall;
330
+ };
331
+
332
+ const [identifier, apiName] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
333
+ payload = { apiName, arguments: function_call.arguments, identifier };
334
+
335
+ dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
326
336
  dispatchMessage({ id, key: 'content', type: 'updateMessage', value: '' });
327
- payload = function_call;
328
337
  } else {
329
- if (message.function_call) {
330
- payload = message.function_call;
338
+ if (message.plugin) {
339
+ payload = message.plugin;
331
340
  }
332
341
  }
333
342
 
334
- if (!payload.name) return;
343
+ if (!payload.apiName) return;
335
344
 
336
- // const fid = nanoid();
337
345
  dispatchMessage({ id, key: 'role', type: 'updateMessage', value: 'function' });
338
- dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.name });
339
- dispatchMessage({ id, key: 'function_call', type: 'updateMessage', value: payload });
340
-
341
- // dispatchMessage({
342
- // id: id,
343
- // message: FUNCTION_LOADING,
344
- // parentId: message.,
345
- // role: 'function',
346
- // type: 'addMessage',
347
- // });
348
-
349
- const abortController = toggleChatLoading(true, id);
350
- const data = await fetchPlugin(payload, { signal: abortController?.signal });
346
+ dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.identifier });
347
+ dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
348
+
349
+ let data: string;
350
+ try {
351
+ const abortController = toggleChatLoading(true, id);
352
+ data = await fetchPlugin(payload, { signal: abortController?.signal });
353
+ } catch (error) {
354
+ dispatchMessage({ id, key: 'error', type: 'updateMessage', value: error });
355
+
356
+ data = '';
357
+ }
351
358
  toggleChatLoading(false);
359
+ // 如果报错则结束了
360
+ if (!data) return;
352
361
 
353
362
  dispatchMessage({ id, key: 'content', type: 'updateMessage', value: data });
354
363