feat: decision tree visualization with React Flow + Dagre auto-layout

This commit is contained in:
Vectry
2026-02-09 23:58:41 +00:00
parent 21b4f9f316
commit 867e1e9eb1
6 changed files with 1151 additions and 24 deletions

View File

@@ -20,9 +20,10 @@ import {
Terminal,
} from "lucide-react";
import { cn, formatDuration, formatRelativeTime } from "@/lib/utils";
import { DecisionTree } from "./decision-tree";
type TraceStatus = "RUNNING" | "COMPLETED" | "ERROR";
type TabType = "decisions" | "spans" | "events";
type TabType = "tree" | "decisions" | "spans" | "events";
interface DecisionPoint {
id: string;
@@ -33,6 +34,7 @@ interface DecisionPoint {
contextSnapshot: Record<string, unknown> | null;
confidence: number | null;
timestamp: string;
parentSpanId?: string | null;
}
interface Span {
@@ -46,6 +48,7 @@ interface Span {
input: unknown;
output: unknown;
metadata: Record<string, unknown>;
parentSpanId?: string | null;
}
interface Event {
@@ -54,6 +57,7 @@ interface Event {
name: string;
timestamp: string;
metadata: Record<string, unknown>;
spanId?: string | null;
}
interface Trace {
@@ -138,7 +142,7 @@ export function TraceDetail({
spans,
events,
}: TraceDetailProps) {
const [activeTab, setActiveTab] = useState<TabType>("decisions");
const [activeTab, setActiveTab] = useState<TabType>("tree");
const status = statusConfig[trace.status];
const StatusIcon = status.icon;
@@ -243,6 +247,12 @@ export function TraceDetail({
{/* Tabs */}
<div className="border-b border-neutral-800">
<div className="flex gap-1">
<TabButton
active={activeTab === "tree"}
onClick={() => setActiveTab("tree")}
icon={GitBranch}
label="Tree"
/>
<TabButton
active={activeTab === "decisions"}
onClick={() => setActiveTab("decisions")}
@@ -265,7 +275,15 @@ export function TraceDetail({
</div>
{/* Tab Content */}
<div className="min-h-[400px]">
<div className="min-h-[600px]">
{activeTab === "tree" && (
<DecisionTree
trace={trace}
spans={spans}
decisionPoints={decisionPoints}
events={events}
/>
)}
{activeTab === "decisions" && (
<DecisionsTab decisionPoints={decisionPoints} />
)}