@lobehub/chat 1.84.4 → 1.84.6
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 +51 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/error.json +4 -1
- package/locales/ar/plugin.json +8 -0
- package/locales/bg-BG/error.json +4 -1
- package/locales/bg-BG/plugin.json +8 -0
- package/locales/de-DE/error.json +4 -1
- package/locales/de-DE/plugin.json +8 -0
- package/locales/en-US/error.json +4 -1
- package/locales/en-US/plugin.json +8 -0
- package/locales/es-ES/error.json +4 -1
- package/locales/es-ES/plugin.json +8 -0
- package/locales/fa-IR/error.json +4 -1
- package/locales/fa-IR/plugin.json +8 -0
- package/locales/fr-FR/error.json +4 -1
- package/locales/fr-FR/plugin.json +8 -0
- package/locales/it-IT/error.json +4 -1
- package/locales/it-IT/plugin.json +8 -0
- package/locales/ja-JP/error.json +4 -1
- package/locales/ja-JP/plugin.json +8 -0
- package/locales/ko-KR/error.json +4 -1
- package/locales/ko-KR/plugin.json +8 -0
- package/locales/nl-NL/error.json +4 -1
- package/locales/nl-NL/plugin.json +8 -0
- package/locales/pl-PL/error.json +4 -1
- package/locales/pl-PL/plugin.json +8 -0
- package/locales/pt-BR/error.json +4 -1
- package/locales/pt-BR/plugin.json +8 -0
- package/locales/ru-RU/error.json +4 -1
- package/locales/ru-RU/plugin.json +8 -0
- package/locales/tr-TR/error.json +4 -1
- package/locales/tr-TR/plugin.json +8 -0
- package/locales/vi-VN/error.json +4 -1
- package/locales/vi-VN/plugin.json +8 -0
- package/locales/zh-CN/error.json +3 -0
- package/locales/zh-CN/plugin.json +8 -0
- package/locales/zh-TW/error.json +4 -1
- package/locales/zh-TW/plugin.json +8 -0
- package/package.json +1 -1
- package/src/config/aiModels/openrouter.ts +298 -6
- package/src/config/aiModels/qwen.ts +202 -4
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +28 -7
- package/src/features/PluginDevModal/PluginPreview/ApiVisualizer.tsx +12 -4
- package/src/features/PluginDevModal/PluginPreview/EmptyState.tsx +2 -3
- package/src/libs/agent-runtime/qwen/index.ts +7 -3
- package/src/libs/mcp/types.ts +1 -1
- package/src/locales/default/error.ts +3 -0
- package/src/locales/default/plugin.ts +8 -0
- package/src/server/routers/desktop/mcp.ts +10 -1
- package/src/server/routers/tools/mcp.ts +11 -1
- package/src/server/services/mcp/index.ts +35 -12
- package/src/services/mcp.ts +14 -3
- package/src/types/tool/plugin.ts +12 -2
@@ -1,13 +1,202 @@
|
|
1
1
|
import { AIChatModelCard } from '@/types/aiModel';
|
2
2
|
|
3
|
-
// https://help.aliyun.com/zh/model-studio/
|
3
|
+
// https://help.aliyun.com/zh/model-studio/models?spm=a2c4g.11186623
|
4
4
|
|
5
5
|
const qwenChatModels: AIChatModelCard[] = [
|
6
6
|
{
|
7
7
|
abilities: {
|
8
|
+
functionCall: true,
|
9
|
+
reasoning: true,
|
10
|
+
},
|
11
|
+
contextWindowTokens: 131_072,
|
12
|
+
description:
|
13
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
14
|
+
displayName: 'Qwen3 235B A22B',
|
15
|
+
enabled: true,
|
16
|
+
id: 'qwen3-235b-a22b',
|
17
|
+
maxOutput: 8192,
|
18
|
+
organization: 'Qwen',
|
19
|
+
pricing: { // Thinking mode pricing
|
20
|
+
currency: 'CNY',
|
21
|
+
input: 40,
|
22
|
+
output: 12,
|
23
|
+
},
|
24
|
+
releasedAt: '2025-04-28',
|
25
|
+
settings: {
|
26
|
+
extendParams: ['enableReasoning'],
|
27
|
+
},
|
28
|
+
type: 'chat',
|
29
|
+
},
|
30
|
+
{
|
31
|
+
abilities: {
|
32
|
+
functionCall: true,
|
33
|
+
reasoning: true,
|
34
|
+
},
|
35
|
+
contextWindowTokens: 131_072,
|
36
|
+
description:
|
37
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
38
|
+
displayName: 'Qwen3 32B',
|
39
|
+
enabled: true,
|
40
|
+
id: 'qwen3-32b',
|
41
|
+
maxOutput: 8192,
|
42
|
+
organization: 'Qwen',
|
43
|
+
pricing: { // Thinking mode pricing
|
44
|
+
currency: 'CNY',
|
45
|
+
input: 20,
|
46
|
+
output: 8,
|
47
|
+
},
|
48
|
+
releasedAt: '2025-04-28',
|
49
|
+
settings: {
|
50
|
+
extendParams: ['enableReasoning'],
|
51
|
+
},
|
52
|
+
type: 'chat',
|
53
|
+
},
|
54
|
+
{
|
55
|
+
abilities: {
|
56
|
+
functionCall: true,
|
57
|
+
reasoning: true,
|
58
|
+
},
|
59
|
+
contextWindowTokens: 131_072,
|
60
|
+
description:
|
61
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
62
|
+
displayName: 'Qwen3 30B A3B',
|
63
|
+
enabled: true,
|
64
|
+
id: 'qwen3-30b-a3b',
|
65
|
+
maxOutput: 8192,
|
66
|
+
organization: 'Qwen',
|
67
|
+
pricing: { // Thinking mode pricing
|
68
|
+
currency: 'CNY',
|
69
|
+
input: 15,
|
70
|
+
output: 6,
|
71
|
+
},
|
72
|
+
releasedAt: '2025-04-28',
|
73
|
+
settings: {
|
74
|
+
extendParams: ['enableReasoning'],
|
75
|
+
},
|
76
|
+
type: 'chat',
|
77
|
+
},
|
78
|
+
{
|
79
|
+
abilities: {
|
80
|
+
functionCall: true,
|
81
|
+
reasoning: true,
|
82
|
+
},
|
83
|
+
contextWindowTokens: 131_072,
|
84
|
+
description:
|
85
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
86
|
+
displayName: 'Qwen3 14B',
|
87
|
+
id: 'qwen3-14b',
|
88
|
+
maxOutput: 8192,
|
89
|
+
organization: 'Qwen',
|
90
|
+
pricing: { // Thinking mode pricing
|
91
|
+
currency: 'CNY',
|
92
|
+
input: 10,
|
93
|
+
output: 4,
|
94
|
+
},
|
95
|
+
releasedAt: '2025-04-28',
|
96
|
+
settings: {
|
97
|
+
extendParams: ['enableReasoning'],
|
98
|
+
},
|
99
|
+
type: 'chat',
|
100
|
+
},
|
101
|
+
{
|
102
|
+
abilities: {
|
103
|
+
functionCall: true,
|
104
|
+
reasoning: true,
|
105
|
+
},
|
106
|
+
contextWindowTokens: 131_072,
|
107
|
+
description:
|
108
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
109
|
+
displayName: 'Qwen3 8B',
|
110
|
+
id: 'qwen3-8b',
|
111
|
+
maxOutput: 8192,
|
112
|
+
organization: 'Qwen',
|
113
|
+
pricing: { // Thinking mode pricing
|
114
|
+
currency: 'CNY',
|
115
|
+
input: 5,
|
116
|
+
output: 2,
|
117
|
+
},
|
118
|
+
releasedAt: '2025-04-28',
|
119
|
+
settings: {
|
120
|
+
extendParams: ['enableReasoning'],
|
121
|
+
},
|
122
|
+
type: 'chat',
|
123
|
+
},
|
124
|
+
{
|
125
|
+
abilities: {
|
126
|
+
functionCall: true,
|
8
127
|
reasoning: true,
|
9
128
|
},
|
10
129
|
contextWindowTokens: 131_072,
|
130
|
+
description:
|
131
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
132
|
+
displayName: 'Qwen3 4B',
|
133
|
+
id: 'qwen3-4b',
|
134
|
+
maxOutput: 8192,
|
135
|
+
organization: 'Qwen',
|
136
|
+
pricing: { // Thinking mode pricing
|
137
|
+
currency: 'CNY',
|
138
|
+
input: 3,
|
139
|
+
output: 1.2,
|
140
|
+
},
|
141
|
+
releasedAt: '2025-04-28',
|
142
|
+
settings: {
|
143
|
+
extendParams: ['enableReasoning'],
|
144
|
+
},
|
145
|
+
type: 'chat',
|
146
|
+
},
|
147
|
+
{
|
148
|
+
abilities: {
|
149
|
+
functionCall: true,
|
150
|
+
reasoning: true,
|
151
|
+
},
|
152
|
+
contextWindowTokens: 32_768,
|
153
|
+
description:
|
154
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
155
|
+
displayName: 'Qwen3 1.7B',
|
156
|
+
id: 'qwen3-1.7b',
|
157
|
+
maxOutput: 8192,
|
158
|
+
organization: 'Qwen',
|
159
|
+
pricing: { // Thinking mode pricing
|
160
|
+
currency: 'CNY',
|
161
|
+
input: 3,
|
162
|
+
output: 1.2,
|
163
|
+
},
|
164
|
+
releasedAt: '2025-04-28',
|
165
|
+
settings: {
|
166
|
+
extendParams: ['enableReasoning'],
|
167
|
+
},
|
168
|
+
type: 'chat',
|
169
|
+
},
|
170
|
+
{
|
171
|
+
abilities: {
|
172
|
+
functionCall: true,
|
173
|
+
reasoning: true,
|
174
|
+
},
|
175
|
+
contextWindowTokens: 32_768,
|
176
|
+
description:
|
177
|
+
'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
|
178
|
+
displayName: 'Qwen3 0.6B',
|
179
|
+
id: 'qwen3-0.6b',
|
180
|
+
maxOutput: 8192,
|
181
|
+
organization: 'Qwen',
|
182
|
+
pricing: { // Thinking mode pricing
|
183
|
+
currency: 'CNY',
|
184
|
+
input: 3,
|
185
|
+
output: 1.2,
|
186
|
+
},
|
187
|
+
releasedAt: '2025-04-28',
|
188
|
+
settings: {
|
189
|
+
extendParams: ['enableReasoning'],
|
190
|
+
},
|
191
|
+
type: 'chat',
|
192
|
+
},
|
193
|
+
{
|
194
|
+
abilities: {
|
195
|
+
functionCall: true,
|
196
|
+
reasoning: true,
|
197
|
+
search: true,
|
198
|
+
},
|
199
|
+
contextWindowTokens: 131_072,
|
11
200
|
description:
|
12
201
|
'基于 Qwen2.5 模型训练的 QwQ 推理模型,通过强化学习大幅度提升了模型推理能力。模型数学代码等核心指标(AIME 24/25、LiveCodeBench)以及部分通用指标(IFEval、LiveBench等)达到DeepSeek-R1 满血版水平。',
|
13
202
|
displayName: 'QwQ Plus',
|
@@ -29,6 +218,7 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
29
218
|
{
|
30
219
|
abilities: {
|
31
220
|
functionCall: true,
|
221
|
+
reasoning: true,
|
32
222
|
search: true,
|
33
223
|
},
|
34
224
|
contextWindowTokens: 1_000_000,
|
@@ -41,9 +231,11 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
41
231
|
pricing: {
|
42
232
|
currency: 'CNY',
|
43
233
|
input: 0.3,
|
44
|
-
output:
|
234
|
+
output: 6, // Thinking mode pricing
|
45
235
|
},
|
236
|
+
releasedAt: '2025-04-28',
|
46
237
|
settings: {
|
238
|
+
extendParams: ['enableReasoning'],
|
47
239
|
searchImpl: 'params',
|
48
240
|
},
|
49
241
|
type: 'chat',
|
@@ -51,6 +243,7 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
51
243
|
{
|
52
244
|
abilities: {
|
53
245
|
functionCall: true,
|
246
|
+
reasoning: true,
|
54
247
|
search: true,
|
55
248
|
},
|
56
249
|
contextWindowTokens: 131_072,
|
@@ -63,9 +256,11 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
63
256
|
pricing: {
|
64
257
|
currency: 'CNY',
|
65
258
|
input: 0.8,
|
66
|
-
output:
|
259
|
+
output: 16, // Thinking mode pricing
|
67
260
|
},
|
261
|
+
releasedAt: '2025-04-28',
|
68
262
|
settings: {
|
263
|
+
extendParams: ['enableReasoning'],
|
69
264
|
searchImpl: 'params',
|
70
265
|
},
|
71
266
|
type: 'chat',
|
@@ -101,7 +296,6 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
101
296
|
description:
|
102
297
|
'通义千问超大规模语言模型,支持长文本上下文,以及基于长文档、多文档等多个场景的对话功能。',
|
103
298
|
displayName: 'Qwen Long',
|
104
|
-
enabled: true,
|
105
299
|
id: 'qwen-long',
|
106
300
|
maxOutput: 6000,
|
107
301
|
organization: 'Qwen',
|
@@ -263,6 +457,7 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
263
457
|
{
|
264
458
|
abilities: {
|
265
459
|
reasoning: true,
|
460
|
+
search: true,
|
266
461
|
},
|
267
462
|
contextWindowTokens: 131_072,
|
268
463
|
description:
|
@@ -277,6 +472,9 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
277
472
|
output: 6,
|
278
473
|
},
|
279
474
|
releasedAt: '2025-03-06',
|
475
|
+
settings: {
|
476
|
+
searchImpl: 'params',
|
477
|
+
},
|
280
478
|
type: 'chat',
|
281
479
|
},
|
282
480
|
{
|
@@ -8,7 +8,7 @@ import {
|
|
8
8
|
} from '@icons-pack/react-simple-icons';
|
9
9
|
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
10
10
|
import { Alert, AutoComplete, FormItem, Input, TextArea } from '@lobehub/ui';
|
11
|
-
import { Button, Form, FormInstance } from 'antd';
|
11
|
+
import { Button, Divider, Form, FormInstance } from 'antd';
|
12
12
|
import { FC, useState } from 'react';
|
13
13
|
import { useTranslation } from 'react-i18next';
|
14
14
|
import { Flexbox } from 'react-layout-kit';
|
@@ -52,6 +52,7 @@ const STDIO_COMMAND = ['customParams', 'mcp', 'command'];
|
|
52
52
|
const STDIO_ARGS = ['customParams', 'mcp', 'args'];
|
53
53
|
const STDIO_ENV = ['customParams', 'mcp', 'env'];
|
54
54
|
const MCP_TYPE = ['customParams', 'mcp', 'type'];
|
55
|
+
const DESC_TYPE = ['customParams', 'description'];
|
55
56
|
|
56
57
|
const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
57
58
|
const { t } = useTranslation('plugin');
|
@@ -150,16 +151,24 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
150
151
|
const values = form.getFieldsValue();
|
151
152
|
const id = values.identifier;
|
152
153
|
const mcp = values.customParams?.mcp;
|
154
|
+
const description = values.customParams?.description;
|
155
|
+
const avatar = values.customParams?.avatar;
|
153
156
|
|
154
157
|
let data: LobeChatPluginManifest;
|
155
158
|
|
156
159
|
if (mcp.type === 'http') {
|
157
160
|
if (!mcp.url) throw new Error(t('dev.mcp.url.required'));
|
158
|
-
data = await mcpService.getStreamableMcpServerManifest(id, mcp.url
|
161
|
+
data = await mcpService.getStreamableMcpServerManifest(id, mcp.url, {
|
162
|
+
avatar,
|
163
|
+
description,
|
164
|
+
});
|
159
165
|
} else if (mcp.type === 'stdio') {
|
160
166
|
if (!mcp.command) throw new Error(t('dev.mcp.command.required'));
|
161
167
|
if (!mcp.args) throw new Error(t('dev.mcp.args.required'));
|
162
|
-
data = await mcpService.getStdioMcpServerManifest(id, mcp.command, mcp.args
|
168
|
+
data = await mcpService.getStdioMcpServerManifest(id, mcp.command, mcp.args, {
|
169
|
+
avatar,
|
170
|
+
description,
|
171
|
+
});
|
163
172
|
} else {
|
164
173
|
throw new Error('Invalid MCP type'); // Internal error
|
165
174
|
}
|
@@ -253,7 +262,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
253
262
|
>
|
254
263
|
<MCPTypeSelect />
|
255
264
|
</Form.Item>
|
256
|
-
|
257
265
|
<FormItem
|
258
266
|
desc={t('dev.mcp.identifier.desc')}
|
259
267
|
label={t('dev.mcp.identifier.label')}
|
@@ -281,7 +289,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
281
289
|
>
|
282
290
|
<Input placeholder={t('dev.mcp.identifier.placeholder')} />
|
283
291
|
</FormItem>
|
284
|
-
|
285
292
|
{mcpType === 'http' && (
|
286
293
|
<FormItem
|
287
294
|
desc={t('dev.mcp.url.desc')}
|
@@ -296,7 +303,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
296
303
|
<Input placeholder="https://mcp.higress.ai/mcp-github/xxxxx" />
|
297
304
|
</FormItem>
|
298
305
|
)}
|
299
|
-
|
300
306
|
{mcpType === 'stdio' && (
|
301
307
|
<>
|
302
308
|
<FormItem
|
@@ -349,7 +355,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
349
355
|
</Button>
|
350
356
|
</Flexbox>
|
351
357
|
</FormItem>
|
352
|
-
|
353
358
|
{connectionError && (
|
354
359
|
<Alert
|
355
360
|
closable
|
@@ -361,6 +366,22 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
361
366
|
/>
|
362
367
|
)}
|
363
368
|
<FormItem name={'manifest'} noStyle />
|
369
|
+
<Divider />
|
370
|
+
<FormItem
|
371
|
+
desc={t('dev.mcp.desc.desc')}
|
372
|
+
label={t('dev.mcp.desc.label')}
|
373
|
+
name={DESC_TYPE}
|
374
|
+
tag={'description'}
|
375
|
+
>
|
376
|
+
<Input placeholder={t('dev.mcp.desc.placeholder')} />
|
377
|
+
</FormItem>
|
378
|
+
<FormItem
|
379
|
+
label={t('dev.mcp.avatar.label')}
|
380
|
+
name={['customParams', 'avatar']}
|
381
|
+
tag={'avatar'}
|
382
|
+
>
|
383
|
+
<Input placeholder={'https://plugin-avatar.com'} />
|
384
|
+
</FormItem>
|
364
385
|
</Flexbox>
|
365
386
|
</Form>
|
366
387
|
</>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
3
|
import { Block, Icon, Tag } from '@lobehub/ui';
|
4
|
-
import { Input, Space
|
4
|
+
import { Input, Space } from 'antd';
|
5
5
|
import { createStyles } from 'antd-style';
|
6
6
|
import { ChevronDown, ChevronRight } from 'lucide-react';
|
7
7
|
import { memo, useState } from 'react';
|
@@ -9,13 +9,21 @@ import { useTranslation } from 'react-i18next';
|
|
9
9
|
import { Flexbox } from 'react-layout-kit';
|
10
10
|
|
11
11
|
const useStyles = createStyles(({ css, token }) => ({
|
12
|
+
apiDesc: css`
|
13
|
+
overflow: hidden;
|
14
|
+
display: -webkit-box;
|
15
|
+
-webkit-box-orient: vertical;
|
16
|
+
-webkit-line-clamp: 2;
|
17
|
+
|
18
|
+
font-size: 12px;
|
19
|
+
color: ${token.colorTextTertiary};
|
20
|
+
`,
|
12
21
|
apiHeader: css`
|
13
22
|
cursor: pointer;
|
14
23
|
display: flex;
|
15
24
|
align-items: center;
|
16
25
|
justify-content: space-between;
|
17
26
|
`,
|
18
|
-
|
19
27
|
apiTitle: css`
|
20
28
|
font-family: ${token.fontFamilyCode};
|
21
29
|
`,
|
@@ -99,9 +107,9 @@ const ApiItem = memo<ApiItemProps>(({ api }) => {
|
|
99
107
|
return (
|
100
108
|
<Block gap={8} padding={16}>
|
101
109
|
<div className={styles.apiHeader} onClick={() => setExpanded(!expanded)}>
|
102
|
-
<Flexbox gap={
|
110
|
+
<Flexbox gap={8}>
|
103
111
|
<div className={styles.apiTitle}>{api.name}</div>
|
104
|
-
<
|
112
|
+
<div className={styles.apiDesc}>{api.description}</div>
|
105
113
|
</Flexbox>
|
106
114
|
|
107
115
|
<Icon icon={expanded ? ChevronDown : ChevronRight} />
|
@@ -11,6 +11,7 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
11
11
|
container: css`
|
12
12
|
display: flex;
|
13
13
|
flex-direction: column;
|
14
|
+
gap: 12px;
|
14
15
|
align-items: center;
|
15
16
|
justify-content: center;
|
16
17
|
|
@@ -19,7 +20,6 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
19
20
|
padding: ${token.paddingLG}px;
|
20
21
|
`,
|
21
22
|
description: css`
|
22
|
-
max-width: 320px;
|
23
23
|
color: ${token.colorTextSecondary};
|
24
24
|
text-align: center;
|
25
25
|
`,
|
@@ -30,7 +30,6 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
30
30
|
|
31
31
|
width: 64px;
|
32
32
|
height: 64px;
|
33
|
-
margin-block-end: ${token.marginMD}px;
|
34
33
|
border-radius: 50%;
|
35
34
|
|
36
35
|
background-color: ${token.colorPrimaryBg};
|
@@ -68,7 +67,7 @@ export default function PluginEmptyState() {
|
|
68
67
|
{t('dev.preview.empty.title')}
|
69
68
|
</Title>
|
70
69
|
<Paragraph className={styles.description}>{t('dev.preview.empty.desc')}</Paragraph>
|
71
|
-
<Space align="center" direction="vertical"
|
70
|
+
<Space align="center" direction="vertical">
|
72
71
|
<div className={styles.line} style={{ width: 128 }} />
|
73
72
|
<div className={styles.line} style={{ width: 96 }} />
|
74
73
|
<div className={styles.line} style={{ width: 48 }} />
|
@@ -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;
|
package/src/libs/mcp/types.ts
CHANGED
@@ -66,6 +66,7 @@ export default {
|
|
66
66
|
429: '很抱歉,您的请求太多,服务器有点累了,请稍后再试',
|
67
67
|
431: '很抱歉,您的请求头字段太大,服务器无法处理',
|
68
68
|
451: '很抱歉,由于法律原因,服务器拒绝提供此资源',
|
69
|
+
499: '很抱歉,您的请求在服务器处理中被意外中断,可能是因为您主动取消了操作或网络连接不稳定。请检查网络状况后重试。',
|
69
70
|
500: '很抱歉,服务器似乎遇到了一些困难,暂时无法完成您的请求,请稍后再试',
|
70
71
|
501: '很抱歉,服务器还不知道如何处理这个请求,请确认您的操作是否正确',
|
71
72
|
502: '很抱歉,服务器似乎迷失了方向,暂时无法提供服务,请稍后再试',
|
@@ -76,6 +77,8 @@ export default {
|
|
76
77
|
507: '很抱歉,服务器存储空间不足,无法处理您的请求,请稍后再试',
|
77
78
|
509: '很抱歉,服务器的带宽已用尽,请稍后再试',
|
78
79
|
510: '很抱歉,服务器不支持请求的扩展功能,请联系管理员',
|
80
|
+
520: '很抱歉,服务器遇到了一个意外的问题,导致无法完成您的请求。请稍后再试,我们正努力解决这个问题。',
|
81
|
+
522: '很抱歉,服务器连接超时,未能及时响应您的请求。可能是网络不稳定或服务器暂时无法访问。请稍后再试,我们正在努力恢复服务。',
|
79
82
|
524: '很抱歉,服务器在等回复时超时了,可能是因为响应太慢,请稍后再试',
|
80
83
|
|
81
84
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
@@ -54,12 +54,20 @@ export default {
|
|
54
54
|
placeholder: '例如:mcp-hello-world',
|
55
55
|
required: '请输入启动参数',
|
56
56
|
},
|
57
|
+
avatar: {
|
58
|
+
label: '插件图标',
|
59
|
+
},
|
57
60
|
command: {
|
58
61
|
desc: '用于启动 MCP STDIO Server 的可执行文件或脚本',
|
59
62
|
label: '命令',
|
60
63
|
placeholder: '例如:npx / uv / docker 等',
|
61
64
|
required: '请输入启动命令',
|
62
65
|
},
|
66
|
+
desc: {
|
67
|
+
desc: '添加插件的描述说明',
|
68
|
+
label: '插件描述',
|
69
|
+
placeholder: '补充该插件的使用说明和场景等信息',
|
70
|
+
},
|
63
71
|
endpoint: {
|
64
72
|
desc: '输入你的 MCP Streamable HTTP Server 的地址',
|
65
73
|
label: 'MCP Endpoint URL',
|
@@ -8,6 +8,12 @@ import { mcpService } from '@/server/services/mcp';
|
|
8
8
|
const stdioParamsSchema = z.object({
|
9
9
|
args: z.array(z.string()).optional().default([]),
|
10
10
|
command: z.string().min(1),
|
11
|
+
metadata: z
|
12
|
+
.object({
|
13
|
+
avatar: z.string().optional(),
|
14
|
+
description: z.string().optional(),
|
15
|
+
})
|
16
|
+
.optional(),
|
11
17
|
name: z.string().min(1),
|
12
18
|
type: z.literal('stdio').default('stdio'),
|
13
19
|
});
|
@@ -16,7 +22,10 @@ const mcpProcedure = isServerMode ? authedProcedure : passwordProcedure;
|
|
16
22
|
|
17
23
|
export const mcpRouter = router({
|
18
24
|
getStdioMcpServerManifest: mcpProcedure.input(stdioParamsSchema).query(async ({ input }) => {
|
19
|
-
return await mcpService.getStdioMcpServerManifest(
|
25
|
+
return await mcpService.getStdioMcpServerManifest(
|
26
|
+
{ args: input.args, command: input.command, name: input.name },
|
27
|
+
input.metadata,
|
28
|
+
);
|
20
29
|
}),
|
21
30
|
|
22
31
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
@@ -39,11 +39,21 @@ export const mcpRouter = router({
|
|
39
39
|
.input(
|
40
40
|
z.object({
|
41
41
|
identifier: z.string(),
|
42
|
+
metadata: z
|
43
|
+
.object({
|
44
|
+
avatar: z.string().optional(),
|
45
|
+
description: z.string().optional(),
|
46
|
+
})
|
47
|
+
.optional(),
|
42
48
|
url: z.string().url(),
|
43
49
|
}),
|
44
50
|
)
|
45
51
|
.query(async ({ input }) => {
|
46
|
-
return await mcpService.getStreamableMcpServerManifest(
|
52
|
+
return await mcpService.getStreamableMcpServerManifest(
|
53
|
+
input.identifier,
|
54
|
+
input.url,
|
55
|
+
input.metadata,
|
56
|
+
);
|
47
57
|
}),
|
48
58
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
49
59
|
// --- MCP Interaction ---
|
@@ -3,7 +3,8 @@ import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { TRPCError } from '@trpc/server';
|
4
4
|
import debug from 'debug';
|
5
5
|
|
6
|
-
import { MCPClient, MCPClientParams } from '@/libs/mcp';
|
6
|
+
import { MCPClient, MCPClientParams, StdioMCPParams } from '@/libs/mcp';
|
7
|
+
import { CustomPluginMetadata } from '@/types/tool/plugin';
|
7
8
|
import { safeParseJSON } from '@/utils/safeParseJSON';
|
8
9
|
|
9
10
|
const log = debug('lobe-mcp:service');
|
@@ -58,9 +59,20 @@ class MCPService {
|
|
58
59
|
const result = await client.callTool(toolName, args); // Pass args directly
|
59
60
|
log(`Tool "${toolName}" called successfully for params: %O, result: %O`, params, result);
|
60
61
|
const { content, isError } = result;
|
61
|
-
if (!isError) return content;
|
62
62
|
|
63
|
-
return result;
|
63
|
+
if (isError) return result;
|
64
|
+
|
65
|
+
const data = content as { text: string; type: 'text' }[];
|
66
|
+
|
67
|
+
const text = data?.[0]?.text;
|
68
|
+
|
69
|
+
if (!text) return data;
|
70
|
+
|
71
|
+
// try to get json object, which will be stringify in the client
|
72
|
+
const json = safeParseJSON(text);
|
73
|
+
if (json) return json;
|
74
|
+
|
75
|
+
return text;
|
64
76
|
} catch (error) {
|
65
77
|
if (error instanceof McpError) {
|
66
78
|
const mcpError = error as McpError;
|
@@ -143,6 +155,7 @@ class MCPService {
|
|
143
155
|
async getStreamableMcpServerManifest(
|
144
156
|
identifier: string,
|
145
157
|
url: string,
|
158
|
+
metadata?: CustomPluginMetadata,
|
146
159
|
): Promise<LobeChatPluginManifest> {
|
147
160
|
const tools = await this.listTools({ name: identifier, type: 'http', url }); // Get client using params
|
148
161
|
|
@@ -150,27 +163,37 @@ class MCPService {
|
|
150
163
|
api: tools,
|
151
164
|
identifier,
|
152
165
|
meta: {
|
153
|
-
avatar: 'MCP_AVATAR',
|
154
|
-
description:
|
166
|
+
avatar: metadata?.avatar || 'MCP_AVATAR',
|
167
|
+
description:
|
168
|
+
metadata?.description ||
|
169
|
+
`${identifier} MCP server has ${tools.length} tools, like "${tools[0]?.name}"`,
|
155
170
|
title: identifier,
|
156
171
|
},
|
157
172
|
// TODO: temporary
|
158
173
|
type: 'mcp' as any,
|
159
174
|
};
|
160
175
|
}
|
176
|
+
|
161
177
|
async getStdioMcpServerManifest(
|
162
|
-
|
163
|
-
|
164
|
-
args: string[],
|
178
|
+
params: Omit<StdioMCPParams, 'type'>,
|
179
|
+
metadata?: CustomPluginMetadata,
|
165
180
|
): Promise<LobeChatPluginManifest> {
|
166
|
-
const tools = await this.listTools({
|
167
|
-
|
181
|
+
const tools = await this.listTools({
|
182
|
+
args: params.args,
|
183
|
+
command: params.command,
|
184
|
+
name: params.name,
|
185
|
+
type: 'stdio',
|
186
|
+
});
|
187
|
+
|
188
|
+
const identifier = params.name;
|
168
189
|
return {
|
169
190
|
api: tools,
|
170
191
|
identifier,
|
171
192
|
meta: {
|
172
|
-
avatar: 'MCP_AVATAR',
|
173
|
-
description:
|
193
|
+
avatar: metadata?.avatar || 'MCP_AVATAR',
|
194
|
+
description:
|
195
|
+
metadata?.description ||
|
196
|
+
`${identifier} MCP server has ${tools.length} tools, like "${tools[0]?.name}"`,
|
174
197
|
title: identifier,
|
175
198
|
},
|
176
199
|
// TODO: temporary
|