forked from xiaohei/taiji-pda-v0
feat: 添加自定义Agent管理功能
- 在工具注册表下方添加自定义Agent管理卡片 - 支持查看自定义Agent列表(名称、模板、状态、CPU、内存、运行时间) - 支持停止运行中的Agent - 支持重启/启动Agent - 支持删除Agent(带确认提示) - 添加刷新按钮手动更新列表 - 添加API方法:getUserCustomAgents, deleteCustomAgent, stopCustomAgent, restartCustomAgent
This commit is contained in:
+207
-1
@@ -12,7 +12,7 @@ import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { Plus, Search, ExternalLink, Trash2, FileJson, Cloud, Cpu, Code2, Boxes, Workflow, Upload } from "lucide-react"
|
||||
import { Plus, Search, ExternalLink, Trash2, FileJson, Cloud, Cpu, Code2, Boxes, Workflow, Upload, RefreshCw, Square, Play, Bot } from "lucide-react"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import {
|
||||
Dialog,
|
||||
@@ -47,10 +47,16 @@ export default function DataToolsPage() {
|
||||
const [selectedServiceGateway, setSelectedServiceGateway] = useState<string>("")
|
||||
const [selectedDataTemplate, setSelectedDataTemplate] = useState<string>("")
|
||||
const [dataTemplates, setDataTemplates] = useState<any[]>([])
|
||||
|
||||
// 自定义Agent管理状态
|
||||
const [customAgents, setCustomAgents] = useState<any[]>([])
|
||||
const [customAgentsLoading, setCustomAgentsLoading] = useState(true)
|
||||
const [actionLoading, setActionLoading] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
loadTools()
|
||||
loadStats()
|
||||
loadCustomAgents()
|
||||
}, [])
|
||||
|
||||
const loadTools = async () => {
|
||||
@@ -84,6 +90,88 @@ export default function DataToolsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载自定义Agent列表
|
||||
const loadCustomAgents = async () => {
|
||||
try {
|
||||
setCustomAgentsLoading(true)
|
||||
const result = await TaijiAPIClient.getUserCustomAgents()
|
||||
if (result?.success && result.data?.agents) {
|
||||
setCustomAgents(result.data.agents)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load custom agents:", error)
|
||||
} finally {
|
||||
setCustomAgentsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除自定义Agent
|
||||
const handleDeleteCustomAgent = async (agentName: string) => {
|
||||
if (!confirm(t(`确定要删除Agent "${agentName}" 吗?此操作不可恢复。`, `Are you sure you want to delete agent "${agentName}"? This action cannot be undone.`))) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
setActionLoading(agentName)
|
||||
const result = await TaijiAPIClient.deleteCustomAgent(agentName)
|
||||
if (result?.success) {
|
||||
loadCustomAgents()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to delete custom agent:", error)
|
||||
} finally {
|
||||
setActionLoading(null)
|
||||
}
|
||||
}
|
||||
|
||||
// 停止自定义Agent
|
||||
const handleStopCustomAgent = async (agentName: string) => {
|
||||
if (!confirm(t(`确定要停止Agent "${agentName}" 吗?`, `Are you sure you want to stop agent "${agentName}"?`))) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
setActionLoading(agentName)
|
||||
const result = await TaijiAPIClient.stopCustomAgent(agentName)
|
||||
if (result?.success) {
|
||||
loadCustomAgents()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to stop custom agent:", error)
|
||||
} finally {
|
||||
setActionLoading(null)
|
||||
}
|
||||
}
|
||||
|
||||
// 重启自定义Agent
|
||||
const handleRestartCustomAgent = async (agentName: string) => {
|
||||
try {
|
||||
setActionLoading(agentName)
|
||||
const result = await TaijiAPIClient.restartCustomAgent(agentName)
|
||||
if (result?.success) {
|
||||
loadCustomAgents()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to restart custom agent:", error)
|
||||
} finally {
|
||||
setActionLoading(null)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status?.toLowerCase()) {
|
||||
case "running":
|
||||
return "bg-green-500/10 text-green-500"
|
||||
case "stopped":
|
||||
return "bg-gray-500/10 text-gray-500"
|
||||
case "pending":
|
||||
return "bg-yellow-500/10 text-yellow-500"
|
||||
case "failed":
|
||||
return "bg-red-500/10 text-red-500"
|
||||
default:
|
||||
return "bg-gray-500/10 text-gray-500"
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthGuard>
|
||||
<DashboardLayout>
|
||||
@@ -239,6 +327,124 @@ export default function DataToolsPage() {
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 自定义Agent管理 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Bot className="h-5 w-5" />
|
||||
{t("自定义Agent管理", "Custom Agent Management")}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{t("管理已部署的自定义Agent,支持停止、重启和删除操作", "Manage deployed custom agents with stop, restart and delete operations")}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onClick={loadCustomAgents} className="gap-2">
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
{t("刷新", "Refresh")}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t("Agent名称", "Agent Name")}</TableHead>
|
||||
<TableHead>{t("模板", "Template")}</TableHead>
|
||||
<TableHead>{t("状态", "Status")}</TableHead>
|
||||
<TableHead>{t("CPU", "CPU")}</TableHead>
|
||||
<TableHead>{t("内存", "Memory")}</TableHead>
|
||||
<TableHead>{t("运行时间", "Runtime")}</TableHead>
|
||||
<TableHead className="text-right">{t("操作", "Actions")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{customAgentsLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center py-8">
|
||||
<span className="text-muted-foreground">{t("加载中...", "Loading...")}</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : customAgents.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center py-8">
|
||||
<span className="text-muted-foreground">{t("暂无自定义Agent", "No custom agents")}</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
customAgents.map((agent) => (
|
||||
<TableRow key={agent.name}>
|
||||
<TableCell className="font-medium">{agent.name}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="secondary">{agent.template || "-"}</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge className={getStatusColor(agent.status)}>
|
||||
{agent.status || "Unknown"}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-sm">{agent.cpu || "-"}</TableCell>
|
||||
<TableCell className="text-sm">{agent.memory || "-"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">
|
||||
{agent.runningSeconds
|
||||
? `${Math.floor(agent.runningSeconds / 3600)}h ${Math.floor((agent.runningSeconds % 3600) / 60)}m`
|
||||
: "-"
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex justify-end gap-1">
|
||||
{agent.status?.toLowerCase() === "running" ? (
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
title={t("停止", "Stop")}
|
||||
disabled={actionLoading === agent.name}
|
||||
onClick={() => handleStopCustomAgent(agent.name)}
|
||||
>
|
||||
<Square className="h-4 w-4 text-yellow-500" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
title={t("重启", "Restart")}
|
||||
disabled={actionLoading === agent.name}
|
||||
onClick={() => handleRestartCustomAgent(agent.name)}
|
||||
>
|
||||
<RefreshCw className="h-4 w-4 text-blue-500" />
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
title={t("启动", "Start")}
|
||||
disabled={actionLoading === agent.name}
|
||||
onClick={() => handleRestartCustomAgent(agent.name)}
|
||||
>
|
||||
<Play className="h-4 w-4 text-green-500" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
title={t("删除", "Delete")}
|
||||
disabled={actionLoading === agent.name}
|
||||
onClick={() => handleDeleteCustomAgent(agent.name)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="templates" className="space-y-4">
|
||||
|
||||
@@ -519,6 +519,56 @@ export class TaijiAPIClient {
|
||||
return handleResponse(response)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户自定义Agent列表(别名)
|
||||
* GET /api/user/custom-agents
|
||||
*/
|
||||
static async getUserCustomAgents() {
|
||||
return this.getCustomAgents()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除自定义Agent
|
||||
* DELETE /api/user/custom-agents/{name}
|
||||
*
|
||||
* @param agentName - Agent名称
|
||||
*/
|
||||
static async deleteCustomAgent(agentName: string) {
|
||||
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents/${agentName}`, {
|
||||
method: "DELETE",
|
||||
headers: buildHeaders(),
|
||||
})
|
||||
return handleResponse(response)
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止自定义Agent
|
||||
* POST /api/user/custom-agents/{name}/stop
|
||||
*
|
||||
* @param agentName - Agent名称
|
||||
*/
|
||||
static async stopCustomAgent(agentName: string) {
|
||||
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents/${agentName}/stop`, {
|
||||
method: "POST",
|
||||
headers: buildHeaders(),
|
||||
})
|
||||
return handleResponse(response)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启自定义Agent
|
||||
* POST /api/user/custom-agents/{name}/restart
|
||||
*
|
||||
* @param agentName - Agent名称
|
||||
*/
|
||||
static async restartCustomAgent(agentName: string) {
|
||||
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents/${agentName}/restart`, {
|
||||
method: "POST",
|
||||
headers: buildHeaders(),
|
||||
})
|
||||
return handleResponse(response)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作流列表
|
||||
* GET /api/user/workflows/list
|
||||
|
||||
Reference in New Issue
Block a user