@lobehub/chat 0.63.3 → 0.64.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/CHANGELOG.md +62 -0
- package/locales/en_US/common.json +7 -3
- package/locales/en_US/error.json +15 -1
- package/locales/en_US/plugin.json +79 -1
- package/locales/en_US/setting.json +4 -1
- package/locales/zh_CN/common.json +7 -3
- package/locales/zh_CN/error.json +14 -0
- package/locales/zh_CN/plugin.json +79 -20
- package/locales/zh_CN/setting.json +3 -0
- package/package.json +1 -1
- package/src/components/EmojiPicker/index.tsx +13 -5
- package/src/features/AgentSetting/AgentMeta/index.tsx +1 -1
- package/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +72 -0
- package/src/features/AgentSetting/AgentPlugin/MarketList.tsx +160 -0
- package/src/features/AgentSetting/AgentPlugin/PluginSettings.tsx +76 -0
- package/src/features/AgentSetting/AgentPlugin/index.tsx +8 -143
- package/src/features/PluginDevModal/ManifestForm.tsx +96 -0
- package/src/features/PluginDevModal/MetaForm.tsx +90 -0
- package/src/features/PluginDevModal/PluginPreview.tsx +36 -0
- package/src/features/PluginDevModal/index.tsx +153 -0
- package/src/locales/default/error.ts +15 -23
- package/src/locales/default/plugin.ts +79 -3
- package/src/locales/default/setting.ts +3 -0
- package/src/locales/resources/en_US.ts +2 -0
- package/src/pages/chat/features/Conversation/ChatList/Error/index.tsx +2 -1
- package/src/pages/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +7 -4
- package/src/pages/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +27 -33
- package/src/services/plugin.ts +6 -3
- package/src/store/plugin/action.ts +23 -2
- package/src/store/plugin/initialState.ts +17 -1
- package/src/store/plugin/reducers/devPluginList.ts +56 -0
- package/src/store/plugin/selectors.ts +9 -2
- package/src/store/plugin/store.ts +5 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
|
2
|
+
import { App, Button, ConfigProvider, Form, Modal, Popconfirm } from 'antd';
|
|
3
|
+
import { createStyles, useTheme } from 'antd-style';
|
|
4
|
+
import { LucideBlocks } from 'lucide-react';
|
|
5
|
+
import { lighten } from 'polished';
|
|
6
|
+
import { memo, useEffect } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
import { DevPlugin } from '@/store/plugin/initialState';
|
|
11
|
+
|
|
12
|
+
import ManifestForm from './ManifestForm';
|
|
13
|
+
import MetaForm from './MetaForm';
|
|
14
|
+
import PluginPreview from './PluginPreview';
|
|
15
|
+
|
|
16
|
+
const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
|
17
|
+
content: css`
|
|
18
|
+
.${prefixCls}-modal-content {
|
|
19
|
+
border: 1px solid ${token.colorSplit};
|
|
20
|
+
border-radius: 12px;
|
|
21
|
+
}
|
|
22
|
+
`,
|
|
23
|
+
root: css`
|
|
24
|
+
backdrop-filter: blur(5px);
|
|
25
|
+
`,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
interface DevModalProps {
|
|
29
|
+
onDelete?: () => void;
|
|
30
|
+
onOpenChange: (open: boolean) => void;
|
|
31
|
+
onSave?: (value: DevPlugin) => void;
|
|
32
|
+
onValueChange?: (value: Partial<DevPlugin>) => void;
|
|
33
|
+
open?: boolean;
|
|
34
|
+
showDelete?: boolean;
|
|
35
|
+
value?: DevPlugin;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const DevModal = memo<DevModalProps>(
|
|
39
|
+
({ open, showDelete, value, onValueChange, onSave, onOpenChange, onDelete }) => {
|
|
40
|
+
const { t } = useTranslation('plugin');
|
|
41
|
+
|
|
42
|
+
const [form] = Form.useForm();
|
|
43
|
+
const theme = useTheme();
|
|
44
|
+
|
|
45
|
+
const { styles } = useStyles();
|
|
46
|
+
|
|
47
|
+
const { message } = App.useApp();
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
form.setFieldsValue(value);
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
const footer = (
|
|
54
|
+
<Flexbox horizontal justify={'space-between'} style={{ marginTop: 24 }}>
|
|
55
|
+
{showDelete ? (
|
|
56
|
+
<Popconfirm
|
|
57
|
+
okButtonProps={{
|
|
58
|
+
danger: true,
|
|
59
|
+
type: 'primary',
|
|
60
|
+
}}
|
|
61
|
+
onConfirm={() => {
|
|
62
|
+
onDelete?.();
|
|
63
|
+
message.success(t('dev.deleteSuccess'));
|
|
64
|
+
}}
|
|
65
|
+
placement={'topLeft'}
|
|
66
|
+
title={t('dev.confirmDeleteDevPlugin')}
|
|
67
|
+
>
|
|
68
|
+
<Button danger>{t('delete', { ns: 'common' })}</Button>
|
|
69
|
+
</Popconfirm>
|
|
70
|
+
) : (
|
|
71
|
+
<div />
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
<Flexbox horizontal>
|
|
75
|
+
<Button
|
|
76
|
+
onClick={() => {
|
|
77
|
+
onOpenChange(false);
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
{t('cancel', { ns: 'common' })}
|
|
81
|
+
</Button>
|
|
82
|
+
<Button
|
|
83
|
+
onClick={() => {
|
|
84
|
+
form.submit();
|
|
85
|
+
}}
|
|
86
|
+
type={'primary'}
|
|
87
|
+
>
|
|
88
|
+
{t('dev.save')}
|
|
89
|
+
</Button>
|
|
90
|
+
</Flexbox>
|
|
91
|
+
</Flexbox>
|
|
92
|
+
);
|
|
93
|
+
return (
|
|
94
|
+
<Form.Provider
|
|
95
|
+
onFormChange={() => {
|
|
96
|
+
onValueChange?.(form.getFieldsValue());
|
|
97
|
+
}}
|
|
98
|
+
onFormFinish={(_, info) => {
|
|
99
|
+
onSave?.(info.values as DevPlugin);
|
|
100
|
+
message.success(t('dev.saveSuccess'));
|
|
101
|
+
onOpenChange(false);
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<ConfigProvider
|
|
105
|
+
theme={{
|
|
106
|
+
token: {
|
|
107
|
+
colorBgElevated: lighten(0.005, theme.colorBgContainer),
|
|
108
|
+
},
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<Modal
|
|
112
|
+
centered
|
|
113
|
+
closable
|
|
114
|
+
footer={footer}
|
|
115
|
+
maskClosable
|
|
116
|
+
okText={t('dev.save')}
|
|
117
|
+
onCancel={() => {
|
|
118
|
+
onOpenChange(false);
|
|
119
|
+
}}
|
|
120
|
+
onOk={() => {
|
|
121
|
+
form.submit();
|
|
122
|
+
}}
|
|
123
|
+
open={open}
|
|
124
|
+
style={{ height: 750 }}
|
|
125
|
+
title={
|
|
126
|
+
<Flexbox gap={8} horizontal>
|
|
127
|
+
<Icon icon={LucideBlocks} />
|
|
128
|
+
{t('dev.title')}
|
|
129
|
+
</Flexbox>
|
|
130
|
+
}
|
|
131
|
+
width={700}
|
|
132
|
+
wrapClassName={styles.root}
|
|
133
|
+
>
|
|
134
|
+
<Flexbox gap={12}>
|
|
135
|
+
{t('dev.modalDesc')}
|
|
136
|
+
{/*<Tabs*/}
|
|
137
|
+
{/* items={[*/}
|
|
138
|
+
{/* { children: <MetaForm />, key: 'meta', label: t('dev.tabs.meta') },*/}
|
|
139
|
+
{/* { children: <ManifestForm />, key: 'manifest', label: t('dev.tabs.manifest') },*/}
|
|
140
|
+
{/* ]}*/}
|
|
141
|
+
{/*/>*/}
|
|
142
|
+
<PluginPreview />
|
|
143
|
+
<ManifestForm form={form} mode={'url'} />
|
|
144
|
+
<MetaForm form={form} />
|
|
145
|
+
</Flexbox>
|
|
146
|
+
</Modal>
|
|
147
|
+
</ConfigProvider>
|
|
148
|
+
</Form.Provider>
|
|
149
|
+
);
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
export default DevModal;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { PluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
2
|
-
|
|
3
|
-
import { ChatErrorType } from '@/types/fetch';
|
|
4
|
-
|
|
5
1
|
export default {
|
|
6
2
|
pluginSettings: {
|
|
7
3
|
desc: '完成以下配置,即可开始使用该插件',
|
|
@@ -19,28 +15,24 @@ export default {
|
|
|
19
15
|
503: '很抱歉,服务器当前无法处理您的请求,可能是由于过载或正在进行维护,请稍后再试',
|
|
20
16
|
504: '很抱歉,服务器没有等到上游服务器的回应,请稍后再试',
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
[PluginErrorType.PluginMetaInvalid]:
|
|
29
|
-
'很抱歉,该插件的元信息校验未通过,请检查插件元信息格式是否规范',
|
|
30
|
-
[PluginErrorType.PluginManifestNotFound]:
|
|
18
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
19
|
+
PluginMarketIndexNotFound: '很抱歉,服务器没有找到插件索引,请检查索引地址是否正确',
|
|
20
|
+
PluginMarketIndexInvalid: '很抱歉,插件索引校验未通过,请检查索引文件格式是否规范',
|
|
21
|
+
PluginMetaNotFound: '很抱歉,没有在索引中发现该插件,请插件在索引中的配置信息',
|
|
22
|
+
PluginMetaInvalid: '很抱歉,该插件的元信息校验未通过,请检查插件元信息格式是否规范',
|
|
23
|
+
PluginManifestNotFound:
|
|
31
24
|
'很抱歉,服务器没有找到该插件的描述清单 (manifest.json),请检查插件描述文件地址是否正确',
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
[PluginErrorType.PluginApiNotFound]:
|
|
25
|
+
PluginManifestInvalid: '很抱歉,该插件的描述清单校验未通过,请检查描述清单格式是否规范',
|
|
26
|
+
PluginApiNotFound:
|
|
35
27
|
'很抱歉,插件描述清单中不存在该 API ,请检查你的请求方法与插件清单 API 是否匹配',
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'
|
|
28
|
+
PluginApiParamsError: '很抱歉,该插件请求的入参校验未通过,请检查入参与 Api 描述信息是否匹配',
|
|
29
|
+
PluginSettingsInvalid: '该插件需要正确配置后才可以使用,请检查你的配置是否正确',
|
|
30
|
+
PluginServerError:
|
|
31
|
+
'插件服务端请求返回出错,请检查根据下面的报错信息检查你的插件描述文件、插件配置或服务端实现',
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
InvalidAccessCode: '密码不正确或为空,请输入正确的访问密码,或者添加自定义 OpenAI API Key',
|
|
34
|
+
OpenAIBizError: '请求 OpenAI 服务出错,请根据以下信息排查或重试',
|
|
35
|
+
/* eslint-enable */
|
|
44
36
|
},
|
|
45
37
|
unlock: {
|
|
46
38
|
apikey: {
|
|
@@ -4,15 +4,91 @@ export default {
|
|
|
4
4
|
function_call: '函数调用',
|
|
5
5
|
response: '返回结果',
|
|
6
6
|
},
|
|
7
|
+
dev: {
|
|
8
|
+
confirmDeleteDevPlugin: '即将删除该本地插件,删除后将无法找回,是否删除该插件?',
|
|
9
|
+
deleteSuccess: '插件删除成功',
|
|
10
|
+
manifest: {
|
|
11
|
+
identifier: {
|
|
12
|
+
desc: '插件的唯一标识',
|
|
13
|
+
label: '标识符',
|
|
14
|
+
},
|
|
15
|
+
mode: {
|
|
16
|
+
'local': '可视化配置',
|
|
17
|
+
'local-tooltip': '暂时不支持可视化配置',
|
|
18
|
+
'url': '在线链接',
|
|
19
|
+
},
|
|
20
|
+
name: {
|
|
21
|
+
desc: '插件标题',
|
|
22
|
+
label: '标题',
|
|
23
|
+
placeholder: '搜索引擎',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
meta: {
|
|
27
|
+
author: {
|
|
28
|
+
desc: '插件的作者',
|
|
29
|
+
label: '作者',
|
|
30
|
+
},
|
|
31
|
+
avatar: {
|
|
32
|
+
desc: '插件的图标,可以使用 Emoji,也可以使用 URL',
|
|
33
|
+
label: '图标',
|
|
34
|
+
},
|
|
35
|
+
description: {
|
|
36
|
+
desc: '插件描述',
|
|
37
|
+
label: '描述',
|
|
38
|
+
placeholder: '查询搜索引擎获取信息',
|
|
39
|
+
},
|
|
40
|
+
formFieldRequired: '该字段为必填项',
|
|
41
|
+
homepage: {
|
|
42
|
+
desc: '插件的首页',
|
|
43
|
+
label: '首页',
|
|
44
|
+
},
|
|
45
|
+
identifier: {
|
|
46
|
+
desc: '插件的唯一标识,仅支持英文字符、数字、破折号 - 、下划线 _',
|
|
47
|
+
errorDuplicate: '标识符和已有插件重复,请修改标识符',
|
|
48
|
+
label: '标识符',
|
|
49
|
+
pattenErrorMessage: '只能输入英文字符、数字 、- 和_ 这两个符号',
|
|
50
|
+
},
|
|
51
|
+
manifest: {
|
|
52
|
+
desc: 'LobeChat 将会通过该链接安装插件',
|
|
53
|
+
invalid: '输入的 manifest 链接无效,或 manifest 不符合规范',
|
|
54
|
+
label: '插件描述文件 Url 地址',
|
|
55
|
+
urlError: '请输入一个有效的网址',
|
|
56
|
+
},
|
|
57
|
+
title: {
|
|
58
|
+
desc: '插件标题',
|
|
59
|
+
label: '标题',
|
|
60
|
+
placeholder: '搜索引擎',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
metaConfig: '插件元信息配置',
|
|
64
|
+
modalDesc: '添加自定义插件后,可用于插件开发验证,也可直接在会话中使用。插件开发文档请参考',
|
|
65
|
+
preview: {
|
|
66
|
+
card: '预览插件展示效果',
|
|
67
|
+
desc: '预览插件描述',
|
|
68
|
+
title: '插件名称预览',
|
|
69
|
+
},
|
|
70
|
+
save: '保存',
|
|
71
|
+
saveSuccess: '插件设置保存成功',
|
|
72
|
+
tabs: {
|
|
73
|
+
manifest: '功能描述清单 (Manifest)',
|
|
74
|
+
meta: '插件元信息',
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
title: '添加自定义插件',
|
|
78
|
+
},
|
|
79
|
+
list: {
|
|
80
|
+
item: {
|
|
81
|
+
'local.config': '配置',
|
|
82
|
+
'local.title': '本地',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
|
|
7
86
|
loading: {
|
|
8
87
|
content: '数据获取中...',
|
|
9
88
|
plugin: '插件运行中...',
|
|
10
89
|
},
|
|
11
90
|
pluginList: '插件列表',
|
|
12
91
|
plugins: {
|
|
13
|
-
realtimeWeather: '实时天气预报',
|
|
14
|
-
searchEngine: '搜索引擎',
|
|
15
92
|
unknown: '插件检测中...',
|
|
16
|
-
websiteCrawler: '网页内容提取',
|
|
17
93
|
},
|
|
18
94
|
};
|
|
@@ -3,6 +3,7 @@ import empty from '../../../locales/en_US/empty.json';
|
|
|
3
3
|
import error from '../../../locales/en_US/error.json';
|
|
4
4
|
import plugin from '../../../locales/en_US/plugin.json';
|
|
5
5
|
import setting from '../../../locales/en_US/setting.json';
|
|
6
|
+
import welcome from '../../../locales/en_US/welcome.json';
|
|
6
7
|
|
|
7
8
|
const resources = {
|
|
8
9
|
common,
|
|
@@ -10,6 +11,7 @@ const resources = {
|
|
|
10
11
|
error,
|
|
11
12
|
plugin,
|
|
12
13
|
setting,
|
|
14
|
+
welcome,
|
|
13
15
|
} as const;
|
|
14
16
|
|
|
15
17
|
export default resources;
|
|
@@ -18,7 +18,8 @@ export const renderErrorMessage: RenderErrorMessage = (error, message: ChatMessa
|
|
|
18
18
|
case PluginErrorType.PluginManifestInvalid:
|
|
19
19
|
case PluginErrorType.PluginManifestNotFound:
|
|
20
20
|
case PluginErrorType.PluginApiNotFound:
|
|
21
|
-
case PluginErrorType.PluginApiParamsError:
|
|
21
|
+
case PluginErrorType.PluginApiParamsError:
|
|
22
|
+
case PluginErrorType.PluginServerError: {
|
|
22
23
|
return <PluginError content={(error as any).body} id={message.id} />;
|
|
23
24
|
}
|
|
24
25
|
case PluginErrorType.PluginSettingsInvalid: {
|
|
@@ -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 { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
9
|
+
import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
10
10
|
|
|
11
11
|
import PluginResult from './PluginResultRender';
|
|
12
12
|
import { useStyles } from './style';
|
|
@@ -27,8 +27,11 @@ const FunctionCall = memo<FunctionCallProps>(
|
|
|
27
27
|
|
|
28
28
|
const item = usePluginStore(pluginSelectors.getPluginMetaById(id));
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
|
|
30
|
+
const pluginAvatar = pluginHelpers.getPluginAvatar(item?.meta);
|
|
31
|
+
const pluginTitle = pluginHelpers.getPluginTitle(item?.meta);
|
|
32
|
+
|
|
33
|
+
const avatar = pluginAvatar ? (
|
|
34
|
+
<Avatar avatar={pluginAvatar} size={32} />
|
|
32
35
|
) : (
|
|
33
36
|
<Icon icon={LucideToyBrick} />
|
|
34
37
|
);
|
|
@@ -54,7 +57,7 @@ const FunctionCall = memo<FunctionCallProps>(
|
|
|
54
57
|
) : (
|
|
55
58
|
avatar
|
|
56
59
|
)}
|
|
57
|
-
{t(
|
|
60
|
+
{pluginTitle ?? t('plugins.unknown')}
|
|
58
61
|
<Icon icon={open ? LucideChevronUp : LucideChevronDown} />
|
|
59
62
|
</Flexbox>
|
|
60
63
|
{open && (
|
|
@@ -12,41 +12,35 @@ export interface FunctionMessageProps extends ChatMessage {
|
|
|
12
12
|
loading?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
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;
|
|
15
|
+
const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
|
|
16
|
+
const { t } = useTranslation('plugin');
|
|
45
17
|
|
|
18
|
+
const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
|
|
19
|
+
let isJSON = true;
|
|
20
|
+
try {
|
|
21
|
+
JSON.parse(content);
|
|
22
|
+
} catch {
|
|
23
|
+
isJSON = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// if (!loading)
|
|
27
|
+
|
|
28
|
+
if (!isJSON) {
|
|
46
29
|
return (
|
|
47
|
-
<
|
|
30
|
+
<Flexbox gap={8} horizontal>
|
|
31
|
+
<div>
|
|
32
|
+
<Loading3QuartersOutlined spin />
|
|
33
|
+
</div>
|
|
34
|
+
{t('loading.content')}
|
|
35
|
+
</Flexbox>
|
|
48
36
|
);
|
|
49
|
-
}
|
|
50
|
-
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!manifest?.ui?.url) return;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<CustomRender content={JSON.parse(content)} name={name || 'unknown'} url={manifest.ui?.url} />
|
|
43
|
+
);
|
|
44
|
+
});
|
|
51
45
|
|
|
52
46
|
export default PluginMessage;
|
package/src/services/plugin.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PluginRequestPayload, createHeadersWithPluginSettings } from '@lobehub/chat-plugin-sdk';
|
|
2
2
|
|
|
3
|
-
import { usePluginStore } from '@/store/plugin';
|
|
3
|
+
import { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
4
4
|
import { getMessageError } from '@/utils/fetch';
|
|
5
5
|
|
|
6
6
|
import { URLS } from './url';
|
|
@@ -17,10 +17,13 @@ export const fetchPlugin = async (
|
|
|
17
17
|
params: PluginRequestPayload,
|
|
18
18
|
options?: FetchChatModelOptions,
|
|
19
19
|
) => {
|
|
20
|
-
const
|
|
20
|
+
const s = usePluginStore.getState();
|
|
21
|
+
|
|
22
|
+
const settings = pluginSelectors.getPluginSettingsById(params.identifier)(s);
|
|
23
|
+
const manifest = pluginSelectors.getPluginManifestById(params.identifier)(s);
|
|
21
24
|
|
|
22
25
|
const res = await fetch(URLS.plugins, {
|
|
23
|
-
body: JSON.stringify(params),
|
|
26
|
+
body: JSON.stringify({ ...params, manifest }),
|
|
24
27
|
headers: createHeadersWithPluginSettings(settings),
|
|
25
28
|
method: 'POST',
|
|
26
29
|
signal: options?.signal,
|
|
@@ -14,6 +14,8 @@ import { pluginSelectors } from '@/store/plugin/selectors';
|
|
|
14
14
|
import { LobeSessions } from '@/types/session';
|
|
15
15
|
import { setNamespace } from '@/utils/storeDebug';
|
|
16
16
|
|
|
17
|
+
import { DevPlugin, defaultDevPlugin } from './initialState';
|
|
18
|
+
import { DevListDispatch, devPluginListReducer } from './reducers/devPluginList';
|
|
17
19
|
import { PluginDispatch, pluginManifestReducer } from './reducers/manifest';
|
|
18
20
|
import { PluginStore } from './store';
|
|
19
21
|
|
|
@@ -24,9 +26,12 @@ const t = setNamespace('plugin');
|
|
|
24
26
|
*/
|
|
25
27
|
export interface PluginAction {
|
|
26
28
|
checkLocalEnabledPlugins: (sessions: LobeSessions) => void;
|
|
29
|
+
dispatchDevPluginList: (payload: DevListDispatch) => void;
|
|
27
30
|
dispatchPluginManifest: (payload: PluginDispatch) => void;
|
|
28
31
|
fetchPluginManifest: (name: string) => Promise<void>;
|
|
32
|
+
saveToDevList: (value: DevPlugin) => void;
|
|
29
33
|
updateManifestLoadingState: (key: string, value: boolean | undefined) => void;
|
|
34
|
+
updateNewDevPlugin: (value: Partial<DevPlugin>) => void;
|
|
30
35
|
updatePluginSettings: <T>(id: string, settings: Partial<T>) => void;
|
|
31
36
|
useFetchPluginList: () => SWRResponse<LobeChatPluginsMarketIndex>;
|
|
32
37
|
}
|
|
@@ -55,13 +60,18 @@ export const createPluginSlice: StateCreator<
|
|
|
55
60
|
|
|
56
61
|
set({ manifestPrepared: true }, false, t('checkLocalEnabledPlugins'));
|
|
57
62
|
},
|
|
63
|
+
dispatchDevPluginList: (payload) => {
|
|
64
|
+
const { devPluginList } = get();
|
|
65
|
+
|
|
66
|
+
const nextList = devPluginListReducer(devPluginList, payload);
|
|
67
|
+
set({ devPluginList: nextList }, false, t('dispatchDevList', payload));
|
|
68
|
+
},
|
|
58
69
|
dispatchPluginManifest: (payload) => {
|
|
59
70
|
const { pluginManifestMap } = get();
|
|
60
71
|
const nextManifest = pluginManifestReducer(pluginManifestMap, payload);
|
|
61
72
|
|
|
62
73
|
set({ pluginManifestMap: nextManifest }, false, t('dispatchPluginManifest', payload));
|
|
63
74
|
},
|
|
64
|
-
|
|
65
75
|
fetchPluginManifest: async (name) => {
|
|
66
76
|
const plugin = pluginSelectors.getPluginMetaById(name)(get());
|
|
67
77
|
// 1. 校验文件
|
|
@@ -103,6 +113,10 @@ export const createPluginSlice: StateCreator<
|
|
|
103
113
|
// 4. 存储 manifest 信息
|
|
104
114
|
get().dispatchPluginManifest({ id: plugin.identifier, plugin: data, type: 'addManifest' });
|
|
105
115
|
},
|
|
116
|
+
saveToDevList: (value) => {
|
|
117
|
+
get().dispatchDevPluginList({ plugin: value, type: 'addItem' });
|
|
118
|
+
set({ newDevPlugin: defaultDevPlugin }, false, t('saveToDevList'));
|
|
119
|
+
},
|
|
106
120
|
|
|
107
121
|
updateManifestLoadingState: (key, value) => {
|
|
108
122
|
set(
|
|
@@ -113,7 +127,13 @@ export const createPluginSlice: StateCreator<
|
|
|
113
127
|
t('updateManifestLoadingState'),
|
|
114
128
|
);
|
|
115
129
|
},
|
|
116
|
-
|
|
130
|
+
updateNewDevPlugin: (newDevPlugin) => {
|
|
131
|
+
set(
|
|
132
|
+
{ newDevPlugin: merge({}, get().newDevPlugin, newDevPlugin) },
|
|
133
|
+
false,
|
|
134
|
+
t('updateNewDevPlugin'),
|
|
135
|
+
);
|
|
136
|
+
},
|
|
117
137
|
updatePluginSettings: (id, settings) => {
|
|
118
138
|
set(
|
|
119
139
|
produce((draft) => {
|
|
@@ -123,6 +143,7 @@ export const createPluginSlice: StateCreator<
|
|
|
123
143
|
t('updatePluginSettings'),
|
|
124
144
|
);
|
|
125
145
|
},
|
|
146
|
+
|
|
126
147
|
useFetchPluginList: () =>
|
|
127
148
|
useSWR<LobeChatPluginsMarketIndex>('fetchPluginList', getPluginList, {
|
|
128
149
|
onSuccess: (pluginMarketIndex) => {
|
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
|
|
1
|
+
import { LobeChatPluginManifest, LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
|
|
2
2
|
|
|
3
3
|
import { PluginManifestMap } from '@/types/plugin';
|
|
4
4
|
|
|
5
5
|
export type PluginManifestLoadingState = Record<string, boolean>;
|
|
6
6
|
export type PluginsSettings = Record<string, any>;
|
|
7
7
|
|
|
8
|
+
export interface DevPlugin extends LobeChatPluginMeta {
|
|
9
|
+
apiMode: 'openapi' | 'simple';
|
|
10
|
+
enableSettings: boolean;
|
|
11
|
+
manifestConfig?: LobeChatPluginManifest;
|
|
12
|
+
manifestMode: 'local' | 'url';
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
export interface PluginStoreState {
|
|
16
|
+
devPluginList: DevPlugin[];
|
|
9
17
|
manifestPrepared: boolean;
|
|
18
|
+
newDevPlugin: Partial<DevPlugin>;
|
|
10
19
|
pluginList: LobeChatPluginMeta[];
|
|
11
20
|
pluginManifestLoading: PluginManifestLoadingState;
|
|
12
21
|
pluginManifestMap: PluginManifestMap;
|
|
13
22
|
pluginsSettings: PluginsSettings;
|
|
14
23
|
}
|
|
24
|
+
export const defaultDevPlugin: Partial<DevPlugin> = {
|
|
25
|
+
apiMode: 'simple',
|
|
26
|
+
enableSettings: false,
|
|
27
|
+
manifestMode: 'url',
|
|
28
|
+
};
|
|
15
29
|
|
|
16
30
|
export const initialState: PluginStoreState = {
|
|
31
|
+
devPluginList: [],
|
|
17
32
|
manifestPrepared: false,
|
|
33
|
+
newDevPlugin: defaultDevPlugin,
|
|
18
34
|
pluginList: [],
|
|
19
35
|
pluginManifestLoading: {},
|
|
20
36
|
pluginManifestMap: {},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { produce } from 'immer';
|
|
2
|
+
|
|
3
|
+
import { DevPlugin } from '../initialState';
|
|
4
|
+
|
|
5
|
+
export type DevListState = DevPlugin[];
|
|
6
|
+
|
|
7
|
+
export type AddPluginAction = {
|
|
8
|
+
plugin: DevPlugin;
|
|
9
|
+
type: 'addItem';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type DeletePluginAction = {
|
|
13
|
+
id: string;
|
|
14
|
+
type: 'deleteItem';
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type UpdatePluginAction = {
|
|
18
|
+
id: string;
|
|
19
|
+
plugin: DevPlugin;
|
|
20
|
+
type: 'updateItem';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type DevListDispatch = AddPluginAction | DeletePluginAction | UpdatePluginAction;
|
|
24
|
+
|
|
25
|
+
export const devPluginListReducer = (
|
|
26
|
+
state: DevListState,
|
|
27
|
+
payload: DevListDispatch,
|
|
28
|
+
): DevListState => {
|
|
29
|
+
switch (payload.type) {
|
|
30
|
+
case 'addItem': {
|
|
31
|
+
return [...state, payload.plugin];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
case 'deleteItem': {
|
|
35
|
+
return produce(state, (draftState) => {
|
|
36
|
+
const deleteIndex = state.findIndex((plugin) => plugin.identifier === payload.id);
|
|
37
|
+
if (deleteIndex !== -1) {
|
|
38
|
+
draftState.splice(deleteIndex, 1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'updateItem': {
|
|
44
|
+
return produce(state, (draftState) => {
|
|
45
|
+
const updateIndex = state.findIndex((plugin) => plugin.identifier === payload.id);
|
|
46
|
+
if (updateIndex !== -1) {
|
|
47
|
+
draftState[updateIndex] = payload.plugin;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
default: {
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
@@ -23,8 +23,13 @@ const enabledSchema =
|
|
|
23
23
|
);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
const pluginList = (s: PluginStoreState) => [...s.pluginList, ...s.devPluginList];
|
|
27
|
+
|
|
26
28
|
const getPluginMetaById = (id: string) => (s: PluginStoreState) =>
|
|
27
|
-
pluginHelpers.getPluginFormList(s
|
|
29
|
+
pluginHelpers.getPluginFormList(pluginList(s), id);
|
|
30
|
+
|
|
31
|
+
const getDevPluginById = (id: string) => (s: PluginStoreState) =>
|
|
32
|
+
s.devPluginList.find((i) => i.identifier === id);
|
|
28
33
|
|
|
29
34
|
const getPluginManifestById = (id: string) => (s: PluginStoreState) => s.pluginManifestMap[id];
|
|
30
35
|
const getPluginSettingsById = (id: string) => (s: PluginStoreState) => s.pluginsSettings[id];
|
|
@@ -41,7 +46,7 @@ const getPluginManifestLoadingStatus = (id: string) => (s: PluginStoreState) =>
|
|
|
41
46
|
};
|
|
42
47
|
|
|
43
48
|
const displayPluginList = (s: PluginStoreState) =>
|
|
44
|
-
s.
|
|
49
|
+
pluginList(s).map((p) => ({
|
|
45
50
|
author: p.author,
|
|
46
51
|
avatar: p.meta?.avatar,
|
|
47
52
|
createAt: p.createAt,
|
|
@@ -54,8 +59,10 @@ const displayPluginList = (s: PluginStoreState) =>
|
|
|
54
59
|
export const pluginSelectors = {
|
|
55
60
|
displayPluginList,
|
|
56
61
|
enabledSchema,
|
|
62
|
+
getDevPluginById,
|
|
57
63
|
getPluginManifestById,
|
|
58
64
|
getPluginManifestLoadingStatus,
|
|
59
65
|
getPluginMetaById,
|
|
60
66
|
getPluginSettingsById,
|
|
67
|
+
pluginList,
|
|
61
68
|
};
|