mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-10 21:10:59 +00:00
feat: semi frontend
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import quart
|
import quart
|
||||||
@@ -48,31 +49,37 @@ class MCPRouterGroup(group.RouterGroup):
|
|||||||
|
|
||||||
elif quart.request.method == 'POST':
|
elif quart.request.method == 'POST':
|
||||||
data = await quart.request.json
|
data = await quart.request.json
|
||||||
|
data = data['source']
|
||||||
|
try:
|
||||||
# 检查服务器名称是否重复
|
# 检查服务器名称是否重复
|
||||||
result = await self.ap.persistence_mgr.execute_async(
|
result = await self.ap.persistence_mgr.execute_async(
|
||||||
sqlalchemy.select(MCPServer).where(MCPServer.name == data['name'])
|
sqlalchemy.select(MCPServer).where(MCPServer.name == data['name'])
|
||||||
)
|
)
|
||||||
if result.first() is not None:
|
if result.first() is not None:
|
||||||
return self.http_status(400, -1, 'Server name already exists')
|
return self.http_status(400, -1, 'Server name already exists')
|
||||||
|
|
||||||
# 创建新服务器配置
|
# 创建新服务器配置
|
||||||
new_server = {
|
new_server = {
|
||||||
'uuid': str(uuid.uuid4()),
|
'uuid': str(uuid.uuid4()),
|
||||||
'name': data['name'],
|
'name': data['name'],
|
||||||
'mode': data['mode'],
|
'mode': 'sse',
|
||||||
'enable': data.get('enable', False),
|
'enable': data.get('enable', False),
|
||||||
'description': data.get('description',''),
|
'description': data.get('description',''),
|
||||||
'extra_args': {
|
'extra_args': {
|
||||||
'url':data.get('url',''),
|
'url':data.get('url',''),
|
||||||
'headers':data.get('headers',{}),
|
'headers':data.get('headers',{}),
|
||||||
'timeout':data.get('timeout',60),
|
'timeout':data.get('timeout',60),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
await self.ap.persistence_mgr.execute_async(
|
await self.ap.persistence_mgr.execute_async(
|
||||||
sqlalchemy.insert(MCPServer).values(new_server)
|
sqlalchemy.insert(MCPServer).values(new_server)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return self.success()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
@self.route('/servers/<server_name>', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN)
|
@self.route('/servers/<server_name>', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN)
|
||||||
async def _(server_name: str) -> str:
|
async def _(server_name: str) -> str:
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import { systemInfo } from '@/app/infra/http/HttpClient';
|
|||||||
import { ApiRespPluginSystemStatus } from '@/app/infra/entities/api';
|
import { ApiRespPluginSystemStatus } from '@/app/infra/entities/api';
|
||||||
import { set } from 'lodash';
|
import { set } from 'lodash';
|
||||||
import { passiveEventSupported } from '@tanstack/react-table';
|
import { passiveEventSupported } from '@tanstack/react-table';
|
||||||
import { config } from 'process';
|
|
||||||
|
|
||||||
enum PluginInstallStatus {
|
enum PluginInstallStatus {
|
||||||
WAIT_INPUT = 'wait_input',
|
WAIT_INPUT = 'wait_input',
|
||||||
@@ -324,36 +324,21 @@ export default function PluginConfigPage() {
|
|||||||
setMcpInstallStatus(PluginInstallStatus.INSTALLING);
|
setMcpInstallStatus(PluginInstallStatus.INSTALLING);
|
||||||
console.log('installing mcp server from sse with config:', config);
|
console.log('installing mcp server from sse with config:', config);
|
||||||
httpClient.installMCPServerFromSSE(config ?? {})
|
httpClient.installMCPServerFromSSE(config ?? {})
|
||||||
.then((resp) => {
|
.then((resp:any) => {
|
||||||
const taskId = resp.task_id;
|
if (resp && resp.status === 'success') {
|
||||||
let alreadySuccess = false;
|
console.log('MCP server installed successfully');
|
||||||
console.log('taskId:', taskId);
|
toast.success(t('mcp.installSuccess'));
|
||||||
// 每秒拉取一次任务状态
|
setMcpSSEURL('');
|
||||||
const interval = setInterval(() => {
|
setMcpName('');
|
||||||
httpClient.getAsyncTask(taskId).then((resp) => {
|
setMcpDescription('');
|
||||||
console.log('task status:', resp);
|
setMcpSSEHeaders('');
|
||||||
if (resp.runtime.done) {
|
setMcpTimeout(60);
|
||||||
clearInterval(interval);
|
setMcpSSEInstallModalOpen(false);
|
||||||
if (resp.runtime.exception) {
|
mcpComponentRef.current?.refreshServerList();
|
||||||
setMcpInstallError(resp.runtime.exception);
|
} else {
|
||||||
setMcpInstallStatus(PluginInstallStatus.ERROR);
|
setMcpInstallError(t('mcp.installFailed'));
|
||||||
} else {
|
setMcpInstallStatus(PluginInstallStatus.ERROR);
|
||||||
// success
|
}
|
||||||
if (!alreadySuccess) {
|
|
||||||
toast.success(t('mcp.installSuccess'));
|
|
||||||
alreadySuccess = true;
|
|
||||||
}
|
|
||||||
setMcpSSEInstallModalOpen(false);
|
|
||||||
setMcpName('');
|
|
||||||
setMcpDescription('');
|
|
||||||
setMcpSSEURL('');
|
|
||||||
setMcpSSEHeaders('');
|
|
||||||
setMcpTimeout(60);
|
|
||||||
mcpComponentRef.current?.refreshServerList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log('error when install mcp server:', err);
|
console.log('error when install mcp server:', err);
|
||||||
@@ -672,19 +657,7 @@ export default function PluginConfigPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4">
|
|
||||||
<div>
|
|
||||||
<label className="text-sm text-muted-foreground block mb-1">
|
|
||||||
{t('mcp.headers')}
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
placeholder={t('mcp.headersExample')}
|
|
||||||
value={mcpSSEHeaders}
|
|
||||||
onChange={(e) => setMcpSSEHeaders(e.target.value)}
|
|
||||||
className="mb-1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
<div>
|
<div>
|
||||||
@@ -746,14 +719,13 @@ export default function PluginConfigPage() {
|
|||||||
toast.error(t('mcp.timeoutRequired'));
|
toast.error(t('mcp.timeoutRequired'));
|
||||||
}
|
}
|
||||||
const configToSend = {
|
const configToSend = {
|
||||||
name: mcpSSEConfig?.name,
|
name: mcpName,
|
||||||
description: mcpSSEConfig?.description,
|
description: mcpDescription,
|
||||||
sse_url: mcpSSEConfig?.sse_url,
|
sse_url: mcpSSEURL,
|
||||||
sse_headers: mcpSSEConfig?.sse_headers,
|
sse_headers: mcpSSEHeaders,
|
||||||
timeout: Number(mcpSSEConfig?.timeout) || 60,
|
timeout: Number(mcpTimeout) || 60,
|
||||||
};
|
};
|
||||||
|
// handleMcpModalConfirm();
|
||||||
handleMcpModalConfirm();
|
|
||||||
// call installer (for now installMcpServer will log config and call backend with url only)
|
// call installer (for now installMcpServer will log config and call backend with url only)
|
||||||
installMcpServerFromSSE(configToSend);
|
installMcpServerFromSSE(configToSend);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ export class BackendClient extends BaseHttpClient {
|
|||||||
public installMCPServerFromSSE(
|
public installMCPServerFromSSE(
|
||||||
source: {},
|
source: {},
|
||||||
): Promise<AsyncTaskCreatedResp> {
|
): Promise<AsyncTaskCreatedResp> {
|
||||||
return this.post('/api/v1/mcp/install/sse', { source });
|
return this.post('/api/v1/mcp/servers', { source });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ System API ============
|
// ============ System API ============
|
||||||
|
|||||||
Reference in New Issue
Block a user