"use client"; import { useState } from "react"; import Link from "next/link"; import { ArrowLeft, CheckCircle, XCircle, Activity, Clock, Calendar, GitBranch, Layers, Zap, ChevronDown, ChevronRight, FileJson, DollarSign, AlertCircle, Terminal, } from "lucide-react"; import { cn, formatDuration, formatRelativeTime } from "@/lib/utils"; import { DecisionTree } from "./decision-tree"; import { TraceAnalytics } from "./trace-analytics"; type TraceStatus = "RUNNING" | "COMPLETED" | "ERROR"; type TabType = "tree" | "analytics" | "decisions" | "spans" | "events"; interface DecisionPoint { id: string; type: string; chosenAction: string; alternatives: string[]; reasoning: string | null; contextSnapshot: Record | null; confidence: number | null; timestamp: string; parentSpanId?: string | null; } interface Span { id: string; name: string; type: string; status: "OK" | "ERROR" | "CANCELLED"; startedAt: string; endedAt: string | null; durationMs: number | null; input: unknown; output: unknown; metadata: Record; parentSpanId?: string | null; } interface Event { id: string; type: string; name: string; timestamp: string; metadata: Record; spanId?: string | null; } interface Trace { id: string; name: string; status: TraceStatus; startedAt: string; endedAt: string | null; durationMs: number | null; tags: string[]; metadata: Record; costUsd: number | null; } interface TraceDetailProps { trace: Trace; decisionPoints: DecisionPoint[]; spans: Span[]; events: Event[]; } const statusConfig: Record; color: string; bgColor: string }> = { RUNNING: { label: "Running", icon: Activity, color: "text-amber-400", bgColor: "bg-amber-500/10 border-amber-500/20", }, COMPLETED: { label: "Completed", icon: CheckCircle, color: "text-emerald-400", bgColor: "bg-emerald-500/10 border-emerald-500/20", }, ERROR: { label: "Error", icon: XCircle, color: "text-red-400", bgColor: "bg-red-500/10 border-red-500/20", }, }; const decisionTypeColors: Record = { TOOL_SELECTION: { bg: "bg-blue-500/10", text: "text-blue-400", border: "border-blue-500/20", }, PATH_SELECTION: { bg: "bg-purple-500/10", text: "text-purple-400", border: "border-purple-500/20", }, RESPONSE_FORMULATION: { bg: "bg-emerald-500/10", text: "text-emerald-400", border: "border-emerald-500/20", }, DEFAULT: { bg: "bg-neutral-700/30", text: "text-neutral-400", border: "border-neutral-600/30", }, }; const spanStatusColors: Record = { OK: "text-emerald-400 bg-emerald-500/10 border-emerald-500/20", ERROR: "text-red-400 bg-red-500/10 border-red-500/20", CANCELLED: "text-amber-400 bg-amber-500/10 border-amber-500/20", }; const eventTypeColors: Record; color: string }> = { LLM_CALL: { icon: Zap, color: "text-purple-400" }, TOOL_CALL: { icon: Terminal, color: "text-blue-400" }, ERROR: { icon: AlertCircle, color: "text-red-400" }, DEFAULT: { icon: Activity, color: "text-neutral-400" }, }; export function TraceDetail({ trace, decisionPoints, spans, events, }: TraceDetailProps) { const [activeTab, setActiveTab] = useState("tree"); const status = statusConfig[trace.status]; const StatusIcon = status.icon; return (
{/* Back Button */} Back to traces {/* Header */}
{/* Title and Status */}

{trace.name}

{status.label}
Started {formatRelativeTime(trace.startedAt)} Duration {formatDuration(trace.durationMs)} {trace.costUsd !== null && ( Cost ${trace.costUsd.toFixed(4)} )}
{trace.tags.length > 0 && (
{trace.tags.map((tag) => ( {tag} ))}
)}
{/* Stats */}
{decisionPoints.length}
Decisions
{spans.length}
Spans
{events.length}
Events
{/* Tabs */}
setActiveTab("tree")} icon={GitBranch} label="Tree" /> setActiveTab("analytics")} icon={Activity} label="Analytics" /> setActiveTab("decisions")} icon={GitBranch} label={`Decisions (${decisionPoints.length})`} /> setActiveTab("spans")} icon={Layers} label={`Spans (${spans.length})`} /> setActiveTab("events")} icon={Activity} label={`Events (${events.length})`} />
{/* Tab Content */}
{activeTab === "tree" && ( )} {activeTab === "analytics" && ( )} {activeTab === "decisions" && ( )} {activeTab === "spans" && } {activeTab === "events" && }
); } function TabButton({ active, onClick, icon: Icon, label, }: { active: boolean; onClick: () => void; icon: React.ComponentType<{ className?: string }>; label: string; }) { return ( ); } function DecisionsTab({ decisionPoints }: { decisionPoints: DecisionPoint[] }) { if (decisionPoints.length === 0) { return (

No decision points recorded

); } return (
{decisionPoints.map((decision) => ( ))}
); } function DecisionCard({ decision }: { decision: DecisionPoint }) { const [expanded, setExpanded] = useState(false); const colors = decisionTypeColors[decision.type] || decisionTypeColors.DEFAULT; return (
{decision.type}

{decision.chosenAction}

{formatRelativeTime(decision.timestamp)}

{decision.confidence !== null && (
Confidence

{Math.round(decision.confidence * 100)}%

)}
{decision.reasoning && (

“{decision.reasoning}”

)} {decision.alternatives.length > 0 && (
{expanded && (
{decision.alternatives.map((alt, idx) => (
#{idx + 1} {alt}
))}
)}
)} {decision.contextSnapshot && Object.keys(decision.contextSnapshot).length > 0 && (
Context snapshot
              {JSON.stringify(decision.contextSnapshot, null, 2)}
            
)}
); } function SpansTab({ spans }: { spans: Span[] }) { if (spans.length === 0) { return (

No spans recorded

); } const maxDuration = Math.max(...spans.map((s) => s.durationMs || 0)); return (
{spans.map((span) => ( ))}
); } function SpanItem({ span, maxDuration }: { span: Span; maxDuration: number }) { const [expanded, setExpanded] = useState(false); const statusColor = spanStatusColors[span.status] || spanStatusColors.CANCELLED; const durationPercent = maxDuration > 0 ? ((span.durationMs || 0) / maxDuration) * 100 : 0; return (
{expanded && (
Input
                {JSON.stringify(span.input, null, 2)}
              
Output
                {JSON.stringify(span.output, null, 2)}
              
{Object.keys(span.metadata).length > 0 && (
Metadata
                {JSON.stringify(span.metadata, null, 2)}
              
)}
)}
); } function EventsTab({ events }: { events: Event[] }) { if (events.length === 0) { return (

No events recorded

); } return (
{events.map((event) => { const { icon: Icon, color } = eventTypeColors[event.type] || eventTypeColors.DEFAULT; return (

{event.name}

{event.type}

{formatRelativeTime(event.timestamp)}
); })}
); }