import { useEffect, useMemo, useState } from "react"; import { Bot, Check, LoaderCircle, Save, Sparkles } from "lucide-react"; import { toast } from "sonner"; import { SkillTransferSelector } from "@/components/workspace/agents/skill-transfer-selector"; import { ModelNameSelect } from "@/components/workspace/model-name-select"; import { SkillDetailDialog } from "@/components/workspace/skills/skill-detail-dialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { getAgent, updateAgent } from "@/core/agents/api"; import type { Agent } from "@/core/agents/types"; import { useAllSkillsAdmin } from "@/core/skills/hooks"; import { cn } from "@/lib/utils"; import { STRATEGY_AGENT_ROLES } from "./constants"; interface AgentConfigDialogProps { open: boolean; onOpenChange: (open: boolean) => void; } interface AgentConfigDraft { soul: string; model: string | null; skills: string[]; } function normalizeSkills(skills: string[]) { return [...new Set(skills)].sort((left, right) => left.localeCompare(right)); } function sameSkills(left: string[], right: string[]) { const normalizedLeft = normalizeSkills(left); const normalizedRight = normalizeSkills(right); return ( normalizedLeft.length === normalizedRight.length && normalizedLeft.every((skill, index) => skill === normalizedRight[index]) ); } export function AgentConfigDialog({ open, onOpenChange }: AgentConfigDialogProps) { const [selectedId, setSelectedId] = useState(STRATEGY_AGENT_ROLES[0].id); const [agent, setAgent] = useState(null); const [soul, setSoul] = useState(""); const [model, setModel] = useState(null); const [selectedSkills, setSelectedSkills] = useState([]); const [savedDraft, setSavedDraft] = useState(null); const [detailSkillName, setDetailSkillName] = useState(null); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const selectedRole = useMemo( () => STRATEGY_AGENT_ROLES.find((role) => role.id === selectedId)!, [selectedId], ); const { skills, isLoading: isSkillsLoading, error: skillsError, } = useAllSkillsAdmin(open); const selectableSkills = useMemo( () => skills.filter((skill) => { const globallyAvailable = Boolean(skill.builtin) || Boolean(skill.published); return ( skill.enabled && (skill.state ?? "active") === "active" && globallyAvailable ); }), [skills], ); const dirty = Boolean( agent && savedDraft && (soul !== savedDraft.soul || model !== savedDraft.model || !sameSkills(selectedSkills, savedDraft.skills)), ); useEffect(() => { if (!open || !selectedId) return; let active = true; setLoading(true); setError(""); setAgent(null); setSavedDraft(null); void getAgent(selectedId) .then((data) => { if (!active) return; setAgent(data); const nextDraft = { soul: data.soul ?? "", model: data.model ?? null, skills: data.skills ?? [], }; setSoul(nextDraft.soul); setModel(nextDraft.model); setSelectedSkills(nextDraft.skills); setSavedDraft(nextDraft); }) .catch((reason: unknown) => { if (!active) return; setError( reason instanceof Error ? reason.message : "智能体配置加载失败,请确认后端已完成默认配置初始化。", ); }) .finally(() => { if (active) setLoading(false); }); return () => { active = false; }; }, [open, selectedId]); const save = async () => { if (!agent) return; setSaving(true); try { const updated = await updateAgent(agent.id, { soul: soul.trim(), model, skills: selectedSkills, }); setAgent(updated); setSoul(updated.soul ?? ""); setModel(updated.model ?? null); setSelectedSkills(updated.skills ?? []); setSavedDraft({ soul: updated.soul ?? "", model: updated.model ?? null, skills: updated.skills ?? [], }); toast.success(`${updated.name} 配置已保存`); } catch (reason) { toast.error(reason instanceof Error ? reason.message : "保存失败"); } finally { setSaving(false); } }; const confirmDiscardChanges = () => !dirty || window.confirm("当前智能体配置尚未保存,确定放弃这些修改吗?"); const selectRole = (roleId: string) => { if (roleId === selectedId || saving || !confirmDiscardChanges()) return; setSelectedId(roleId); }; const handleOpenChange = (nextOpen: boolean) => { if (!nextOpen && (saving || !confirmDiscardChanges())) return; onOpenChange(nextOpen); }; return ( 策略智能体配置 这是全局统一配置,不属于某一个项目。管理员保存后,所有策略项目下一次运行都会使用这里的模型、SOUL 和技能。
流程角色 · {STRATEGY_AGENT_ROLES.length}
{STRATEGY_AGENT_ROLES.map((role) => { const selected = role.id === selectedId; return ( ); })}

{selectedRole.name}

{selectedRole.role}

{selectedRole.id}
{loading ? (
正在读取智能体配置…
) : error ? (

暂时无法加载该智能体

{error}

) : agent ? (
当前名称
{agent.name}