forked from xiaohei/taiji-pda-v0
feat: 根据后端接口更新自定义Agent创建功能
- 更新 API Client createCustomAgent 方法,添加 model、agentRole、agentCapabilities 参数 - 更新模板接口解析,支持新格式 frameworkTemplates 和 dataTemplates - 添加数据存储模板选择器(mysql_agent、postgresql_agent 等) - 添加动态环境变量配置,根据模板的 env_info.required/optional 动态生成表单 - 添加 A2A 框架专用配置(Agent角色、Agent能力) - 更新表单验证和配置摘要
This commit is contained in:
+186
-33
@@ -61,6 +61,13 @@ export default function DataToolsPage() {
|
||||
const [selectedDataTemplate, setSelectedDataTemplate] = useState<string>("")
|
||||
const [dataTemplates, setDataTemplates] = useState<any[]>([])
|
||||
const [frameworkTemplates, setFrameworkTemplates] = useState<any[]>([])
|
||||
// 新增:API返回的数据存储模板(含环境变量信息)
|
||||
const [apiDataTemplates, setApiDataTemplates] = useState<any[]>([])
|
||||
const [selectedApiDataTemplate, setSelectedApiDataTemplate] = useState<string>("")
|
||||
const [envConfig, setEnvConfig] = useState<Record<string, string>>({})
|
||||
// A2A 框架专用字段
|
||||
const [agentRole, setAgentRole] = useState<string>("")
|
||||
const [agentCapabilities, setAgentCapabilities] = useState<string>("")
|
||||
const [availableModels, setAvailableModels] = useState<any[]>([])
|
||||
const [toolName, setToolName] = useState<string>("")
|
||||
const [deploying, setDeploying] = useState(false)
|
||||
@@ -82,12 +89,34 @@ export default function DataToolsPage() {
|
||||
const loadFrameworkTemplates = async () => {
|
||||
try {
|
||||
const result = await TaijiAPIClient.getCustomAgentTemplates()
|
||||
if (result?.success && result.data?.templates) {
|
||||
// 文档返回的是字符串数组:["A2A", "langchain", "MCP"]
|
||||
// 转换为对象数组以便显示
|
||||
if (result?.success && result.data) {
|
||||
// 新格式:{ frameworkTemplates: [...], dataTemplates: [...] }
|
||||
if (result.data.frameworkTemplates) {
|
||||
// 框架模板:字符串数组 ["A2A", "langchain", "MCP"]
|
||||
const templates = result.data.frameworkTemplates.map((template: string) => {
|
||||
const displayNames: Record<string, string> = {
|
||||
'A2A': 'A2A框架',
|
||||
'langchain': 'LangChain框架',
|
||||
'MCP': 'MCP框架'
|
||||
}
|
||||
return {
|
||||
name: template,
|
||||
displayName: displayNames[template] || template,
|
||||
description: ''
|
||||
}
|
||||
})
|
||||
setFrameworkTemplates(templates)
|
||||
}
|
||||
|
||||
// 数据存储模板:对象数组,包含 env_info
|
||||
if (result.data.dataTemplates) {
|
||||
setApiDataTemplates(result.data.dataTemplates)
|
||||
}
|
||||
|
||||
// 兼容旧格式:{ templates: [...] }
|
||||
if (result.data.templates && !result.data.frameworkTemplates) {
|
||||
const templates = result.data.templates.map((template: string | any) => {
|
||||
if (typeof template === 'string') {
|
||||
// 如果是字符串,转换为对象
|
||||
const displayNames: Record<string, string> = {
|
||||
'A2A': 'A2A框架',
|
||||
'langchain': 'LangChain框架',
|
||||
@@ -99,11 +128,11 @@ export default function DataToolsPage() {
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
// 如果已经是对象,直接返回(兼容旧格式)
|
||||
return template
|
||||
})
|
||||
setFrameworkTemplates(templates)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load framework templates:", error)
|
||||
}
|
||||
@@ -350,8 +379,9 @@ export default function DataToolsPage() {
|
||||
|
||||
// 部署Agent(创建自定义Agent)
|
||||
const handleDeployAgent = async () => {
|
||||
if (!selectedFramework || !toolName) {
|
||||
alert(t("请填写所有必填字段", "Please fill in all required fields"))
|
||||
// 校验必填字段
|
||||
if (!selectedApiDataTemplate || !toolName) {
|
||||
alert(t("请选择数据存储模板并填写Agent名称", "Please select a data template and enter agent name"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -374,22 +404,40 @@ export default function DataToolsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// 使用创建自定义Agent接口 POST /api/user/custom-agents
|
||||
// 根据文档:template是必填的模板名称,frameworkTemplate是框架类型(可选)
|
||||
const frameworkName = selectedFramework.toUpperCase() // MCP/A2A/langchain
|
||||
const templateName = `${frameworkName.toLowerCase()}-agent` // 生成模板名称,如 "mcp-agent"
|
||||
// 框架类型(默认MCP)
|
||||
const frameworkName = selectedFramework || "MCP"
|
||||
|
||||
const result = await TaijiAPIClient.createCustomAgent({
|
||||
// 构建请求参数
|
||||
const requestData: any = {
|
||||
name: toolName,
|
||||
template: templateName, // 必填:模板名称
|
||||
template: selectedApiDataTemplate, // 必填:数据存储模板(如 mysql_agent)
|
||||
frameworkTemplate: frameworkName, // 可选:框架类型(A2A/langchain/MCP)
|
||||
description: toolName, // 可选:Agent描述
|
||||
cpuRequest: cpuRequest, // 必填:CPU请求量(如"500m")
|
||||
cpuRequest: cpuRequest, // 必填:CPU请求量
|
||||
cpuLimit: cpuLimit, // 可选:CPU限制量
|
||||
memoryRequest: memoryRequest, // 必填:内存请求量(如"1Gi")
|
||||
memoryRequest: memoryRequest, // 必填:内存请求量
|
||||
memoryLimit: memoryLimit, // 可选:内存限制量
|
||||
tools: selectedTools.length > 0 ? selectedTools : undefined, // 可选:工具ID列表
|
||||
})
|
||||
model: podConfig.model, // 可选:模型名称
|
||||
}
|
||||
|
||||
// 添加环境变量配置(如数据库连接信息)
|
||||
if (Object.keys(envConfig).length > 0) {
|
||||
requestData.envConfig = envConfig
|
||||
}
|
||||
|
||||
// A2A 框架专用字段
|
||||
if (frameworkName === "A2A") {
|
||||
if (agentRole) {
|
||||
requestData.agentRole = agentRole
|
||||
}
|
||||
if (agentCapabilities) {
|
||||
// 将逗号分隔的字符串转换为数组
|
||||
requestData.agentCapabilities = agentCapabilities.split(",").map(s => s.trim()).filter(Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
const result = await TaijiAPIClient.createCustomAgent(requestData)
|
||||
|
||||
if (result?.success) {
|
||||
alert(t("自定义Agent创建成功,正在部署", "Custom Agent created successfully, deploying"))
|
||||
@@ -398,6 +446,10 @@ export default function DataToolsPage() {
|
||||
setSelectedFramework("")
|
||||
setSelectedServiceGateway("")
|
||||
setSelectedDataTemplate("")
|
||||
setSelectedApiDataTemplate("")
|
||||
setEnvConfig({})
|
||||
setAgentRole("")
|
||||
setAgentCapabilities("")
|
||||
setToolName("")
|
||||
setPodConfig({
|
||||
agentCount: 1,
|
||||
@@ -790,10 +842,92 @@ export default function DataToolsPage() {
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("Agent框架模板", "Agent Framework Template")}</Label>
|
||||
<Label>{t("Agent名称", "Agent Name")} <span className="text-destructive">*</span></Label>
|
||||
<Input
|
||||
placeholder={t("输入Agent名称(小写字母、数字、连字符)", "Enter agent name (lowercase, numbers, hyphens)")}
|
||||
value={toolName}
|
||||
onChange={(e) => setToolName(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '-'))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 数据存储模板选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label>{t("数据存储模板", "Data Storage Template")} <span className="text-destructive">*</span></Label>
|
||||
<Select
|
||||
value={selectedApiDataTemplate}
|
||||
onValueChange={(value) => {
|
||||
setSelectedApiDataTemplate(value)
|
||||
// 重置环境变量配置
|
||||
setEnvConfig({})
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("选择数据存储模板", "Select data storage template")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{apiDataTemplates.length === 0 ? (
|
||||
<SelectItem value="" disabled>
|
||||
{t("暂无可用模板", "No templates available")}
|
||||
</SelectItem>
|
||||
) : (
|
||||
apiDataTemplates.map((template) => (
|
||||
<SelectItem key={template.template} value={template.template}>
|
||||
<div className="flex flex-col">
|
||||
<span>{template.template}</span>
|
||||
{template.description && (
|
||||
<span className="text-xs text-muted-foreground">{template.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("选择Agent使用的数据存储类型", "Select data storage type for the agent")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 动态环境变量配置(根据选择的模板) */}
|
||||
{selectedApiDataTemplate && (() => {
|
||||
const template = apiDataTemplates.find(t => t.template === selectedApiDataTemplate)
|
||||
if (!template?.env_info) return null
|
||||
const required = template.env_info.required || {}
|
||||
const optional = template.env_info.optional || {}
|
||||
return (
|
||||
<div className="border rounded-lg p-4 space-y-3 bg-muted/30">
|
||||
<h4 className="font-medium text-sm">{t("数据库连接配置", "Database Connection Config")}</h4>
|
||||
{Object.entries(required).map(([key, desc]) => (
|
||||
<div key={key} className="space-y-1">
|
||||
<Label className="text-sm">{key} <span className="text-destructive">*</span></Label>
|
||||
<Input
|
||||
placeholder={String(desc)}
|
||||
value={envConfig[key] || ""}
|
||||
onChange={(e) => setEnvConfig({ ...envConfig, [key]: e.target.value })}
|
||||
type={key.toLowerCase().includes('password') ? 'password' : 'text'}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{Object.entries(optional).map(([key, desc]) => (
|
||||
<div key={key} className="space-y-1">
|
||||
<Label className="text-sm text-muted-foreground">{key} {t("(可选)", "(Optional)")}</Label>
|
||||
<Input
|
||||
placeholder={String(desc)}
|
||||
value={envConfig[key] || ""}
|
||||
onChange={(e) => setEnvConfig({ ...envConfig, [key]: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* 框架类型选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label>{t("框架类型", "Framework Type")}</Label>
|
||||
<Select value={selectedFramework} onValueChange={setSelectedFramework}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("选择框架模板", "Select framework template")} />
|
||||
<SelectValue placeholder={t("选择框架类型(默认MCP)", "Select framework type (default MCP)")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{frameworkTemplates.length === 0 ? (
|
||||
@@ -805,7 +939,7 @@ export default function DataToolsPage() {
|
||||
<SelectItem key={template.name} value={template.name}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Code2 className="h-4 w-4" />
|
||||
{template.displayName || template.name} - {template.description || ""}
|
||||
{template.displayName || template.name}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))
|
||||
@@ -813,18 +947,35 @@ export default function DataToolsPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("根据选择的框架注册相应的工具", "Register tools according to selected framework")}
|
||||
{t("选择Agent运行框架,默认为MCP", "Select agent runtime framework, defaults to MCP")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>{t("工具名称", "Tool Name")}</Label>
|
||||
{/* A2A 框架专用配置 */}
|
||||
{selectedFramework === "A2A" && (
|
||||
<div className="border rounded-lg p-4 space-y-3 bg-blue-500/5 border-blue-500/20">
|
||||
<h4 className="font-medium text-sm flex items-center gap-2">
|
||||
<Workflow className="h-4 w-4 text-blue-500" />
|
||||
{t("A2A 框架配置", "A2A Framework Config")}
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-sm">{t("Agent角色", "Agent Role")}</Label>
|
||||
<Input
|
||||
placeholder={t("输入工具名称", "Enter tool name")}
|
||||
value={toolName}
|
||||
onChange={(e) => setToolName(e.target.value)}
|
||||
placeholder={t("如:data_analyzer", "e.g., data_analyzer")}
|
||||
value={agentRole}
|
||||
onChange={(e) => setAgentRole(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-sm">{t("Agent能力", "Agent Capabilities")}</Label>
|
||||
<Input
|
||||
placeholder={t("多个能力用逗号分隔,如:sql_query, data_analysis", "Comma separated, e.g., sql_query, data_analysis")}
|
||||
value={agentCapabilities}
|
||||
onChange={(e) => setAgentCapabilities(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border-t pt-4 space-y-4">
|
||||
<h3 className="font-semibold flex items-center gap-2">
|
||||
@@ -953,13 +1104,13 @@ export default function DataToolsPage() {
|
||||
<p className="font-medium mb-2">{t("配置摘要", "Configuration Summary")}</p>
|
||||
<div className="grid grid-cols-2 gap-2 text-muted-foreground">
|
||||
<div className="col-span-2">
|
||||
{t("框架", "Framework")}: {selectedFramework || t("未选择", "Not selected")}
|
||||
{t("Agent名称", "Agent Name")}: {toolName || t("未填写", "Not filled")}
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
{t("网关", "Gateway")}: {selectedServiceGateway || t("未选择", "Not selected")}
|
||||
{t("数据存储模板", "Data Template")}: {selectedApiDataTemplate || t("未选择", "Not selected")}
|
||||
</div>
|
||||
<div>
|
||||
{t("Agent数量", "Agent Count")}: {podConfig.agentCount}
|
||||
<div className="col-span-2">
|
||||
{t("框架", "Framework")}: {selectedFramework || "MCP"}
|
||||
</div>
|
||||
<div>
|
||||
{t("CPU", "CPU")}: {podConfig.cpuCores} {t("核", "cores")}
|
||||
@@ -967,12 +1118,14 @@ export default function DataToolsPage() {
|
||||
<div>
|
||||
{t("内存", "Memory")}: {podConfig.memoryGB}GB
|
||||
</div>
|
||||
<div>
|
||||
{t("最大扩展", "Max Scale")}: {podConfig.maxAgents}
|
||||
</div>
|
||||
<div>
|
||||
{t("模型", "Model")}: {podConfig.model}
|
||||
</div>
|
||||
{selectedFramework === "A2A" && agentRole && (
|
||||
<div className="col-span-2">
|
||||
{t("Agent角色", "Agent Role")}: {agentRole}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -982,7 +1135,7 @@ export default function DataToolsPage() {
|
||||
<Button variant="outline" onClick={() => setShowNewToolDialog(false)} disabled={deploying}>
|
||||
{t("取消", "Cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleDeployAgent} disabled={deploying || !selectedFramework || !toolName}>
|
||||
<Button onClick={handleDeployAgent} disabled={deploying || !selectedApiDataTemplate || !toolName}>
|
||||
{deploying ? t("部署中...", "Deploying...") : t("部署Agent", "Deploy Agent")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
+22
-2
@@ -685,20 +685,40 @@ export class TaijiAPIClient {
|
||||
* 创建租户自定义的Agent,将使用渠道为该租户分配的CPU和内存资源配额
|
||||
*
|
||||
* 注意:后端实际路径为 /api/user/custom-agents(非 /api/user/agents/custom/create)
|
||||
*
|
||||
* @param data.name - Agent名称(小写字母、数字、连字符)
|
||||
* @param data.template - 数据存储模板(如 "mysql_agent"、"postgresql_agent")
|
||||
* @param data.frameworkTemplate - 框架类型("MCP" | "A2A" | "langchain"),默认 "MCP"
|
||||
* @param data.description - Agent描述
|
||||
* @param data.cpuRequest - CPU请求量(如 "500m")
|
||||
* @param data.cpuLimit - CPU限制量
|
||||
* @param data.memoryRequest - 内存请求量(如 "1Gi")
|
||||
* @param data.memoryLimit - 内存限制量
|
||||
* @param data.tools - 工具ID列表(UUID)
|
||||
* @param data.model - 模型名称(如 "gpt-4")
|
||||
* @param data.endpoint - 自定义终结点
|
||||
* @param data.apiKey - API密钥
|
||||
* @param data.envConfig - 环境变量配置(数据库连接等)
|
||||
* @param data.agentRole - A2A框架专用:Agent角色(如 "data_analyzer")
|
||||
* @param data.agentCapabilities - A2A框架专用:Agent能力列表
|
||||
*/
|
||||
static async createCustomAgent(data: {
|
||||
name: string
|
||||
template?: string
|
||||
frameworkTemplate?: string
|
||||
template: string // 必填:数据存储模板
|
||||
frameworkTemplate?: string // 可选:框架类型
|
||||
description?: string
|
||||
cpuRequest: string
|
||||
cpuLimit?: string
|
||||
memoryRequest: string
|
||||
memoryLimit?: string
|
||||
tools?: string[]
|
||||
model?: string // 新增:模型名称
|
||||
endpoint?: string
|
||||
apiKey?: string
|
||||
envConfig?: Record<string, string>
|
||||
// A2A 框架专用(新增)
|
||||
agentRole?: string
|
||||
agentCapabilities?: string[]
|
||||
}) {
|
||||
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents`, {
|
||||
method: "POST",
|
||||
|
||||
Reference in New Issue
Block a user