File size: 5,059 Bytes
3193174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 { useState, useEffect } from "react";
import { Play, Square, RotateCcw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Separator } from "@/components/ui/separator";
import { ExecutionTimeline } from "./ExecutionTimeline";
import { TokenUsageChart } from "./TokenUsageChart";
import { ResultViewer } from "./ResultViewer";
import { AgentStatusBadge } from "./AgentStatusBadge";
import { useExecutionStore } from "@/stores/executionStore";
import { useGraphStore } from "@/stores/graphStore";
import { useConfigStore } from "@/stores/configStore";
import type { AgentExecutionStatus } from "@/types/execution";

export function ExecutionPanel() {
  const { taskQuery, setTaskQuery, toGraphRequest, updateNodeExecutionStatus, resetExecutionStatus, nodes } =
    useGraphStore();

  const {
    status,
    events,
    agentStatuses,
    tokenUsage,
    finalAnswer,
    totalTokens,
    totalTime,
    error,
    startExecution,
    cancelExecution,
    clearRun,
  } = useExecutionStore();

  const { runnerConfig, llmProviders } = useConfigStore();

  const isRunning = status === "running";

  // Sync execution statuses to graph nodes for live visualization
  useEffect(() => {
    Object.entries(agentStatuses).forEach(([agentId, agentStatus]) => {
      updateNodeExecutionStatus(agentId, agentStatus as AgentExecutionStatus);
    });
  }, [agentStatuses, updateNodeExecutionStatus]);

  // Update node token counts
  useEffect(() => {
    Object.entries(tokenUsage).forEach(([agentId, tokens]) => {
      updateNodeExecutionStatus(agentId, agentStatuses[agentId] as AgentExecutionStatus || "completed", undefined, tokens);
    });
  }, [tokenUsage]);

  const handleRun = async () => {
    resetExecutionStatus();
    const graphData = toGraphRequest();
    const provider = llmProviders.length > 0 ? llmProviders[0] : null;
    await startExecution(null, graphData, taskQuery, runnerConfig, provider);
  };

  const handleStop = () => {
    cancelExecution();
  };

  const handleClear = () => {
    clearRun();
    resetExecutionStatus();
  };

  return (
    <div className="flex h-full w-80 flex-col border-l bg-card">
      <div className="p-3 border-b space-y-3">
        <div className="space-y-2">
          <Label className="text-xs">Task Query</Label>
          <Input
            placeholder="Describe the task for the agents..."
            value={taskQuery}
            onChange={(e) => setTaskQuery(e.target.value)}
            disabled={isRunning}
          />
        </div>

        <div className="flex gap-2">
          {!isRunning ? (
            <Button
              onClick={handleRun}
              disabled={nodes.length === 0 || !taskQuery.trim()}
              className="flex-1 gap-1"
              size="sm"
            >
              <Play className="h-3.5 w-3.5" />
              Execute
            </Button>
          ) : (
            <Button onClick={handleStop} variant="destructive" className="flex-1 gap-1" size="sm">
              <Square className="h-3.5 w-3.5" />
              Stop
            </Button>
          )}
          <Button onClick={handleClear} variant="outline" size="sm" disabled={isRunning}>
            <RotateCcw className="h-3.5 w-3.5" />
          </Button>
        </div>

        {/* Agent statuses */}
        {Object.keys(agentStatuses).length > 0 && (
          <>
            <Separator />
            <div className="space-y-1">
              {Object.entries(agentStatuses).map(([id, agentStatus]) => (
                <AgentStatusBadge
                  key={id}
                  status={agentStatus as AgentExecutionStatus}
                  agentName={id}
                />
              ))}
            </div>
          </>
        )}
      </div>

      <Tabs defaultValue="timeline" className="flex-1 flex flex-col">
        <TabsList className="mx-3 mt-2">
          <TabsTrigger value="timeline" className="text-xs">
            Timeline
          </TabsTrigger>
          <TabsTrigger value="tokens" className="text-xs">
            Tokens
          </TabsTrigger>
          <TabsTrigger value="result" className="text-xs">
            Result
          </TabsTrigger>
        </TabsList>

        <TabsContent value="timeline" className="flex-1 overflow-hidden mt-0">
          <ExecutionTimeline events={events} />
        </TabsContent>

        <TabsContent value="tokens" className="flex-1 overflow-hidden mt-0 p-3">
          <TokenUsageChart tokenUsage={tokenUsage} />
        </TabsContent>

        <TabsContent value="result" className="flex-1 overflow-hidden mt-0">
          <ResultViewer
            status={status}
            finalAnswer={finalAnswer}
            totalTokens={totalTokens}
            totalTime={totalTime}
            events={events}
            error={error}
          />
        </TabsContent>
      </Tabs>
    </div>
  );
}