Spaces:
Running
Running
| import { | |
| Play, | |
| CheckCircle2, | |
| XCircle, | |
| AlertTriangle, | |
| Zap, | |
| Activity, | |
| StopCircle, | |
| GitBranch, | |
| type LucideIcon, | |
| } from "lucide-react"; | |
| import { ScrollArea } from "@/components/ui/scroll-area"; | |
| import type { StreamEvent, StreamEventType } from "@/types/execution"; | |
| const eventConfig: Record<string, { icon: LucideIcon; color: string }> = { | |
| run_start: { icon: Play, color: "text-blue-500" }, | |
| run_end: { icon: CheckCircle2, color: "text-green-500" }, | |
| agent_start: { icon: Play, color: "text-blue-400" }, | |
| agent_output: { icon: CheckCircle2, color: "text-green-400" }, | |
| agent_error: { icon: XCircle, color: "text-red-500" }, | |
| budget_warning: { icon: AlertTriangle, color: "text-yellow-500" }, | |
| budget_exceeded: { icon: AlertTriangle, color: "text-red-500" }, | |
| topology_changed: { icon: GitBranch, color: "text-purple-500" }, | |
| early_stop: { icon: StopCircle, color: "text-amber-500" }, | |
| prune: { icon: Activity, color: "text-orange-500" }, | |
| parallel_start: { icon: Zap, color: "text-indigo-500" }, | |
| parallel_end: { icon: Zap, color: "text-indigo-400" }, | |
| }; | |
| function formatTime(timestamp: string): string { | |
| try { | |
| const d = new Date(timestamp); | |
| return d.toLocaleTimeString([], { hour12: false } as Intl.DateTimeFormatOptions); | |
| } catch { | |
| return ""; | |
| } | |
| } | |
| function eventSummary(event: StreamEvent): string { | |
| const ev = event as Record<string, any>; | |
| switch (event.event_type) { | |
| case "run_start": | |
| return `Run started: ${event.num_agents ?? 0} agents`; | |
| case "run_end": | |
| return `Run ${event.success ? "completed" : "failed"} in ${(event.total_time ?? 0).toFixed(1)}s (${event.total_tokens ?? 0} tokens)`; | |
| case "agent_start": | |
| return `${event.agent_name || event.agent_id} started`; | |
| case "agent_output": { | |
| const preview = (event.content || "").slice(0, 80); | |
| return `${event.agent_name || event.agent_id}: ${preview}${(event.content?.length ?? 0) > 80 ? "..." : ""}`; | |
| } | |
| case "agent_error": | |
| return `${event.agent_id} error: ${event.error_message}`; | |
| case "topology_changed": { | |
| const parts: string[] = ["Topology modified"]; | |
| if (ev.added_edges) parts.push(`+${ev.added_edges} edges`); | |
| if (ev.removed_edges) parts.push(`-${ev.removed_edges} edges`); | |
| if (ev.skipped_agents?.length) parts.push(`skipped: ${ev.skipped_agents.join(", ")}`); | |
| if (ev.forced_agents?.length) parts.push(`forced: ${ev.forced_agents.join(", ")}`); | |
| return parts.join(" | "); | |
| } | |
| case "early_stop": | |
| return `Early stopped: ${ev.reason || event.content || "condition met"}`; | |
| case "prune": | |
| return `Pruned: ${ev.agent_id || ev.agents?.join(", ") || "agents"}`; | |
| case "parallel_start": | |
| return `Parallel: ${event.agent_ids?.join(", ")}`; | |
| case "error": | |
| return `Error: ${event.error || "Unknown"}`; | |
| case "cancelled": | |
| return "Execution cancelled"; | |
| default: | |
| return event.event_type; | |
| } | |
| } | |
| interface ExecutionTimelineProps { | |
| events: StreamEvent[]; | |
| } | |
| export function ExecutionTimeline({ events }: ExecutionTimelineProps) { | |
| const filtered = events.filter( | |
| (e) => e.event_type !== "token" && e.event_type !== "memory_read" && e.event_type !== "memory_write" | |
| ); | |
| return ( | |
| <ScrollArea className="h-full"> | |
| <div className="space-y-1 p-2"> | |
| {filtered.length === 0 && ( | |
| <p className="text-xs text-muted-foreground text-center py-4"> | |
| No events yet. Execute a workflow to see events here. | |
| </p> | |
| )} | |
| {filtered.map((event, i) => { | |
| const cfg = eventConfig[event.event_type] || { icon: Activity, color: "text-muted-foreground" }; | |
| const Icon = cfg.icon; | |
| return ( | |
| <div key={i} className="flex items-start gap-2 text-xs py-1"> | |
| <Icon className={`h-3.5 w-3.5 mt-0.5 flex-shrink-0 ${cfg.color}`} /> | |
| <span className="text-muted-foreground flex-shrink-0 w-16 font-mono"> | |
| {formatTime(event.timestamp)} | |
| </span> | |
| <span className="text-foreground">{eventSummary(event)}</span> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </ScrollArea> | |
| ); | |
| } | |