@lobehub/chat 1.84.3 → 1.84.5

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.
@@ -0,0 +1,72 @@
1
+ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
+ import { Block, Icon } from '@lobehub/ui';
3
+ import { Form as AForm, Button, FormInstance, Typography } from 'antd';
4
+ import { useTheme } from 'antd-style';
5
+ import { FileCode } from 'lucide-react';
6
+ import { memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import ManifestPreviewer from '@/components/ManifestPreviewer';
11
+ import PluginAvatar from '@/features/PluginStore/PluginItem/PluginAvatar';
12
+ import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
13
+ import { pluginHelpers } from '@/store/tool';
14
+
15
+ import ApiVisualizer from './ApiVisualizer';
16
+ import PluginEmptyState from './EmptyState';
17
+
18
+ const PluginPreview = memo<{ form: FormInstance }>(({ form }) => {
19
+ const { t } = useTranslation('plugin');
20
+ const theme = useTheme();
21
+ const manifest: LobeChatPluginManifest = AForm.useWatch(['manifest'], form);
22
+ const meta = manifest?.meta;
23
+
24
+ if (!manifest)
25
+ return (
26
+ <Flexbox flex={2} height={'100%'} style={{ background: theme.colorBgLayout }}>
27
+ <PluginEmptyState />
28
+ </Flexbox>
29
+ );
30
+
31
+ return (
32
+ <Flexbox
33
+ flex={2}
34
+ gap={24}
35
+ padding={12}
36
+ style={{ background: theme.colorBgLayout, overflowY: 'auto' }}
37
+ >
38
+ <Block
39
+ gap={16}
40
+ horizontal
41
+ justify={'space-between'}
42
+ padding={16}
43
+ title={t('dev.preview.card')}
44
+ variant={'outlined'}
45
+ >
46
+ <Flexbox gap={16} horizontal>
47
+ <PluginAvatar avatar={pluginHelpers.getPluginAvatar(meta)} size={40} />
48
+ <Flexbox gap={2}>
49
+ <Flexbox align={'center'} gap={8} horizontal>
50
+ {pluginHelpers.getPluginTitle(meta) || 'Plugin Title'}
51
+ <PluginTag type={'customPlugin'} />
52
+ </Flexbox>
53
+ <Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
54
+ {pluginHelpers.getPluginDesc(meta) || 'Plugin Description'}
55
+ </Typography.Text>
56
+ </Flexbox>
57
+ </Flexbox>
58
+
59
+ {manifest && (
60
+ <ManifestPreviewer manifest={manifest}>
61
+ <Flexbox>
62
+ <Button icon={<Icon icon={FileCode} />}>{t('dev.mcp.previewManifest')}</Button>
63
+ </Flexbox>
64
+ </ManifestPreviewer>
65
+ )}
66
+ </Block>
67
+ {manifest && <ApiVisualizer apis={manifest.api as any} />}
68
+ </Flexbox>
69
+ );
70
+ });
71
+
72
+ export default PluginPreview;
@@ -1,4 +1,4 @@
1
- import { Alert, Button, Icon, Modal, Segmented, Tag } from '@lobehub/ui';
1
+ import { Alert, Button, Drawer, Icon, Segmented, Tag } from '@lobehub/ui';
2
2
  import { App, Form, Popconfirm } from 'antd';
3
3
  import { useResponsive } from 'antd-style';
4
4
  import { MoveUpRight } from 'lucide-react';
@@ -29,7 +29,9 @@ const DevModal = memo<DevModalProps>(
29
29
  const [configMode, setConfigMode] = useState<'url' | 'mcp'>('mcp');
30
30
  const { t } = useTranslation('plugin');
31
31
  const { message } = App.useApp();
32
+
32
33
  const [submitting, setSubmitting] = useState(false);
34
+
33
35
  const { mobile } = useResponsive();
34
36
  const [form] = Form.useForm();
35
37
  useEffect(() => {
@@ -106,83 +108,94 @@ const DevModal = memo<DevModalProps>(
106
108
  onOpenChange(false);
107
109
  }}
108
110
  >
109
- <Modal
110
- allowFullscreen
111
+ <Drawer
112
+ containerMaxWidth={'auto'}
111
113
  destroyOnClose
112
114
  footer={footer}
113
- okText={t('dev.save')}
114
- onCancel={(e) => {
115
+ height={'100vh'}
116
+ onClose={(e) => {
115
117
  e.stopPropagation();
116
118
  onOpenChange(false);
117
119
  }}
118
- onOk={(e) => {
119
- e.stopPropagation();
120
- form.submit();
121
- }}
122
120
  open={open}
121
+ placement={'bottom'}
122
+ push={false}
123
+ styles={{
124
+ body: {
125
+ padding: 0,
126
+ },
127
+ bodyContent: {
128
+ height: '100%',
129
+ },
130
+ }}
123
131
  title={t(isEditMode ? 'dev.title.edit' : 'dev.title.create')}
132
+ width={mobile ? '100%' : 800}
124
133
  >
125
134
  <Flexbox
126
- gap={16}
135
+ gap={0}
136
+ height={'100%'}
137
+ horizontal
127
138
  onClick={(e) => {
128
139
  e.stopPropagation();
129
140
  }}
130
141
  >
131
- <Segmented
132
- block
133
- onChange={(e) => {
134
- setConfigMode(e as 'url' | 'mcp');
135
- }}
136
- options={[
137
- {
138
- label: (
139
- <Flexbox align={'center'} gap={4} horizontal justify={'center'}>
140
- {t('dev.manifest.mode.mcp')}
141
- <div>
142
- <Tag bordered={false} color={'warning'}>
143
- {t('dev.manifest.mode.mcpExp')}
144
- </Tag>
145
- </div>
146
- </Flexbox>
147
- ),
148
- value: 'mcp',
149
- },
150
- {
151
- label: t('dev.manifest.mode.url'),
152
- value: 'url',
153
- },
154
- ]}
155
- value={configMode}
156
- variant={'filled'}
157
- />
142
+ <Flexbox flex={3} gap={16} padding={24} style={{ overflowY: 'auto' }}>
143
+ <Segmented
144
+ block
145
+ onChange={(e) => {
146
+ setConfigMode(e as 'url' | 'mcp');
147
+ }}
148
+ options={[
149
+ {
150
+ label: (
151
+ <Flexbox align={'center'} gap={4} horizontal justify={'center'}>
152
+ {t('dev.manifest.mode.mcp')}
153
+ <div>
154
+ <Tag bordered={false} color={'warning'}>
155
+ {t('dev.manifest.mode.mcpExp')}
156
+ </Tag>
157
+ </div>
158
+ </Flexbox>
159
+ ),
160
+ value: 'mcp',
161
+ },
162
+ {
163
+ label: t('dev.manifest.mode.url'),
164
+ value: 'url',
165
+ },
166
+ ]}
167
+ value={configMode}
168
+ variant={'filled'}
169
+ />
158
170
 
159
- {configMode === 'url' && (
160
- <>
161
- <Alert
162
- message={
163
- <Trans i18nKey={'dev.modalDesc'} ns={'plugin'}>
164
- 添加自定义插件后,可用于插件开发验证,也可直接在会话中使用。插件开发文档请参考:
165
- <a
166
- href={WIKI_PLUGIN_GUIDE}
167
- rel="noreferrer"
168
- style={{ paddingInline: 8 }}
169
- target={'_blank'}
170
- >
171
- 文档
172
- </a>
173
- <Icon icon={MoveUpRight} />
174
- </Trans>
175
- }
176
- showIcon
177
- type={'info'}
178
- />
179
- <UrlManifestForm form={form} isEditMode={isEditMode} />
180
- </>
181
- )}
182
- {configMode === 'mcp' && <MCPManifestForm form={form} isEditMode={isEditMode} />}
171
+ {configMode === 'url' && (
172
+ <>
173
+ <Alert
174
+ message={
175
+ <Trans i18nKey={'dev.modalDesc'} ns={'plugin'}>
176
+ 添加自定义插件后,可用于插件开发验证,也可直接在会话中使用。插件开发文档请参考:
177
+ <a
178
+ href={WIKI_PLUGIN_GUIDE}
179
+ rel="noreferrer"
180
+ style={{ paddingInline: 8 }}
181
+ target={'_blank'}
182
+ >
183
+ 文档
184
+ </a>
185
+ <Icon icon={MoveUpRight} />
186
+ </Trans>
187
+ }
188
+ showIcon
189
+ type={'info'}
190
+ />
191
+ <UrlManifestForm form={form} isEditMode={isEditMode} />
192
+ </>
193
+ )}
194
+ {configMode === 'mcp' && <MCPManifestForm form={form} isEditMode={isEditMode} />}
195
+ </Flexbox>
183
196
  <PluginPreview form={form} />
184
197
  </Flexbox>
185
- </Modal>
198
+ </Drawer>
186
199
  </Form.Provider>
187
200
  );
188
201
  },
@@ -24,10 +24,14 @@ export const LobeQwenAI = LobeOpenAICompatibleFactory({
24
24
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
25
25
  chatCompletion: {
26
26
  handlePayload: (payload) => {
27
- const { model, presence_penalty, temperature, top_p, enabledSearch, ...rest } = payload;
27
+ const { model, presence_penalty, temperature, thinking, top_p, enabledSearch, ...rest } = payload;
28
28
 
29
29
  return {
30
30
  ...rest,
31
+ ...( ['qwen3','qwen-turbo','qwen-plus']
32
+ .some(keyword => model.toLowerCase().includes(keyword))
33
+ ? { enable_thinking: thinking !== undefined ? thinking.type === 'enabled' : false }
34
+ : {}),
31
35
  frequency_penalty: undefined,
32
36
  model,
33
37
  presence_penalty: QwenLegacyModels.has(model)
@@ -70,11 +74,11 @@ export const LobeQwenAI = LobeOpenAICompatibleFactory({
70
74
  models: async ({ client }) => {
71
75
  const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
72
76
 
73
- const functionCallKeywords = ['qwen-max', 'qwen-plus', 'qwen-turbo', 'qwen2.5'];
77
+ const functionCallKeywords = ['qwen-max', 'qwen-plus', 'qwen-turbo', 'qwen-long', 'qwen1.5', 'qwen2', 'qwen2.5', 'qwen3'];
74
78
 
75
79
  const visionKeywords = ['qvq', 'vl'];
76
80
 
77
- const reasoningKeywords = ['qvq', 'qwq', 'deepseek-r1'];
81
+ const reasoningKeywords = ['qvq', 'qwq', 'deepseek-r1', 'qwen3'];
78
82
 
79
83
  const modelsPage = (await client.models.list()) as any;
80
84
  const modelList: QwenModelCard[] = modelsPage.data;
@@ -1,4 +1,5 @@
1
1
  export default {
2
+ confirm: '确定',
2
3
  debug: {
3
4
  arguments: '调用参数',
4
5
  function_call: '函数调用',
@@ -80,6 +81,13 @@ export default {
80
81
  required: '请输入 MCP 服务标识符',
81
82
  },
82
83
  previewManifest: '预览插件描述文件',
84
+ quickImport: '快速导入 JSON 配置',
85
+ quickImportError: {
86
+ empty: '输入内容不能为空',
87
+ invalidJson: '无效的 JSON 格式',
88
+ invalidStructure: 'JSON 格式无效',
89
+ },
90
+ stdioNotSupported: '当前环境不支持 stdio 类型的 MCP 插件',
83
91
  testConnection: '测试连接',
84
92
  testConnectionTip: '测试连接成功后 MCP 插件才可以被正常使用',
85
93
  type: {
@@ -148,8 +156,18 @@ export default {
148
156
  schema: 'Schema',
149
157
  },
150
158
  preview: {
159
+ api: {
160
+ noParams: '该工具没有参数',
161
+ noResults: '未找到符合搜索条件的 API',
162
+ params: '参数:',
163
+ searchPlaceholder: '搜索工具...',
164
+ },
151
165
  card: '预览插件展示效果',
152
166
  desc: '预览插件描述',
167
+ empty: {
168
+ desc: '完成配置后,将能够在此处预览插件支持的工具能力',
169
+ title: '配置插件后开始预览',
170
+ },
153
171
  title: '插件名称预览',
154
172
  },
155
173
  save: '安装插件',
@@ -1,34 +0,0 @@
1
- import { Block } from '@lobehub/ui';
2
- import { Form as AForm, FormInstance, Typography } from 'antd';
3
- import { memo } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
- import { Flexbox } from 'react-layout-kit';
6
-
7
- import PluginAvatar from '@/features/PluginStore/PluginItem/PluginAvatar';
8
- import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
9
- import { pluginHelpers } from '@/store/tool';
10
- import { LobeToolCustomPlugin } from '@/types/tool/plugin';
11
-
12
- const PluginPreview = memo<{ form: FormInstance }>(({ form }) => {
13
- const { t } = useTranslation('plugin');
14
-
15
- const plugin: LobeToolCustomPlugin = AForm.useWatch([], form);
16
- const meta = plugin?.manifest?.meta;
17
-
18
- return (
19
- <Block gap={16} horizontal padding={16} title={t('dev.preview.card')} variant={'outlined'}>
20
- <PluginAvatar avatar={pluginHelpers.getPluginAvatar(meta)} size={40} />
21
- <Flexbox gap={2}>
22
- <Flexbox align={'center'} gap={8} horizontal>
23
- {pluginHelpers.getPluginTitle(meta) || 'Plugin Title'}
24
- <PluginTag type={'customPlugin'} />
25
- </Flexbox>
26
- <Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
27
- {pluginHelpers.getPluginDesc(meta) || 'Plugin Description'}
28
- </Typography.Text>
29
- </Flexbox>
30
- </Block>
31
- );
32
- });
33
-
34
- export default PluginPreview;