@lobehub/chat 0.54.4 → 0.55.1
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.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +81 -0
- package/package.json +7 -3
- package/src/components/SelectWithImg/styles.ts +8 -7
- package/src/const/url.ts +4 -0
- package/src/features/AgentSetting/AgentPlugin/index.tsx +83 -20
- package/src/layout/index.tsx +13 -8
- package/src/locales/default/plugin.ts +0 -16
- package/src/pages/api/openai.ts +4 -20
- package/src/pages/api/plugins.api.ts +7 -8
- package/src/pages/chat/[id]/layout.tsx +4 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/CustomRender.tsx +32 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +4 -5
- package/src/pages/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +36 -27
- package/src/pages/chat/features/Conversation/ChatList/Plugins/dynamticLoader.ts +26 -0
- package/src/pages/chat/features/Header/PluginTag.tsx +13 -8
- package/src/services/chatModel.ts +11 -2
- package/src/services/plugin.ts +11 -1
- package/src/store/plugin/action.ts +121 -0
- package/src/store/plugin/hooks/useSessionHydrated.ts +21 -0
- package/src/store/plugin/index.ts +2 -0
- package/src/store/plugin/initialState.ts +19 -0
- package/src/store/plugin/reducers/manifest.ts +36 -0
- package/src/store/plugin/selectors.ts +23 -0
- package/src/store/plugin/store.ts +48 -0
- package/src/store/session/hooks/useOnFinishHydrationSession.ts +6 -3
- package/src/store/session/hooks/useSessionHydrated.ts +0 -1
- package/src/store/session/slices/agentConfig/action.ts +2 -6
- package/src/store/session/slices/chat/selectors/utils.test.ts +15 -1
- package/src/store/session/slices/chat/selectors/utils.ts +5 -3
- package/src/store/session/store.ts +0 -1
- package/src/types/plugin.ts +3 -4
- package/src/plugins/Render.ts +0 -11
- package/src/plugins/index.ts +0 -11
- package/src/plugins/searchEngine/Render.tsx +0 -92
- package/src/plugins/searchEngine/index.ts +0 -26
- package/src/plugins/searchEngine/type.ts +0 -82
- package/src/plugins/type.ts +0 -45
- package/src/plugins/weather/Render.tsx +0 -53
- package/src/plugins/weather/index.ts +0 -24
- package/src/plugins/weather/type.ts +0 -36
- package/src/plugins/webCrawler/index.ts +0 -20
- package/src/plugins/webCrawler/type.ts +0 -35
package/.eslintrc.js
CHANGED
|
@@ -10,5 +10,6 @@ config.rules['unicorn/no-typeof-undefined'] = 0;
|
|
|
10
10
|
config.rules['unicorn/explicit-length-check'] = 0;
|
|
11
11
|
config.rules['unicorn/prefer-code-point'] = 0;
|
|
12
12
|
config.rules['no-extra-boolean-cast'] = 0;
|
|
13
|
+
config.rules['unicorn/no-useless-undefined'] = 0;
|
|
13
14
|
|
|
14
15
|
module.exports = config;
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,87 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.55.1](https://github.com/lobehub/lobe-chat/compare/v0.55.0...v0.55.1)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-22**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor plugin api with @lobehub/chat-plugins-gateway.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor plugin api with @lobehub/chat-plugins-gateway, closes [#100](https://github.com/lobehub/lobe-chat/issues/100) ([b88d0db](https://github.com/lobehub/lobe-chat/commit/b88d0db))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 0.55.0](https://github.com/lobehub/lobe-chat/compare/v0.54.4...v0.55.0)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-08-22**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: 将网关实现代码集成进 Chat 本体, 抽取插件为独立 store, 重构 openai 接口调用逻辑,将插件 schema 开启关闭逻辑与接口解耦, 重构插件列表获取逻辑,进而完全移除 plugins 目录.
|
|
37
|
+
|
|
38
|
+
#### ✨ Features
|
|
39
|
+
|
|
40
|
+
- **misc**: 初步完成插件市场动态加载全链路, 实现插件组件的动态加载.
|
|
41
|
+
|
|
42
|
+
#### 🐛 Bug Fixes
|
|
43
|
+
|
|
44
|
+
- **misc**: Fix error, 修正无法正常开启插件的问题, 修正测试, 补充插件 store 的水合逻辑.
|
|
45
|
+
|
|
46
|
+
#### 💄 Styles
|
|
47
|
+
|
|
48
|
+
- **misc**: 完成插件市场 loading 态样式.
|
|
49
|
+
|
|
50
|
+
<br/>
|
|
51
|
+
|
|
52
|
+
<details>
|
|
53
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
54
|
+
|
|
55
|
+
#### Code refactoring
|
|
56
|
+
|
|
57
|
+
- **misc**: 将网关实现代码集成进 Chat 本体 ([17e8161](https://github.com/lobehub/lobe-chat/commit/17e8161))
|
|
58
|
+
- **misc**: 抽取插件为独立 store ([12b7e7d](https://github.com/lobehub/lobe-chat/commit/12b7e7d))
|
|
59
|
+
- **misc**: 重构 openai 接口调用逻辑,将插件 schema 开启关闭逻辑与接口解耦 ([5aa886e](https://github.com/lobehub/lobe-chat/commit/5aa886e))
|
|
60
|
+
- **misc**: 重构插件列表获取逻辑,进而完全移除 plugins 目录 ([10055e1](https://github.com/lobehub/lobe-chat/commit/10055e1))
|
|
61
|
+
|
|
62
|
+
#### What's improved
|
|
63
|
+
|
|
64
|
+
- **misc**: 初步完成插件市场动态加载全链路 ([bc5e40f](https://github.com/lobehub/lobe-chat/commit/bc5e40f))
|
|
65
|
+
- **misc**: 实现插件组件的动态加载 ([04dbab2](https://github.com/lobehub/lobe-chat/commit/04dbab2))
|
|
66
|
+
|
|
67
|
+
#### What's fixed
|
|
68
|
+
|
|
69
|
+
- **misc**: Fix error ([fbeec75](https://github.com/lobehub/lobe-chat/commit/fbeec75))
|
|
70
|
+
- **misc**: 修正无法正常开启插件的问题 ([b3e9090](https://github.com/lobehub/lobe-chat/commit/b3e9090))
|
|
71
|
+
- **misc**: 修正测试 ([001de5b](https://github.com/lobehub/lobe-chat/commit/001de5b))
|
|
72
|
+
- **misc**: 补充插件 store 的水合逻辑 ([bfb649b](https://github.com/lobehub/lobe-chat/commit/bfb649b))
|
|
73
|
+
|
|
74
|
+
#### Styles
|
|
75
|
+
|
|
76
|
+
- **misc**: 完成插件市场 loading 态样式 ([8009691](https://github.com/lobehub/lobe-chat/commit/8009691))
|
|
77
|
+
|
|
78
|
+
</details>
|
|
79
|
+
|
|
80
|
+
<div align="right">
|
|
81
|
+
|
|
82
|
+
[](#readme-top)
|
|
83
|
+
|
|
84
|
+
</div>
|
|
85
|
+
|
|
5
86
|
### [Version 0.54.4](https://github.com/lobehub/lobe-chat/compare/v0.54.3...v0.54.4)
|
|
6
87
|
|
|
7
88
|
<sup>Released on **2023-08-21**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.1",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -63,10 +63,12 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@ant-design/colors": "^7",
|
|
65
65
|
"@ant-design/icons": "^5",
|
|
66
|
+
"@cfworker/json-schema": "^1.12.7",
|
|
66
67
|
"@emoji-mart/data": "^1",
|
|
67
68
|
"@emoji-mart/react": "^1",
|
|
68
69
|
"@icons-pack/react-simple-icons": "^8",
|
|
69
70
|
"@lobehub/chat-plugin-sdk": "latest",
|
|
71
|
+
"@lobehub/chat-plugins-gateway": "^1.2.2",
|
|
70
72
|
"@lobehub/ui": "latest",
|
|
71
73
|
"@vercel/analytics": "^1",
|
|
72
74
|
"ahooks": "^3",
|
|
@@ -93,6 +95,7 @@
|
|
|
93
95
|
"react-layout-kit": "^1.7.1",
|
|
94
96
|
"serpapi": "^2",
|
|
95
97
|
"swr": "^2",
|
|
98
|
+
"systemjs": "^6.14.2",
|
|
96
99
|
"ts-md5": "^1",
|
|
97
100
|
"use-merge-value": "^1",
|
|
98
101
|
"uuid": "^9",
|
|
@@ -111,7 +114,8 @@
|
|
|
111
114
|
"@types/node": "^20",
|
|
112
115
|
"@types/react": "^18",
|
|
113
116
|
"@types/react-dom": "^18",
|
|
114
|
-
"@types/
|
|
117
|
+
"@types/systemjs": "^6.13.1",
|
|
118
|
+
"@types/testing-library__jest-dom": "^6",
|
|
115
119
|
"@types/uuid": "^9",
|
|
116
120
|
"@umijs/lint": "^4",
|
|
117
121
|
"@vitest/coverage-v8": "latest",
|
|
@@ -123,7 +127,7 @@
|
|
|
123
127
|
"i18next-browser-languagedetector": "^7",
|
|
124
128
|
"i18next-resources-for-ts": "^1",
|
|
125
129
|
"jsdom": "^22",
|
|
126
|
-
"lint-staged": "^
|
|
130
|
+
"lint-staged": "^14",
|
|
127
131
|
"next-pwa": "^5",
|
|
128
132
|
"node-fetch": "^3",
|
|
129
133
|
"prettier": "^2",
|
|
@@ -8,13 +8,14 @@ export const useStyles = createStyles(({ css, token, cx }) => {
|
|
|
8
8
|
}
|
|
9
9
|
`;
|
|
10
10
|
|
|
11
|
-
const img = cx(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
const img = cx(
|
|
12
|
+
css`
|
|
13
|
+
border-radius: ${token.borderRadius}px;
|
|
14
|
+
box-shadow: inset 0 0 0 2px ${token.colorSplit};
|
|
15
|
+
transition: all 100ms ${token.motionEaseOut};
|
|
16
|
+
`,
|
|
17
|
+
hover,
|
|
18
|
+
);
|
|
18
19
|
|
|
19
20
|
return {
|
|
20
21
|
active: css`
|
package/src/const/url.ts
CHANGED
|
@@ -5,3 +5,7 @@ export const CHANGELOG = `${pkg.homepage}/blob/master/CHANGELOG.md`;
|
|
|
5
5
|
export const ABOUT = pkg.homepage;
|
|
6
6
|
export const FEEDBACK = pkg.bugs.url;
|
|
7
7
|
export const DISCORD = 'https://discord.gg/AYFPHvv2jT';
|
|
8
|
+
|
|
9
|
+
export const PLUGINS_INDEX_URL =
|
|
10
|
+
process.env.PLUGINS_INDEX_URL ??
|
|
11
|
+
'https://registry.npmmirror.com/@lobehub/lobe-chat-plugins/latest/files';
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
import { Avatar, Form, ItemGroup } from '@lobehub/ui';
|
|
2
|
-
import { Switch } from 'antd';
|
|
2
|
+
import { Skeleton, Switch } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import isEqual from 'fast-deep-equal';
|
|
3
5
|
import { ToyBrick } from 'lucide-react';
|
|
4
6
|
import { memo, useMemo } from 'react';
|
|
5
7
|
import { useTranslation } from 'react-i18next';
|
|
6
8
|
|
|
7
9
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
8
|
-
import {
|
|
10
|
+
import { usePluginStore } from '@/store/plugin';
|
|
9
11
|
import { AgentAction } from '@/store/session/slices/agentConfig';
|
|
10
12
|
import { LobeAgentConfig } from '@/types/session';
|
|
11
13
|
|
|
14
|
+
const useStyles = createStyles(({ css }) => ({
|
|
15
|
+
avatar: css`
|
|
16
|
+
.ant-skeleton-header {
|
|
17
|
+
padding-right: 0;
|
|
18
|
+
}
|
|
19
|
+
`,
|
|
20
|
+
label: css`
|
|
21
|
+
li {
|
|
22
|
+
height: 14px !important;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
li + li {
|
|
26
|
+
margin-block-start: 12px !important;
|
|
27
|
+
}
|
|
28
|
+
`,
|
|
29
|
+
}));
|
|
30
|
+
|
|
12
31
|
export interface AgentPluginProps {
|
|
13
32
|
config: LobeAgentConfig;
|
|
14
33
|
updateConfig: AgentAction['toggleAgentPlugin'];
|
|
@@ -16,27 +35,71 @@ export interface AgentPluginProps {
|
|
|
16
35
|
|
|
17
36
|
const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
|
|
18
37
|
const { t } = useTranslation('setting');
|
|
38
|
+
const { styles } = useStyles();
|
|
39
|
+
const [useFetchPluginList, fetchPluginManifest] = usePluginStore((s) => [
|
|
40
|
+
s.useFetchPluginList,
|
|
41
|
+
s.fetchPluginManifest,
|
|
42
|
+
]);
|
|
43
|
+
const pluginManifestLoading = usePluginStore((s) => s.pluginManifestLoading, isEqual);
|
|
44
|
+
const { data } = useFetchPluginList();
|
|
45
|
+
|
|
46
|
+
const plugin: ItemGroup = useMemo(() => {
|
|
47
|
+
let children;
|
|
48
|
+
|
|
49
|
+
const loadingItem = {
|
|
50
|
+
avatar: (
|
|
51
|
+
<Skeleton
|
|
52
|
+
active
|
|
53
|
+
avatar={{ size: 40 }}
|
|
54
|
+
className={styles.avatar}
|
|
55
|
+
paragraph={false}
|
|
56
|
+
title={false}
|
|
57
|
+
/>
|
|
58
|
+
),
|
|
59
|
+
label: (
|
|
60
|
+
<Skeleton
|
|
61
|
+
active
|
|
62
|
+
avatar={false}
|
|
63
|
+
paragraph={{ className: styles.label, style: { marginBottom: 0 } }}
|
|
64
|
+
style={{ width: 300 }}
|
|
65
|
+
title={false}
|
|
66
|
+
/>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
children = !data
|
|
71
|
+
? [loadingItem, loadingItem, loadingItem]
|
|
72
|
+
: data.plugins.map((item) => ({
|
|
73
|
+
avatar: <Avatar avatar={item.meta.avatar} />,
|
|
74
|
+
children: (
|
|
75
|
+
<Switch
|
|
76
|
+
checked={
|
|
77
|
+
// 如果在加载中,说明激活了
|
|
78
|
+
pluginManifestLoading[item.name] || !config.plugins
|
|
79
|
+
? false
|
|
80
|
+
: config.plugins.includes(item.name)
|
|
81
|
+
}
|
|
82
|
+
loading={pluginManifestLoading[item.name]}
|
|
83
|
+
onChange={(checked) => {
|
|
84
|
+
updateConfig(item.name, checked);
|
|
85
|
+
if (checked) {
|
|
86
|
+
fetchPluginManifest(item.name);
|
|
87
|
+
}
|
|
88
|
+
}}
|
|
89
|
+
/>
|
|
90
|
+
),
|
|
91
|
+
desc: item.meta?.description,
|
|
92
|
+
label: t(`plugins.${item.name}` as any, { ns: 'plugin' }),
|
|
93
|
+
minWidth: undefined,
|
|
94
|
+
tag: item.name,
|
|
95
|
+
}));
|
|
19
96
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
children: Object.values(PluginsMap).map((item) => ({
|
|
23
|
-
avatar: <Avatar avatar={item.avatar} />,
|
|
24
|
-
children: (
|
|
25
|
-
<Switch
|
|
26
|
-
checked={!config.plugins ? false : config.plugins.includes(item.name)}
|
|
27
|
-
onChange={() => updateConfig(item.name)}
|
|
28
|
-
/>
|
|
29
|
-
),
|
|
30
|
-
desc: item.schema.description,
|
|
31
|
-
label: t(`plugins.${item.name}` as any, { ns: 'plugin' }),
|
|
32
|
-
minWidth: undefined,
|
|
33
|
-
tag: item.name,
|
|
34
|
-
})),
|
|
97
|
+
return {
|
|
98
|
+
children,
|
|
35
99
|
icon: ToyBrick,
|
|
36
100
|
title: t('settingPlugin.title'),
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
);
|
|
101
|
+
};
|
|
102
|
+
}, [config, data?.plugins, pluginManifestLoading]);
|
|
40
103
|
|
|
41
104
|
return <Form items={[plugin]} {...FORM_STYLE} />;
|
|
42
105
|
});
|
package/src/layout/index.tsx
CHANGED
|
@@ -8,7 +8,8 @@ import { PropsWithChildren, memo, useCallback, useEffect } from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { createI18nNext } from '@/locales/create';
|
|
10
10
|
import { useGlobalStore, useOnFinishHydrationGlobal } from '@/store/global';
|
|
11
|
-
import {
|
|
11
|
+
import { usePluginStore } from '@/store/plugin';
|
|
12
|
+
import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
|
|
12
13
|
import { GlobalStyle } from '@/styles';
|
|
13
14
|
|
|
14
15
|
import { useStyles } from './style';
|
|
@@ -24,6 +25,10 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
|
24
25
|
});
|
|
25
26
|
});
|
|
26
27
|
|
|
28
|
+
useOnFinishHydrationSession((s) => {
|
|
29
|
+
usePluginStore.getState().checkLocalEnabledPlugins(s.sessions);
|
|
30
|
+
});
|
|
31
|
+
|
|
27
32
|
return (
|
|
28
33
|
<ConfigProvider locale={Zh_CN}>
|
|
29
34
|
<App className={styles.bg}>{children}</App>
|
|
@@ -32,21 +37,21 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
|
32
37
|
});
|
|
33
38
|
|
|
34
39
|
export default memo(({ children }: PropsWithChildren) => {
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
// refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
|
|
42
|
+
useSessionStore.persist.rehydrate();
|
|
43
|
+
useGlobalStore.persist.rehydrate();
|
|
44
|
+
usePluginStore.persist.rehydrate();
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
35
47
|
const themeMode = useGlobalStore((s) => s.settings.themeMode);
|
|
36
48
|
const [primaryColor, neutralColor] = useGlobalStore((s) => [
|
|
37
49
|
s.settings.primaryColor,
|
|
38
50
|
s.settings.neutralColor,
|
|
39
51
|
]);
|
|
40
|
-
|
|
41
52
|
const { browserPrefers } = useThemeMode();
|
|
42
53
|
const isDarkMode = themeMode === 'auto' ? browserPrefers === 'dark' : themeMode === 'dark';
|
|
43
54
|
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
// refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
|
|
46
|
-
useSessionStore.persist.rehydrate();
|
|
47
|
-
useGlobalStore.persist.rehydrate();
|
|
48
|
-
}, []);
|
|
49
|
-
|
|
50
55
|
const genCustomToken: any = useCallback(
|
|
51
56
|
() => lobeCustomTheme({ isDarkMode, neutralColor, primaryColor }),
|
|
52
57
|
[primaryColor, neutralColor, isDarkMode],
|
|
@@ -13,22 +13,6 @@ export default {
|
|
|
13
13
|
realtimeWeather: '实时天气预报',
|
|
14
14
|
searchEngine: '搜索引擎',
|
|
15
15
|
undefined: '插件检测中...',
|
|
16
|
-
|
|
17
16
|
websiteCrawler: '网页内容提取',
|
|
18
17
|
},
|
|
19
|
-
realtimeWeather: {
|
|
20
|
-
data: {
|
|
21
|
-
date: '日期',
|
|
22
|
-
daytemp_float: '日间温度',
|
|
23
|
-
dayweather: '日间天气',
|
|
24
|
-
daywind: '日间风速',
|
|
25
|
-
nighttemp_float: '夜间温度',
|
|
26
|
-
nightweather: '夜间天气',
|
|
27
|
-
nightwind: '夜间风速',
|
|
28
|
-
week: '星期',
|
|
29
|
-
},
|
|
30
|
-
title: '近7日天气数据({{city}})',
|
|
31
|
-
updateAt: '更新时间',
|
|
32
|
-
},
|
|
33
|
-
responseData: '返回数据',
|
|
34
18
|
};
|
package/src/pages/api/openai.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { OpenAIStream, OpenAIStreamCallbacks, StreamingTextResponse } from 'ai';
|
|
2
2
|
import { Configuration, OpenAIApi } from 'openai-edge';
|
|
3
|
-
import { ChatCompletionFunctions } from 'openai-edge/types/api';
|
|
4
3
|
|
|
5
4
|
import { getServerConfig } from '@/config/server';
|
|
6
5
|
import { createErrorResponse } from '@/pages/api/error';
|
|
7
|
-
import { PluginsMap } from '@/plugins';
|
|
8
6
|
import { ErrorType } from '@/types/fetch';
|
|
9
7
|
import { OpenAIStreamPayload } from '@/types/openai';
|
|
10
8
|
|
|
@@ -38,32 +36,18 @@ export const createChatCompletion = async ({
|
|
|
38
36
|
|
|
39
37
|
const openai = createOpenAI(OPENAI_API_KEY, endpoint);
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
// ============ 1. 前置处理 messages ============ //
|
|
40
|
+
const { messages, ...params } = payload;
|
|
42
41
|
|
|
43
|
-
// ============ 1. 前置处理 functions ============ //
|
|
44
|
-
|
|
45
|
-
const filterFunctions: ChatCompletionFunctions[] = Object.values(PluginsMap)
|
|
46
|
-
.filter((p) => {
|
|
47
|
-
// 如果不存在 enabledPlugins,那么全部不启用
|
|
48
|
-
if (!enabledPlugins) return false;
|
|
49
|
-
|
|
50
|
-
// 如果存在 enabledPlugins,那么只启用 enabledPlugins 中的插件
|
|
51
|
-
return enabledPlugins.includes(p.name);
|
|
52
|
-
})
|
|
53
|
-
.map((f) => f.schema);
|
|
54
|
-
|
|
55
|
-
const functions = filterFunctions.length === 0 ? undefined : filterFunctions;
|
|
56
|
-
|
|
57
|
-
// ============ 2. 前置处理 messages ============ //
|
|
58
42
|
const formatMessages = messages.map((m) => ({
|
|
59
43
|
content: m.content,
|
|
60
44
|
name: m.name,
|
|
61
45
|
role: m.role,
|
|
62
46
|
}));
|
|
63
47
|
|
|
64
|
-
// ============
|
|
48
|
+
// ============ 2. 发送请求 ============ //
|
|
65
49
|
|
|
66
|
-
const requestParams = {
|
|
50
|
+
const requestParams = { messages: formatMessages, stream: true, ...params };
|
|
67
51
|
|
|
68
52
|
let response: Response;
|
|
69
53
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { createLobeChatPluginGateway } from '@lobehub/chat-plugins-gateway';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
// refs to: https://github.com/lobehub/chat-plugins-gateway
|
|
5
|
-
const PLUGIN_GATEWAY_URL = process.env.PLUGIN_GATEWAY_URL || 'https://chat-plugins.lobehub.com';
|
|
3
|
+
import { PLUGINS_INDEX_URL } from '@/const/url';
|
|
6
4
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
export const config = {
|
|
6
|
+
runtime: 'edge',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default createLobeChatPluginGateway(PLUGINS_INDEX_URL);
|
|
@@ -2,11 +2,13 @@ import { useRouter } from 'next/router';
|
|
|
2
2
|
import { PropsWithChildren, memo } from 'react';
|
|
3
3
|
import { shallow } from 'zustand/shallow';
|
|
4
4
|
|
|
5
|
+
import { usePluginStore } from '@/store/plugin';
|
|
5
6
|
import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
|
|
6
7
|
|
|
7
8
|
import ChatLayout from '../layout';
|
|
8
9
|
|
|
9
10
|
const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
11
|
+
const useFetchPluginList = usePluginStore((s) => s.useFetchPluginList);
|
|
10
12
|
const [activeSession, toggleTopic] = useSessionStore((s) => {
|
|
11
13
|
return [s.activeSession, s.toggleTopic];
|
|
12
14
|
}, shallow);
|
|
@@ -24,6 +26,8 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
|
24
26
|
toggleTopic();
|
|
25
27
|
}, [id]);
|
|
26
28
|
|
|
29
|
+
useFetchPluginList();
|
|
30
|
+
|
|
27
31
|
return <ChatLayout>{children}</ChatLayout>;
|
|
28
32
|
});
|
|
29
33
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PluginRender, PluginRenderProps } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { Skeleton } from 'antd';
|
|
3
|
+
import { memo, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { system } from './dynamticLoader';
|
|
6
|
+
|
|
7
|
+
interface CustomRenderProps extends PluginRenderProps {
|
|
8
|
+
url: string;
|
|
9
|
+
}
|
|
10
|
+
const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
|
|
11
|
+
const [component, setComp] = useState<PluginRender | null>(null);
|
|
12
|
+
|
|
13
|
+
if (!component) {
|
|
14
|
+
system
|
|
15
|
+
.import(url)
|
|
16
|
+
.then((module1) => {
|
|
17
|
+
setComp(module1.default);
|
|
18
|
+
// 使用module1模块
|
|
19
|
+
})
|
|
20
|
+
.catch((error) => {
|
|
21
|
+
setComp(null);
|
|
22
|
+
console.error(error);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return <Skeleton active style={{ width: 300 }} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const Comp = component;
|
|
29
|
+
|
|
30
|
+
return <Comp {...props} />;
|
|
31
|
+
});
|
|
32
|
+
export default CustomRender;
|
|
@@ -6,7 +6,7 @@ import { memo, useState } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
10
10
|
import { OpenAIFunctionCall } from '@/types/chatMessage';
|
|
11
11
|
|
|
12
12
|
import PluginResult from './PluginResultRender';
|
|
@@ -22,11 +22,10 @@ const FunctionCall = memo<FunctionCallProps>(({ function_call, loading, content
|
|
|
22
22
|
const { t } = useTranslation('plugin');
|
|
23
23
|
const { styles } = useStyles();
|
|
24
24
|
const [open, setOpen] = useState(false);
|
|
25
|
+
const item = usePluginStore(pluginSelectors.getPluginMetaByName(function_call?.name || ''));
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const avatar = plugin?.avatar ? (
|
|
29
|
-
<Avatar avatar={plugin?.avatar} size={32} />
|
|
27
|
+
const avatar = item?.meta.avatar ? (
|
|
28
|
+
<Avatar avatar={item?.meta.avatar} size={32} />
|
|
30
29
|
) : (
|
|
31
30
|
<Icon icon={LucideToyBrick} />
|
|
32
31
|
);
|
|
@@ -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 {
|
|
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>(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
isJSON =
|
|
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
|
-
<
|
|
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 {
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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 (
|