Back to Langfuse

trace-graph-view

web/src/features/trace-graph-view/README.md

3.205.02.3 KB
Original Source

trace-graph-view

Read-only agent-graph renderer for the trace detail view: ELK computes the layout, we draw HTML nodes over an SVG edge layer, d3-zoom owns the viewport. Deliberately NOT React Flow / vis-network — the view is read-only and the custom renderer keeps it virtualization-ready.

Data flow (one way)

agentGraphData (tRPC getAgentGraphData)
  → buildStepData            timing-based step inference (cycle-guarded)
    / transformLanggraphToGeneralized   when langgraph metadata exists
  → buildGraphCanvasData     nodes/edges + node→observations cycling map
  → layout/elkLayout.computeGraphLayout   async ELK (lazy import);
      layout/measureNode     estimates node boxes (labels, counter reserve)
  → components/ElkGraphRenderer           draws + gestures
      components/GraphNode                view-only node (memo)

Ownership

  • Viewport: deterministic and data-derived — the rendered transform is always userOverride ?? fit(layout, size). The user's last gesture (drag/wheel/pinch/toolbar zoom) is the ONLY viewport state; without one, fit re-applies on every layout/size change. Selection never moves the viewport (it's a ring/glow — under fit the node is always visible); Fit and a graph change clear the override. Per-frame pan/zoom writes the world div's CSS transform (and the edge stroke-compensation var) imperatively — React state holds only the discrete derivations (compact label threshold, fitted reveal, layout/error).
  • Selection: the ?observation= URL param, wired in components/TraceGraphView.tsx (click-cycling through a node's observations, URL→node sync with a parent-walk fallback for descendants without their own graph node).
  • Playback glow: the active-observation set comes from web/src/components/trace/contexts/PlayheadContext.tsx (the engine); THIS folder owns only the projection observation-ids → node names (components/TraceGraphView.tsx) and the glow rendering (GraphNode).
  • Pure layout math: layout/* has no React imports and is unit-tested (layout/*.clienttest.ts).

Next migration slices

  • Move ELK layout into a Web Worker (seam: layout/elkLayout.ts#getElk).
  • Node virtualization for very large graphs (only render nodes intersecting the viewport — the renderer's world/viewport split is already shaped for it).