@lobehub/chat 0.54.3 → 0.55.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 (45) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +81 -0
  3. package/package.json +6 -3
  4. package/src/components/SelectWithImg/styles.ts +8 -7
  5. package/src/const/url.ts +4 -0
  6. package/src/features/AgentSetting/AgentPlugin/index.tsx +83 -20
  7. package/src/layout/index.tsx +13 -8
  8. package/src/locales/default/plugin.ts +0 -16
  9. package/src/pages/api/openai.ts +4 -20
  10. package/src/pages/api/plugins.api.ts +161 -8
  11. package/src/pages/chat/[id]/layout.tsx +4 -0
  12. package/src/pages/chat/features/Conversation/ChatList/Plugins/CustomRender.tsx +32 -0
  13. package/src/pages/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +4 -5
  14. package/src/pages/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +36 -27
  15. package/src/pages/chat/features/Conversation/ChatList/Plugins/dynamticLoader.ts +26 -0
  16. package/src/pages/chat/features/Header/PluginTag.tsx +13 -8
  17. package/src/services/chatModel.ts +11 -2
  18. package/src/services/plugin.ts +11 -1
  19. package/src/store/global/slices/agent.ts +2 -2
  20. package/src/store/global/slices/settings.ts +1 -1
  21. package/src/store/plugin/action.ts +121 -0
  22. package/src/store/plugin/hooks/useSessionHydrated.ts +21 -0
  23. package/src/store/plugin/index.ts +2 -0
  24. package/src/store/plugin/initialState.ts +19 -0
  25. package/src/store/plugin/reducers/manifest.ts +36 -0
  26. package/src/store/plugin/selectors.ts +23 -0
  27. package/src/store/plugin/store.ts +48 -0
  28. package/src/store/session/hooks/useOnFinishHydrationSession.ts +6 -3
  29. package/src/store/session/hooks/useSessionHydrated.ts +0 -1
  30. package/src/store/session/slices/agentConfig/action.ts +2 -6
  31. package/src/store/session/slices/chat/selectors/utils.test.ts +15 -1
  32. package/src/store/session/slices/chat/selectors/utils.ts +5 -3
  33. package/src/store/session/store.ts +0 -1
  34. package/src/types/plugin.ts +3 -4
  35. package/src/plugins/Render.ts +0 -11
  36. package/src/plugins/index.ts +0 -11
  37. package/src/plugins/searchEngine/Render.tsx +0 -92
  38. package/src/plugins/searchEngine/index.ts +0 -26
  39. package/src/plugins/searchEngine/type.ts +0 -82
  40. package/src/plugins/type.ts +0 -45
  41. package/src/plugins/weather/Render.tsx +0 -53
  42. package/src/plugins/weather/index.ts +0 -24
  43. package/src/plugins/weather/type.ts +0 -36
  44. package/src/plugins/webCrawler/index.ts +0 -20
  45. package/src/plugins/webCrawler/type.ts +0 -35
@@ -3,41 +3,50 @@ import { memo } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
- import { PluginsRender } from '@/plugins/Render';
6
+ import { usePluginStore } from '@/store/plugin';
7
7
  import { ChatMessage } from '@/types/chatMessage';
8
8
 
9
+ import CustomRender from './CustomRender';
10
+
9
11
  export interface FunctionMessageProps extends ChatMessage {
10
12
  loading?: boolean;
11
13
  }
12
14
 
13
- const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
14
- const { t } = useTranslation('plugin');
15
-
16
- const Render = PluginsRender[name || ''];
17
-
18
- let isJSON = true;
19
- try {
20
- JSON.parse(content);
21
- } catch {
22
- isJSON = false;
23
- }
15
+ const PluginMessage = memo<FunctionMessageProps>(
16
+ ({
17
+ content,
18
+ // loading,
19
+ name,
20
+ }) => {
21
+ const { t } = useTranslation('plugin');
22
+
23
+ const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
24
+ let isJSON = true;
25
+ try {
26
+ JSON.parse(content);
27
+ } catch {
28
+ isJSON = false;
29
+ }
30
+
31
+ // if (!loading)
32
+
33
+ if (!isJSON) {
34
+ return (
35
+ <Flexbox gap={8} horizontal>
36
+ <div>
37
+ <Loading3QuartersOutlined spin />
38
+ </div>
39
+ {t('loading.content')}
40
+ </Flexbox>
41
+ );
42
+ }
43
+
44
+ if (!manifest?.ui?.url) return;
24
45
 
25
- if (!isJSON) {
26
46
  return (
27
- <Flexbox gap={8} horizontal>
28
- <div>
29
- <Loading3QuartersOutlined spin />
30
- </div>
31
- {t('loading.content')}
32
- </Flexbox>
47
+ <CustomRender content={JSON.parse(content)} name={name || 'unknown'} url={manifest.ui?.url} />
33
48
  );
34
- }
35
-
36
- if (Render) {
37
- return <Render content={JSON.parse(content)} name={name || 'unknown'} />;
38
- }
39
-
40
- return null;
41
- });
49
+ },
50
+ );
42
51
 
43
52
  export default PluginMessage;
@@ -0,0 +1,26 @@
1
+ /* eslint-disable no-undef */
2
+
3
+ /**
4
+ * 本动态加载模块使用 SystemJS 实现,在 Lobe Chat 中缓存了 React、ReactDOM、antd、antd-style 四个模块。
5
+ */
6
+ import * as antd from 'antd';
7
+ import * as AntdStyle from 'antd-style';
8
+ import * as React from 'react';
9
+ import * as ReactDOM from 'react-dom';
10
+ import 'systemjs';
11
+
12
+ System.addImportMap({
13
+ imports: {
14
+ 'React': 'app:React',
15
+ 'ReactDOM': 'app:ReactDOM',
16
+ 'antd': 'app:antd',
17
+ 'antd-style': 'app:antd-style',
18
+ },
19
+ });
20
+
21
+ System.set('app:React', { default: React, ...React });
22
+ System.set('app:ReactDOM', { __useDefault: true, ...ReactDOM });
23
+ System.set('app:antd', antd);
24
+ System.set('app:antd-style', AntdStyle);
25
+
26
+ export const system = System;
@@ -5,7 +5,7 @@ import { LucideToyBrick } from 'lucide-react';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
- import { PluginsMap } from '@/plugins';
8
+ import { usePluginStore } from '@/store/plugin';
9
9
 
10
10
  export interface PluginTagProps {
11
11
  plugins: string[];
@@ -14,15 +14,20 @@ export interface PluginTagProps {
14
14
  const PluginTag = memo<PluginTagProps>(({ plugins }) => {
15
15
  const { t } = useTranslation('plugin');
16
16
 
17
+ const list = usePluginStore((s) => s.pluginList);
18
+
17
19
  if (plugins.length === 0) return null;
18
20
 
19
- const items: MenuProps['items'] = plugins.map((id) => ({
20
- icon: (
21
- <Avatar avatar={PluginsMap[id].avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
22
- ),
23
- key: id,
24
- label: t(`plugins.${id}` as any),
25
- }));
21
+ const items: MenuProps['items'] = plugins.map((id) => {
22
+ const item = list?.find((p) => p.name === id);
23
+ return {
24
+ icon: (
25
+ <Avatar avatar={item?.meta.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
26
+ ),
27
+ key: id,
28
+ label: t(`plugins.${id}` as any),
29
+ };
30
+ });
26
31
  const count = plugins.length;
27
32
 
28
33
  return (
@@ -1,7 +1,9 @@
1
1
  import { merge } from 'lodash-es';
2
+ import { ChatCompletionFunctions } from 'openai-edge/types/api';
2
3
 
3
4
  import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch';
4
5
  import { useGlobalStore } from '@/store/global';
6
+ import { pluginSelectors, usePluginStore } from '@/store/plugin';
5
7
  import { initialLobeAgentConfig } from '@/store/session/initialState';
6
8
  import type { OpenAIStreamPayload } from '@/types/openai';
7
9
 
@@ -15,7 +17,7 @@ interface FetchChatModelOptions {
15
17
  * 专门用于对话的 fetch
16
18
  */
17
19
  export const fetchChatModel = (
18
- params: Partial<OpenAIStreamPayload>,
20
+ { plugins: enabledPlugins, ...params }: Partial<OpenAIStreamPayload>,
19
21
  options?: FetchChatModelOptions,
20
22
  ) => {
21
23
  const payload = merge(
@@ -26,9 +28,16 @@ export const fetchChatModel = (
26
28
  },
27
29
  params,
28
30
  );
31
+ // ============ 1. 前置处理 functions ============ //
32
+
33
+ const filterFunctions: ChatCompletionFunctions[] = pluginSelectors.enabledSchema(enabledPlugins)(
34
+ usePluginStore.getState(),
35
+ );
36
+
37
+ const functions = filterFunctions.length === 0 ? undefined : filterFunctions;
29
38
 
30
39
  return fetch(URLS.openai, {
31
- body: JSON.stringify(payload),
40
+ body: JSON.stringify({ ...payload, functions }),
32
41
  headers: {
33
42
  'Content-Type': 'application/json',
34
43
  [LOBE_CHAT_ACCESS_CODE]: useGlobalStore.getState().settings.password || '',
@@ -1,6 +1,7 @@
1
1
  import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
2
 
3
3
  import { LOBE_CHAT_ACCESS_CODE } from '@/const/fetch';
4
+ import { PLUGINS_INDEX_URL } from '@/const/url';
4
5
  import { useGlobalStore } from '@/store/global';
5
6
 
6
7
  import { URLS } from './url';
@@ -11,7 +12,7 @@ interface FetchChatModelOptions {
11
12
  }
12
13
 
13
14
  /**
14
- * 专门用于对话的 fetch
15
+ * 请求插件结果
15
16
  */
16
17
  export const fetchPlugin = async (
17
18
  params: PluginRequestPayload,
@@ -29,3 +30,12 @@ export const fetchPlugin = async (
29
30
 
30
31
  return await res.text();
31
32
  };
33
+
34
+ /**
35
+ * 请求插件列表
36
+ */
37
+ export const getPluginList = async () => {
38
+ const res = await fetch(PLUGINS_INDEX_URL);
39
+
40
+ return res.json();
41
+ };
@@ -52,7 +52,7 @@ export const createAgentSlice: StateCreator<
52
52
  setAgentConfig: (config) => {
53
53
  const settings = produce(get().settings, (draft: GlobalSettings) => {
54
54
  const oldConfig = draft.defaultAgent.config as LobeAgentConfig;
55
- draft.defaultAgent.config = merge(oldConfig, config);
55
+ draft.defaultAgent.config = merge({}, oldConfig, config);
56
56
  });
57
57
 
58
58
  set({ settings }, false, t('setAgentConfig', config));
@@ -60,7 +60,7 @@ export const createAgentSlice: StateCreator<
60
60
  setAgentMeta: (meta) => {
61
61
  const settings = produce(get().settings, (draft) => {
62
62
  const oldMeta = draft.defaultAgent.meta as MetaData;
63
- draft.defaultAgent.meta = merge(oldMeta, meta);
63
+ draft.defaultAgent.meta = merge({}, oldMeta, meta);
64
64
  });
65
65
 
66
66
  set({ settings }, false, t('setAgentMeta', meta));
@@ -61,7 +61,7 @@ export const createSettingsSlice: StateCreator<
61
61
 
62
62
  setSettings: (settings) => {
63
63
  const oldSetting = get().settings;
64
- set({ settings: merge(oldSetting, settings) }, false, t('setSettings', settings));
64
+ set({ settings: merge({}, oldSetting, settings) }, false, t('setSettings', settings));
65
65
  },
66
66
  switchThemeMode: (themeMode) => {
67
67
  get().setSettings({ themeMode });
@@ -0,0 +1,121 @@
1
+ import {
2
+ LobeChatPlugin,
3
+ LobeChatPluginsMarketIndex,
4
+ pluginManifestSchema,
5
+ } from '@lobehub/chat-plugin-sdk';
6
+ import { message } from 'antd';
7
+ import { produce } from 'immer';
8
+ import { uniq } from 'lodash-es';
9
+ import useSWR, { SWRResponse } from 'swr';
10
+ import { StateCreator } from 'zustand/vanilla';
11
+
12
+ import { getPluginList } from '@/services/plugin';
13
+ import { LobeSessions } from '@/types/session';
14
+ import { setNamespace } from '@/utils/storeDebug';
15
+
16
+ import { PluginDispatch, pluginManifestReducer } from './reducers/manifest';
17
+ import { PluginStore } from './store';
18
+
19
+ const t = setNamespace('plugin');
20
+
21
+ /**
22
+ * 代理行为接口
23
+ */
24
+ export interface PluginAction {
25
+ checkLocalEnabledPlugins: (sessions: LobeSessions) => void;
26
+ dispatchPluginManifest: (payload: PluginDispatch) => void;
27
+ fetchPluginManifest: (name: string) => Promise<void>;
28
+ updateManifestLoadingState: (key: string, value: boolean | undefined) => void;
29
+ useFetchPluginList: () => SWRResponse<LobeChatPluginsMarketIndex>;
30
+ }
31
+
32
+ export const createPluginSlice: StateCreator<
33
+ PluginStore,
34
+ [['zustand/devtools', never]],
35
+ [],
36
+ PluginAction
37
+ > = (set, get) => ({
38
+ checkLocalEnabledPlugins: async (sessions) => {
39
+ const { fetchPluginManifest } = get();
40
+
41
+ let enabledPlugins: string[] = [];
42
+
43
+ for (const session of Object.values(sessions)) {
44
+ const plugins = session.config.plugins;
45
+ if (!plugins || plugins.length === 0) continue;
46
+
47
+ enabledPlugins = [...enabledPlugins, ...plugins];
48
+ }
49
+
50
+ const plugins = uniq(enabledPlugins);
51
+
52
+ await Promise.all(plugins.map((name) => fetchPluginManifest(name)));
53
+
54
+ console.log('fetched');
55
+ set({ manifestPrepared: true }, false, t('checkLocalEnabledPlugins'));
56
+ },
57
+ dispatchPluginManifest: (payload) => {
58
+ const { pluginManifestMap } = get();
59
+ const nextManifest = pluginManifestReducer(pluginManifestMap, payload);
60
+
61
+ set({ pluginManifestMap: nextManifest }, false, t('dispatchPluginManifest', payload));
62
+ },
63
+
64
+ fetchPluginManifest: async (name) => {
65
+ const plugin = get().pluginList.find((plugin) => plugin.name === name);
66
+ // 1. 校验文件
67
+
68
+ if (!plugin) return;
69
+
70
+ if (!plugin.manifest) {
71
+ message.error('插件未配置 描述文件');
72
+ return;
73
+ }
74
+
75
+ // 2. 发送请求
76
+ get().updateManifestLoadingState(name, true);
77
+ let data: LobeChatPlugin | null;
78
+
79
+ try {
80
+ const res = await fetch(plugin.manifest);
81
+
82
+ data = await res.json();
83
+ } catch (error) {
84
+ data = null;
85
+ console.error(error);
86
+ }
87
+
88
+ get().updateManifestLoadingState(name, undefined);
89
+ if (!data) {
90
+ message.error('插件描述文件请求失败');
91
+ return;
92
+ }
93
+
94
+ // 3. 校验插件文件格式规范
95
+ const { success } = pluginManifestSchema.safeParse(data);
96
+
97
+ if (!success) {
98
+ message.error('插件描述文件格式错误');
99
+ return;
100
+ }
101
+
102
+ // 4. 存储 manifest 信息
103
+ get().dispatchPluginManifest({ id: plugin.name, plugin: data, type: 'addManifest' });
104
+ },
105
+
106
+ updateManifestLoadingState: (key, value) => {
107
+ set(
108
+ produce((draft) => {
109
+ draft.pluginManifestLoading[key] = value;
110
+ }),
111
+ false,
112
+ t('updateManifestLoadingState'),
113
+ );
114
+ },
115
+ useFetchPluginList: () =>
116
+ useSWR<LobeChatPluginsMarketIndex>('fetchPluginList', getPluginList, {
117
+ onSuccess: (pluginMarketIndex) => {
118
+ set({ pluginList: pluginMarketIndex.plugins }, false, t('useFetchPluginList'));
119
+ },
120
+ }),
121
+ });
@@ -0,0 +1,21 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { usePluginStore } from '../store';
4
+
5
+ export const usePluginHydrated = () => {
6
+ // 根据 sessions 是否有值来判断是否已经初始化
7
+
8
+ // 并且插件的 manifest 也准备完毕
9
+ const manifestPrepared = usePluginStore((s) => s.manifestPrepared);
10
+ const [isInit, setInit] = useState(false);
11
+
12
+ useEffect(() => {
13
+ const hasRehydrated = usePluginStore.persist.hasHydrated();
14
+
15
+ if (hasRehydrated && !isInit) {
16
+ setInit(true);
17
+ }
18
+ }, []);
19
+
20
+ return isInit && manifestPrepared;
21
+ };
@@ -0,0 +1,2 @@
1
+ export * from './selectors';
2
+ export { usePluginStore } from './store';
@@ -0,0 +1,19 @@
1
+ import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
2
+
3
+ import { PluginManifestMap } from '@/types/plugin';
4
+
5
+ export type PluginManifestLoadingState = Record<string, boolean>;
6
+
7
+ export interface PluginStoreState {
8
+ manifestPrepared: boolean;
9
+ pluginList: LobeChatPluginMeta[];
10
+ pluginManifestLoading: PluginManifestLoadingState;
11
+ pluginManifestMap: PluginManifestMap;
12
+ }
13
+
14
+ export const initialState: PluginStoreState = {
15
+ manifestPrepared: false,
16
+ pluginList: [],
17
+ pluginManifestLoading: {},
18
+ pluginManifestMap: {},
19
+ };
@@ -0,0 +1,36 @@
1
+ import { LobeChatPlugin } from '@lobehub/chat-plugin-sdk';
2
+ import { produce } from 'immer';
3
+
4
+ import { PluginManifestMap } from '@/types/plugin';
5
+
6
+ type AddManifestDispatch = { id: string; plugin: LobeChatPlugin; type: 'addManifest' };
7
+ type DeleteManifestDispatch = { id: string; type: 'deleteManifest' };
8
+ // type UpdateManifestDispatch = {
9
+ // id: string;
10
+ // plugin: LobeChatPlugin;
11
+ // type: 'updateManifest';
12
+ // version: string;
13
+ // };
14
+
15
+ export type PluginDispatch = AddManifestDispatch | DeleteManifestDispatch;
16
+ // | UpdateManifestDispatch;
17
+
18
+ export const pluginManifestReducer = (
19
+ state: PluginManifestMap,
20
+ payload: PluginDispatch,
21
+ ): PluginManifestMap => {
22
+ switch (payload.type) {
23
+ case 'addManifest': {
24
+ return produce(state, (draftState) => {
25
+ draftState[payload.id] = payload.plugin;
26
+ });
27
+ }
28
+
29
+ case 'deleteManifest': {
30
+ return produce(state, (draftState) => {
31
+ delete draftState[payload.id];
32
+ });
33
+ }
34
+ // case 'updateManifest'
35
+ }
36
+ };
@@ -0,0 +1,23 @@
1
+ import { PluginStoreState } from './initialState';
2
+
3
+ const enabledSchema =
4
+ (enabledPlugins: string[] = []) =>
5
+ (s: PluginStoreState) => {
6
+ return Object.values(s.pluginManifestMap)
7
+ .filter((p) => {
8
+ // 如果不存在 enabledPlugins,那么全部不启用
9
+ if (!enabledPlugins) return false;
10
+
11
+ // 如果存在 enabledPlugins,那么只启用 enabledPlugins 中的插件
12
+ return enabledPlugins.includes(p.name);
13
+ })
14
+ .map((i) => i.schema);
15
+ };
16
+
17
+ const getPluginMetaByName = (name: string) => (s: PluginStoreState) =>
18
+ s.pluginList?.find((p) => p.name === name);
19
+
20
+ export const pluginSelectors = {
21
+ enabledSchema,
22
+ getPluginMetaByName,
23
+ };
@@ -0,0 +1,48 @@
1
+ import { PersistOptions, devtools, persist } from 'zustand/middleware';
2
+ import { shallow } from 'zustand/shallow';
3
+ import { createWithEqualityFn } from 'zustand/traditional';
4
+ import { StateCreator } from 'zustand/vanilla';
5
+
6
+ import { isDev } from '@/utils/env';
7
+
8
+ import { PluginAction, createPluginSlice } from './action';
9
+ import { PluginStoreState, initialState } from './initialState';
10
+
11
+ // =============== 聚合 createStoreFn ============ //
12
+
13
+ export type PluginStore = PluginStoreState & PluginAction;
14
+
15
+ const createStore: StateCreator<PluginStore, [['zustand/devtools', never]]> = (...parameters) => ({
16
+ ...initialState,
17
+ ...createPluginSlice(...parameters),
18
+ });
19
+
20
+ // =============== persist 本地缓存中间件配置 ============ //
21
+
22
+ type SessionPersist = Pick<PluginStore, 'pluginList'>;
23
+
24
+ const storeName = 'LOBE_PLUGIN';
25
+
26
+ const persistOptions: PersistOptions<PluginStore, SessionPersist> = {
27
+ name: storeName,
28
+
29
+ partialize: (s) => ({
30
+ pluginList: s.pluginList,
31
+ }),
32
+
33
+ // 手动控制 Hydration ,避免 ssr 报错
34
+ skipHydration: true,
35
+ version: 0,
36
+ };
37
+
38
+ // =============== 实装 useStore ============ //
39
+
40
+ export const usePluginStore = createWithEqualityFn<PluginStore>()(
41
+ persist(
42
+ devtools(createStore, {
43
+ name: storeName + (isDev ? '_DEV' : ''),
44
+ }),
45
+ persistOptions,
46
+ ),
47
+ shallow,
48
+ );
@@ -1,18 +1,21 @@
1
1
  import { useEffect } from 'react';
2
2
 
3
- import { useSessionStore } from '../store';
3
+ import { SessionStore, useSessionStore } from '../store';
4
4
 
5
5
  /**
6
6
  * 当 Session 水合完毕后才会执行的 useEffect
7
7
  * @param fn
8
8
  * @param deps
9
9
  */
10
- export const useOnFinishHydrationSession = (fn: () => void, deps: any[] = []) => {
10
+ export const useOnFinishHydrationSession = (
11
+ fn: (state: SessionStore) => void,
12
+ deps: any[] = [],
13
+ ) => {
11
14
  useEffect(() => {
12
15
  const hasRehydrated = useSessionStore.persist.hasHydrated();
13
16
  // 只有当水合完毕后再开始做操作
14
17
  if (!hasRehydrated) return;
15
18
 
16
- fn();
19
+ fn(useSessionStore.getState());
17
20
  }, deps);
18
21
  };
@@ -5,7 +5,6 @@ import { useSessionStore } from '../store';
5
5
  export const useSessionHydrated = () => {
6
6
  // 根据 sessions 是否有值来判断是否已经初始化
7
7
  const hasInited = !!Object.values(useSessionStore.getState().sessions).length;
8
-
9
8
  const [isInit, setInit] = useState(hasInited);
10
9
 
11
10
  useEffect(() => {
@@ -34,22 +34,21 @@ export interface AgentAction {
34
34
  * @returns 一个 Promise,用于异步操作完成后的处理
35
35
  */
36
36
  autocompleteAgentTitle: (id: string) => Promise<void>;
37
-
38
37
  autocompleteMeta: (key: keyof MetaData) => void;
38
+
39
39
  /**
40
40
  * 自动完成会话代理元数据
41
41
  * @param id - 代理的 ID
42
42
  */
43
43
  autocompleteSessionAgentMeta: (id: string, replace?: boolean) => void;
44
-
45
44
  /**
46
45
  * 内部更新代理元数据
47
46
  * @param id - 代理的 ID
48
47
  * @returns 任意类型的返回值
49
48
  */
50
49
  internalUpdateAgentMeta: (id: string) => any;
51
- toggleAgentPlugin: (pluginId: string) => void;
52
50
 
51
+ toggleAgentPlugin: (pluginId: string, checked: boolean) => void;
53
52
  /**
54
53
  * 更新代理配置
55
54
  * @param config - 部分 LobeAgentConfig 的配置
@@ -191,7 +190,6 @@ export const createAgentSlice: StateCreator<
191
190
  get().dispatchSession({ id, key, type: 'updateSessionMeta', value });
192
191
  };
193
192
  },
194
-
195
193
  toggleAgentPlugin: (id: string) => {
196
194
  const { activeId } = get();
197
195
  const session = sessionSelectors.currentSession(get());
@@ -212,7 +210,6 @@ export const createAgentSlice: StateCreator<
212
210
 
213
211
  get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
214
212
  },
215
-
216
213
  updateAgentConfig: (config) => {
217
214
  const { activeId } = get();
218
215
  const session = sessionSelectors.currentSession(get());
@@ -220,7 +217,6 @@ export const createAgentSlice: StateCreator<
220
217
 
221
218
  get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
222
219
  },
223
-
224
220
  updateAgentMeta: (meta) => {
225
221
  const { activeId } = get();
226
222
  const session = sessionSelectors.currentSession(get());
@@ -338,7 +338,21 @@ describe('organizeChats', () => {
338
338
 
339
339
  session.chats[message.id] = message;
340
340
 
341
- const result = organizeChats(session, params);
341
+ const result = organizeChats(session, {
342
+ ...params,
343
+ pluginList: [
344
+ {
345
+ name: 'realtimeWeather',
346
+ meta: {
347
+ avatar: '🧩',
348
+ },
349
+ createAt: 'abc',
350
+ manifest: '',
351
+ homepage: '',
352
+ schemaVersion: 'v1',
353
+ },
354
+ ],
355
+ });
342
356
  const meta = result[3].meta;
343
357
 
344
358
  expect(meta.avatar).toBe('🧩');
@@ -1,4 +1,5 @@
1
- import { PluginsMap } from '@/plugins';
1
+ import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
2
+
2
3
  import { ChatMessage } from '@/types/chatMessage';
3
4
  import { MetaData } from '@/types/meta';
4
5
  import { LobeAgentSession } from '@/types/session';
@@ -8,12 +9,13 @@ interface OrganizeParams {
8
9
  assistant?: MetaData;
9
10
  user?: MetaData;
10
11
  };
12
+ pluginList?: LobeChatPluginMeta[];
11
13
  topicId?: string;
12
14
  }
13
15
 
14
16
  export const organizeChats = (
15
17
  session: LobeAgentSession,
16
- { topicId, meta }: OrganizeParams = {},
18
+ { topicId, meta, pluginList }: OrganizeParams = {},
17
19
  ) => {
18
20
  const getMeta = (message: ChatMessage) => {
19
21
  switch (message.role) {
@@ -36,7 +38,7 @@ export const organizeChats = (
36
38
  }
37
39
 
38
40
  case 'function': {
39
- const plugin = PluginsMap[message.name || ''];
41
+ const plugin = (pluginList || []).find((m) => m.name === message.name);
40
42
 
41
43
  return {
42
44
  avatar: '🧩',
@@ -13,7 +13,6 @@ import { SessionAction, createSessionSlice } from './slices/session/action';
13
13
  // =============== 聚合 createStoreFn ============ //
14
14
 
15
15
  export type SessionStore = SessionAction & AgentAction & ChatAction & SessionStoreState;
16
-
17
16
  const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (...parameters) => ({
18
17
  ...initialState,
19
18
  ...createAgentSlice(...parameters),
@@ -1,4 +1,3 @@
1
- export interface OpenAIPluginPayload {
2
- arguments: string;
3
- name: string;
4
- }
1
+ import { LobeChatPlugin } from '@lobehub/chat-plugin-sdk';
2
+
3
+ export type PluginManifestMap = Record<string, LobeChatPlugin>;