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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions