完整实现 Swarm 多智能体协作系统
- 新增 CLIPluginAdapter 统一接口 (backend/app/core/agent_adapter.py) - 新增 LLM 服务层,支持 Anthropic/OpenAI/DeepSeek/Ollama (backend/app/services/llm_service.py) - 新增 Agent 执行引擎,支持文件锁自动管理 (backend/app/services/agent_executor.py) - 新增 NativeLLMAgent 原生 LLM 适配器 (backend/app/adapters/native_llm_agent.py) - 新增进程管理器 (backend/app/services/process_manager.py) - 新增 Agent 控制 API (backend/app/routers/agents_control.py) - 新增 WebSocket 实时通信 (backend/app/routers/websocket.py) - 更新前端 AgentsPage,支持启动/停止 Agent - 测试通过:Agent 启动、批量操作、栅栏同步 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
279
frontend/src/components/BarrierSyncCard.tsx
Normal file
279
frontend/src/components/BarrierSyncCard.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
import { Shield, Zap } from "lucide-react";
|
||||
|
||||
type NodeState = "ready" | "waiting" | "pending";
|
||||
|
||||
interface BarrierNode {
|
||||
id: string;
|
||||
name: string;
|
||||
gradient: string;
|
||||
state: NodeState;
|
||||
waitingFor?: string;
|
||||
}
|
||||
|
||||
const nodes: BarrierNode[] = [
|
||||
{
|
||||
id: "n1",
|
||||
name: "CLA",
|
||||
gradient: "linear-gradient(135deg,#8b5cf6,#6366f1)",
|
||||
state: "ready",
|
||||
},
|
||||
{
|
||||
id: "n2",
|
||||
name: "KIM",
|
||||
gradient: "linear-gradient(135deg,#f59e0b,#d97706)",
|
||||
state: "waiting",
|
||||
waitingFor: "架构设计完成",
|
||||
},
|
||||
{
|
||||
id: "n3",
|
||||
name: "OPC",
|
||||
gradient: "linear-gradient(135deg,#10b981,#059669)",
|
||||
state: "ready",
|
||||
},
|
||||
{
|
||||
id: "n4",
|
||||
name: "USR",
|
||||
gradient: "linear-gradient(135deg,#f59e0b,#b45309)",
|
||||
state: "pending",
|
||||
},
|
||||
];
|
||||
|
||||
const stateColors: Record<NodeState, string> = {
|
||||
ready: "#00ff9d",
|
||||
waiting: "#ff9500",
|
||||
pending: "#374151",
|
||||
};
|
||||
|
||||
const stateLabels: Record<NodeState, string> = {
|
||||
ready: "READY",
|
||||
waiting: "WAIT",
|
||||
pending: "IDLE",
|
||||
};
|
||||
|
||||
const syncPoints = [
|
||||
{ name: "INIT", completed: true },
|
||||
{ name: "REVIEW", completed: true },
|
||||
{ name: "DESIGN", completed: false, active: true },
|
||||
{ name: "IMPL", completed: false },
|
||||
{ name: "DEPLOY", completed: false },
|
||||
];
|
||||
|
||||
export function BarrierSyncCard() {
|
||||
const readyCount = nodes.filter(n => n.state === "ready").length;
|
||||
|
||||
return (
|
||||
<div className="glass-card" style={{ padding: 20, height: "100%", display: "flex", flexDirection: "column" }}>
|
||||
{/* Header */}
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14, flexShrink: 0 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||
<div
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 8,
|
||||
background: "rgba(0,240,255,0.1)",
|
||||
border: "1px solid rgba(0,240,255,0.2)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Shield size={14} color="#00f0ff" />
|
||||
</div>
|
||||
<span className="card-title">栅栏同步</span>
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
||||
<span className="font-mono-code" style={{ fontSize: 11, color: "#ff9500" }}>
|
||||
{readyCount}/{nodes.length} 就绪
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
padding: "3px 10px",
|
||||
background: "rgba(255,149,0,0.1)",
|
||||
border: "1px solid rgba(255,149,0,0.3)",
|
||||
borderRadius: 6,
|
||||
}}
|
||||
>
|
||||
<span className="font-mono-code" style={{ fontSize: 10, color: "#ff9500" }}>
|
||||
<span className="animate-pulse-fast">●</span> 等待触发
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", gap: 20, flex: 1, minHeight: 0 }}>
|
||||
{/* Left: Agent nodes */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 8, minWidth: 200 }}>
|
||||
{nodes.map(node => (
|
||||
<div
|
||||
key={node.id}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
padding: "8px 12px",
|
||||
background: "rgba(0,0,0,0.3)",
|
||||
border: `1px solid ${stateColors[node.state]}30`,
|
||||
borderRadius: 10,
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="agent-avatar"
|
||||
style={{ background: node.gradient, width: 32, height: 32, fontSize: 10 }}
|
||||
>
|
||||
{node.name}
|
||||
</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div className="font-mono-code" style={{ fontSize: 11, color: "#9ca3af" }}>
|
||||
{node.name === "CLA" ? "Claude Code" : node.name === "KIM" ? "Kimi CLI" : node.name === "OPC" ? "OpenCode" : "Tech Lead"}
|
||||
</div>
|
||||
{node.waitingFor && (
|
||||
<div className="font-mono-code" style={{ fontSize: 9, color: "#ff9500", marginTop: 1 }}>
|
||||
等待: {node.waitingFor}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="barrier-node"
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderColor: stateColors[node.state],
|
||||
color: stateColors[node.state],
|
||||
background: `${stateColors[node.state]}10`,
|
||||
fontSize: 8,
|
||||
}}
|
||||
title={stateLabels[node.state]}
|
||||
>
|
||||
{node.state === "ready" ? (
|
||||
<svg width="12" height="12" viewBox="0 0 12 12">
|
||||
<path d="M2 6L5 9L10 3" stroke="#00ff9d" strokeWidth="1.5" strokeLinecap="round" fill="none" />
|
||||
</svg>
|
||||
) : node.state === "waiting" ? (
|
||||
<Zap size={12} />
|
||||
) : (
|
||||
<span style={{ fontSize: 8 }}>—</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right: Sync points */}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div className="font-mono-code" style={{ fontSize: 10, color: "#6b7280", marginBottom: 10 }}>
|
||||
同步检查点
|
||||
</div>
|
||||
|
||||
{/* Progress line */}
|
||||
<div style={{ position: "relative", marginBottom: 16 }}>
|
||||
<div
|
||||
style={{
|
||||
height: 3,
|
||||
background: "#111827",
|
||||
borderRadius: 2,
|
||||
position: "relative",
|
||||
overflow: "visible",
|
||||
}}
|
||||
>
|
||||
{/* Completed portion */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
height: "100%",
|
||||
width: "42%",
|
||||
background: "linear-gradient(90deg,#00f0ff,#00ff9d)",
|
||||
borderRadius: 2,
|
||||
}}
|
||||
/>
|
||||
{/* Active flowing portion */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "42%",
|
||||
top: 0,
|
||||
height: "100%",
|
||||
width: "16%",
|
||||
background: "linear-gradient(90deg,#ff9500,rgba(255,149,0,0.3))",
|
||||
borderRadius: 2,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
background: "linear-gradient(90deg,transparent,rgba(255,255,255,0.3),transparent)",
|
||||
animation: "line-flow 1.5s linear infinite",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Dots */}
|
||||
{syncPoints.map((sp, i) => {
|
||||
const left = `${(i / (syncPoints.length - 1)) * 100}%`;
|
||||
return (
|
||||
<div
|
||||
key={sp.name}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left,
|
||||
transform: "translate(-50%,-50%)",
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: "50%",
|
||||
border: `2px solid ${sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#374151"}`,
|
||||
background: sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#111827",
|
||||
zIndex: 2,
|
||||
boxShadow: sp.active ? "0 0 8px rgba(255,149,0,0.6)" : sp.completed ? "0 0 6px rgba(0,255,157,0.4)" : "none",
|
||||
}}
|
||||
className={sp.active ? "animate-scale-pulse" : ""}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Labels */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", marginTop: 12 }}>
|
||||
{syncPoints.map(sp => (
|
||||
<div
|
||||
key={sp.name}
|
||||
className="font-mono-code"
|
||||
style={{
|
||||
fontSize: 9,
|
||||
color: sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#374151",
|
||||
textAlign: "center",
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
{sp.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status panel */}
|
||||
<div
|
||||
style={{
|
||||
padding: "10px 12px",
|
||||
background: "rgba(255,149,0,0.05)",
|
||||
border: "1px solid rgba(255,149,0,0.15)",
|
||||
borderRadius: 10,
|
||||
}}
|
||||
>
|
||||
<div className="font-mono-code" style={{ fontSize: 10, color: "#ff9500", marginBottom: 4 }}>
|
||||
⏸ 等待 DESIGN 检查点同步
|
||||
</div>
|
||||
<div className="font-mono-code" style={{ fontSize: 10, color: "#6b7280" }}>
|
||||
触发条件:所有 Agent 调用 wait_for_meeting("design_review") · 已就绪 {readyCount}/{nodes.length}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user