Skip to content

Eliminate duplicated trace parsing between frontend and backend #130

@krisztianfekete

Description

@krisztianfekete

Problem

We parse trace files in two places:

  • Backend (src/agentevals/loader/): authoritative parser used by every API endpoint, the CLI, the MCP server, and the eval pipeline. Recently broadened to handle Jaeger, OTLP, Tempo v1 (batches), Tempo v2 ({"trace": {...}}), and OTLP JSONL.
  • Frontend (ui/src/lib/trace-loader.ts): a parallel implementation that duplicates the dispatch + span construction logic. The only consumer is
    TraceEditorDrawer.tsx, which uses the parsed Trace tree to compute span edit mappings via buildEditMappings.

Issue #127 was the receipt: the frontend parser was Jaeger-only and crashed with jaegerData.data is undefined on Tempo files, even after the backend learned the new shapes. The two parsers drifted, and users felt it.

Why it matters

  • Drift risk. Every new format we support has to be added in two places, in two languages, with two test
    suites.
  • No single source of truth. The frontend's Span shape, attribute extraction, and parent-child wiring are reimplemented from the Python originals. They look the same today; they will not stay that way.
  • Format detection lives in three places now. Backend loader/auto.py, frontend trace-loader.ts, and (for editor edits) trace-patcher.ts. Two of those exist only to support the editor drawer.

Where it actually matters

TraceEditorDrawer is the only direct consumer of frontend parsing. It needs:

  1. The full Trace tree (spans, parent-child, tags) for buildEditMappings to identify which spans hold which editable fields.
  2. The original raw bytes plus a spanIndex so edits can be applied in place and saved back as a new File (lines 79 to 88 of raceEditorDrawer.tsx).

Everything else in the UI gets parsed data from the backend via /api/convert or the streaming SSE channel.

Proposed approach

Bounded refactor in three steps:

  1. Backend exposes the parsed Trace tree. Either extend /api/convert to optionally include normalized spans, or add a new /api/parse that returns just the structural data the editor needs. Reuse the same load_traces entrypoint we ship today.
  2. Frontend deletes trace-loader.ts's parsing code. TraceEditorDrawer calls the new endpoint instead of loadTraces(content). The Span and Trace TypeScript types stay; their construction moves to the backend.
  3. Keep trace-patcher.ts for the edit roundtrip only. It still needs raw bytes plus offsets to serialize an edited file back to disk; that's a different concern from parsing and should stay client-side. We just remove its dependency on the now-deleted parser.

Out of scope

  • The streaming OTLP path (already backend-only).
  • The performance-metrics extraction (already backend-only via SSE).
  • Renaming OtlpJsonLoader (the name already accurately describes the on-disk shape regardless of which system exported the file).

Acceptance criteria

  • No JSON parsing of trace files in the frontend; trace-loader.ts is deleted or reduced to type definitions.
  • TraceEditorDrawer opens any format the backend supports (Jaeger, OTLP doc, Tempo v1/v2, OTLP JSONL) without a frontend code change.
  • Edit + save roundtrip still works (existing manual test: open trace, edit user/response field, save, re-evaluate).
  • One backend regression test covers the new endpoint shape.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions