AI Trace Viewer - Web Interface

The AI Trace Viewer provides two web interfaces for visualizing trace data:

  1. Simple Viewer - Bundled with the Python server (server-rendered)
  2. Advanced Viewer - Standalone web application (client-side, no server required)

Simple Viewer

Location: aitrace/static/

Features

Usage

# Start the server
uv run aitrace

# Open browser
open http://localhost:8000

Architecture


Advanced Viewer

Location: aitrace_viewer/

Overview

A modern, standalone web application for viewing trace logs. Built with Preact, TypeScript, and Vite, it provides advanced features like drag-and-drop file loading, customizable lenses, and detailed JSON inspection.

Features

Log Format

The viewer accepts NDJSON (Newline Delimited JSON) or JSONL (JSON Lines) files.

Example

{"event":"start","timestamp":"2025-10-23T12:00:00Z","trace_id":"abc","span_id":"1"}
{"event":"process","timestamp":"2025-10-23T12:00:01Z","trace_id":"abc","span_id":"2","parent_span_id":"1"}
{"event":"finish","timestamp":"2025-10-23T12:00:02Z","trace_id":"abc","span_id":"1"}

Required Fields

Optional Fields

Usage

Development Mode

cd aitrace_viewer
yarn install
yarn dev

Opens at http://localhost:5173 with hot reload.

Load Sample Data

Click “Load sample logs” to see demo traces.

Upload Your Own Logs

  1. Click the file input to browse for a .jsonl or .ndjson file
  2. Or drag and drop a file onto the drop zone
  3. The viewer will parse and display the trace tree

Ultra-Compact View (Two-Level):

Configurable Density:

State Persistence

Your expand/collapse state is automatically saved!

When you load a file, the viewer:

  1. Computes a SHA256 hash of the file content
  2. Uses this hash to identify the document uniquely
  3. Restores any previously saved expand/collapse state from IndexedDB
  4. Saves state changes automatically as you navigate

Benefits:

State Management Controls:

What’s Saved:

Default Behavior:

Architecture

Data Model

type SpanNode = {
  id: string; // span_id
  traceId: string;
  parentId?: string | null;
  children: string[]; // child span ids
  depth: number;
  timestamp: number; // epoch ms
  level: "debug" | "info" | "warn" | "error";
  msg?: string; // event field
  raw: Record<string, unknown>[]; // all log entries for this span
};

Tree Construction

  1. Index all lines by span_id; maintain trace_id → roots
  2. If a child arrives before parent, keep a pending map; fix up after ingestion
  3. Compute depth with DFS
  4. Handle edge cases: missing parents, orphan nodes, cycles
  5. Sort siblings by timestamp

Components

State Management


Lens System

The lens system provides a powerful way to customize how different types of log entries are displayed.

What are Lenses?

Lenses are display configurations that match specific event patterns and define how their data should be rendered. They allow you to:

How Lenses Work

When a log entry is displayed, the viewer:

  1. Finds the best matching lens based on the event name
  2. Extracts fields according to the lens configuration
  3. Renders each field with the appropriate component (text, JSON tree, etc.)
  4. Automatically parses stringified JSON values

Built-in Lenses

LLM Start Lens

Matches: Events matching /llm_start/i

Displays:

LLM End Lens

Matches: Events matching /llm_end/i

Displays:

Chatbot Lens

Matches: Events matching /chatbot_(invoked|response_generated|error)/i

Displays:

Default Lens

Matches: All events (fallback)

Displays:

Creating Custom Lenses

Edit src/lenses/lensConfig.ts and add your lens to the LENS_REGISTRY:

export const MY_CUSTOM_LENS: Lens = {
  eventPattern: /my_event_name/i, // Regex or string to match event names
  fields: [
    {
      key: "field_name", // Field key from log entry
      display: "Display Name", // Human-readable label
      type: "json-tree", // 'json-tree' or 'text'
      maxInitialDepth: 2, // How many levels to expand initially
    },
    {
      key: "simple_field",
      display: "Simple Field",
      type: "text", // Plain text display
    },
  ],
  priority: 10, // Higher priority = checked first
};

// Add to registry
export const LENS_REGISTRY: Lens[] = [
  MY_CUSTOM_LENS,
  LLM_START_LENS,
  LLM_END_LENS,
  CHATBOT_LENS,
  DEFAULT_LENS,
];

Field Types

json-tree

Displays data as an expandable JSON tree with syntax highlighting.

Features:

{
  key: 'response',
  display: 'Response Data',
  type: 'json-tree',
  maxInitialDepth: 2  // Expand 2 levels by default
}

text

Displays values as plain text in monospace font.

{
  key: 'model',
  display: 'Model Name',
  type: 'text'
}

Wildcard (*)

Show all non-standard fields:

{
  key: '*',
  type: 'json-tree',
  maxInitialDepth: 1
}

JSON Tree Display

The advanced viewer includes an intelligent JSON tree component that adapts its display based on data complexity.

Smart Inline Display

Simple arrays and objects are displayed inline when collapsed to save vertical space:

Simple Arrays (≤5 primitives, <60 chars):

▸ tags: ["python", "debug", "error"]
▸ ids: [1, 2, 3, 4, 5]

Simple Objects (≤3 properties, <60 chars):

▸ metadata: {status: "ok", code: 200, cached: true}
▸ user: {id: 42, name: "John"}

Complex Structures:

▸ response: {15 props}
▸ items: [42 items]

Consistent Layout

All values display with their keys inline:

▸ name: "John Doe"
▸ age: 42
▸ active: true
▸ notes: null

Tree Controls

Context Menus

Right-click on any array or object to:

Visual Features


Smart Value Rendering

The viewer automatically detects “simple” vs “complex” values:

Simple values (<80 chars) → Rendered as compact chips:

[Message Count: 1] [Model: ChatOpenAI]

Complex values (objects/arrays) → Rendered as expandable trees:

▶ Response: {11 props}

Priority System

Lenses with higher priority values are checked first:

Pattern Matching

Event patterns can be:

Regex (recommended):

eventPattern: /llm_(start|end|error)/i; // Case-insensitive, multiple events

String (exact match):

eventPattern: "exact_event_name";

Advanced Examples

Complex Nested Data

export const DATABASE_QUERY_LENS: Lens = {
  eventPattern: /db_query/i,
  fields: [
    { key: "query", display: "SQL Query", type: "text" },
    {
      key: "params",
      display: "Parameters",
      type: "json-tree",
      maxInitialDepth: 3,
    },
    { key: "result", display: "Result", type: "json-tree", maxInitialDepth: 1 },
    { key: "duration_ms", display: "Duration (ms)", type: "text" },
  ],
  priority: 15,
};

Error Handling

export const ERROR_LENS: Lens = {
  eventPattern: /error|exception|failed/i,
  fields: [
    { key: "error_type", display: "Error Type", type: "text" },
    { key: "message", display: "Message", type: "text" },
    {
      key: "stack_trace",
      display: "Stack Trace",
      type: "json-tree",
      maxInitialDepth: 2,
    },
    {
      key: "context",
      display: "Context",
      type: "json-tree",
      maxInitialDepth: 2,
    },
  ],
  priority: 20,
};

Deployment

Building for Production

cd aitrace_viewer
yarn build

This compiles the viewer and outputs to docs/app/ ready for deployment.

GitHub Pages

The built application is automatically deployed to docs/app/ which can be served via GitHub Pages.

Setup

  1. Push changes to GitHub
  2. Go to SettingsPages
  3. Set source to: Deploy from a branch
  4. Select branch: main (or your default)
  5. Select folder: /docs
  6. Save

Your site will be live at: https://<username>.github.io/<repository>/app/

Local Preview

cd aitrace_viewer
yarn build
yarn preview

Opens at http://localhost:4173 serving the built version.

Customizing Base URL

If deploying to a subdirectory, update the base in aitrace_viewer/vite.config.ts:

// For: https://example.com/mytrace/app/
base: "/mytrace/app/";

// For: https://example.com/ (root)
base: "/";

// For: Flexible hosting (default)
base: "./";

Then rebuild with yarn build.


Technology Stack

Advanced Viewer

Component Library Purpose
Framework Preact Lightweight React alternative
Build Tool Vite Fast dev server + bundling
Language TypeScript Type safety
Icons Bootstrap Icons UI icons

Simple Viewer

Component Technology Purpose
Frontend Vanilla JavaScript No framework overhead
Styling CSS Custom, responsive design
Backend FastAPI REST API server
Database SQLite Log storage

Tips & Best Practices

For Developers

  1. Start with maxInitialDepth=1 for large objects to avoid visual clutter
  2. Use text type for simple values (IDs, counts, short strings)
  3. Higher priority for specific patterns to override general ones
  4. Test with real data to adjust expansion depth
  5. Skip standard fields (timestamp, event, trace_id, etc.) - they’re handled automatically

For Users

  1. Use samples to learn the interface
  2. Right-click nodes for context menus
  3. Collapse deep trees to see the big picture
  4. Export logs to files for sharing/archival
  5. View in browser - no server installation required (advanced viewer)

Troubleshooting

Lens not matching?

Data not expanding?

Performance issues with large trees?

Assets not loading (GitHub Pages)?

Sample files not loading?


Future Enhancements

Possible additions:


See Also