Spaces:
Paused
Paused
File size: 5,842 Bytes
5a81b95 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import React, { useState, useCallback } from 'react';
import {
ReactFlow,
Controls,
Background,
applyNodeChanges,
applyEdgeChanges,
addEdge,
Node,
Edge,
OnNodesChange,
OnEdgesChange,
OnConnect,
NodeTypes,
Handle,
Position,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { Bot, Play, Save, Plus, Settings } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { API_URL } from '@/config/api';
// Custom Node Component
const AgentNode = ({ data }: { data: { label: string; role?: string } }) => {
return (
<div className="px-4 py-2 shadow-md rounded-md bg-card border border-border min-w-[150px]">
<div className="flex items-center">
<div className="rounded-full w-8 h-8 flex items-center justify-center bg-purple-500/20 text-purple-500 mr-2">
<Bot size={16} />
</div>
<div className="ml-2">
<div className="text-sm font-bold">{data.label}</div>
<div className="text-xs text-gray-500">{data.role || 'General Agent'}</div>
</div>
</div>
<Handle type="target" position={Position.Top} className="w-16 !bg-muted-foreground" />
<Handle type="source" position={Position.Bottom} className="w-16 !bg-muted-foreground" />
</div>
);
};
const nodeTypes: NodeTypes = {
agent: AgentNode,
};
const initialNodes: Node[] = [
{
id: '1',
type: 'agent',
data: { label: 'Orchestrator', role: 'Supervisor' },
position: { x: 250, y: 5 },
},
];
const initialEdges: Edge[] = [];
export default function AgentFlowWidget() {
const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState<Edge[]>(initialEdges);
const [isRunning, setIsRunning] = useState(false);
const onNodesChange: OnNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[],
);
const onEdgesChange: OnEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[],
);
const onConnect: OnConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[],
);
const addAgent = () => {
const id = `agent-${nodes.length + 1}`;
const newNode: Node = {
id,
type: 'agent',
data: { label: `Agent ${nodes.length + 1}`, role: 'Worker' },
position: { x: Math.random() * 400, y: Math.random() * 400 },
};
setNodes((nds) => [...nds, newNode]);
};
const runFlow = async () => {
setIsRunning(true);
try {
// Prepare payload for backend
const workflowPayload = {
workflow: {
nodes: nodes.map(n => ({ id: n.id, type: n.type, data: n.data })),
edges: edges.map(e => ({ source: e.source, target: e.target }))
}
};
// Call the backend handler (this matches the structure found in toolHandlers.ts)
// Note: In a real app, this would be a proper API call or WebSocket event
console.log('Running workflow:', workflowPayload);
// Simulation
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error('Flow execution failed:', error);
} finally {
setIsRunning(false);
}
};
return (
<div className="h-full flex flex-col bg-card/80 backdrop-blur-sm border border-border/50 rounded-lg overflow-hidden shadow-lg">
{/* Header */}
<div className="p-4 border-b border-border/50 bg-gradient-to-r from-purple-500/10 to-blue-500/5 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-gradient-to-br from-purple-500 to-blue-600 rounded-lg shadow-md">
<Bot className="w-5 h-5 text-white" />
</div>
<div>
<h2 className="font-bold text-foreground tracking-tight">Agent Flow Builder</h2>
<div className="flex items-center gap-2 text-[10px] font-mono text-muted-foreground">
<span className={cn("w-1.5 h-1.5 rounded-full", isRunning ? "bg-green-400 animate-pulse" : "bg-purple-400")} />
{isRunning ? "EXECUTING" : "DESIGN MODE"}
</div>
</div>
</div>
<div className="flex gap-2">
<Button size="sm" variant="outline" onClick={addAgent} className="h-8 gap-2">
<Plus size={14} /> Add Agent
</Button>
<Button size="sm" onClick={runFlow} disabled={isRunning} className="h-8 gap-2 bg-purple-600 hover:bg-purple-700">
<Play size={14} /> Run Flow
</Button>
</div>
</div>
{/* Canvas */}
<div className="flex-1 h-[500px] w-full bg-background/50">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
>
<Background color="#888" gap={16} size={1} />
<Controls />
</ReactFlow>
</div>
</div>
);
}
|