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>
    );
}