import { useState, useEffect, useRef } from "react"; interface ReasoningStep { id: string; type: "tool" | "result" | "error" | "thinking"; content: string; timestamp: Date; status: "running" | "completed"; } interface ReasoningPanelProps { sessionId?: string; } export function ReasoningPanel({ sessionId = "default" }: ReasoningPanelProps) { const [steps, setSteps] = useState([]); const [isStreaming, setIsStreaming] = useState(false); const containerRef = useRef(null); // Subscribe to status updates useEffect(() => { let eventSource: EventSource | null = null; const connect = () => { // Use relative URL for production, works with both local and HF Spaces const apiBase = import.meta.env.VITE_API_BASE_URL || ""; eventSource = new EventSource(`${apiBase}/api/status/${sessionId}/stream`); eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); // Handle reasoning steps from backend if (data.reasoning && data.reasoning.length > 0) { setIsStreaming(true); const newSteps: ReasoningStep[] = data.reasoning.map((step: any, index: number) => ({ id: `step-${index}-${step.timestamp}`, type: step.type as ReasoningStep["type"], content: step.content, timestamp: new Date(step.timestamp), status: step.status === "active" ? "running" : "completed", })); setSteps(newSteps); } if (data.completed_at) { setIsStreaming(false); } } catch (e) { console.error("Error parsing status:", e); } }; eventSource.onerror = () => { eventSource?.close(); // Reconnect after 3 seconds setTimeout(connect, 3000); }; }; connect(); return () => eventSource?.close(); }, [sessionId]); // Auto-scroll to bottom useEffect(() => { if (containerRef.current) { containerRef.current.scrollTop = containerRef.current.scrollHeight; } }, [steps]); const getIcon = (type: ReasoningStep["type"], status: string) => { if (status === "running") { return (
); } switch (type) { case "tool": return 🔧; case "result": return ✓; case "error": return ✗; case "thinking": return 🧠; default: return •; } }; const getColor = (type: ReasoningStep["type"], status: string) => { if (status === "running") return "text-blue-300 bg-blue-500/10 border-blue-500/30"; switch (type) { case "tool": return "text-blue-300 bg-gray-800/50 border-gray-600"; case "result": return "text-green-300 bg-green-500/10 border-green-500/30"; case "error": return "text-red-300 bg-red-500/10 border-red-500/30"; case "thinking": return "text-purple-300 bg-purple-500/10 border-purple-500/30"; default: return "text-gray-300 bg-gray-800/50 border-gray-600"; } }; return (
{/* Header */}
🤖 Tool Calls {isStreaming && ( Live )}
{steps.length > 0 && ( )}
{/* Tool call log */}
{steps.length === 0 ? (
🔧

Tool calls will appear here

When AI uses tools, you'll see them logged

) : (
{steps.map((step) => (
{getIcon(step.type, step.status)}
{step.content} {step.timestamp.toLocaleTimeString()}
))} {isStreaming && (
Waiting for next action...
)}
)}
); }