Skip to content

Repository files navigation

TraceGraph

Linear-speed agent trace & trajectory platform

TypeScript Tests License pnpm

LangSmith and Langfuse treat traces as server-fetched CRUD data. Every span expansion, every filter change, every trajectory replay hits the network. TraceGraph inverts this: sync the full trace tree to the browser once, render from a local object pool, and let the server be a sync target — not the UI's source of truth.


Why

Agent traces are structurally different from traditional APM spans:

  • Deep nesting — a single run can produce 50–500 steps (tool calls, LLM invocations, sub-agent delegations, retries, human checkpoints)
  • Heavy payloads — each step carries full prompt/completion text, token counts, tool schemas, intermediate reasoning
  • Temporal scrubbing — evaluators replay trajectories step-by-step; this is a random-access read-heavy pattern, the worst case for fetch-on-demand
  • Decision trace layer — the real value is capturing why the agent chose a path; current platforms don't model this as a first-class primitive
Interaction TraceGraph Target LangSmith Baseline
Open app (warm cache) < 500ms ~2–4s
Open a trace < 50ms ~300–800ms (fetch)
Scrub between steps < 16ms (one frame) ~200–500ms (fetch/step)
Filter trace list < 50ms ~500ms–2s (server query)
Add annotation < 16ms (local) ~300ms (round-trip)
Live step (streaming) < 50ms to render ~1–3s (polling)

Architecture

System Overview

graph TB
    subgraph BROWSER["Browser"]
        UI["React + observer tree"]
        MOBX["MobX Observable Pool"]
        IDB["IndexedDB (trace store)"]
        SYNC_C["Sync Engine (client)"]
        UI <-->|computed/reaction| MOBX
        MOBX <-->|hydrate/persist| IDB
        MOBX <-->|apply delta| SYNC_C
    end

    subgraph SERVER["Server"]
        SYNC_S["Sync Server"]
        INGEST["Ingestion API (OTLP)"]
        PG["PostgreSQL (traces)"]
        REDIS["Redis (event bus)"]
        VEC["pgvector (similar traces)"]
        INGEST --> PG
        INGEST --> REDIS
        REDIS --> SYNC_S
        PG --> VEC
    end

    SYNC_C <-->|"WebSocket (deltas)"| SYNC_S

    subgraph AGENT["Agent Harness"]
        OTEL["OpenTelemetry SDK"]
        SDK["TraceGraph SDK (optional)"]
    end

    OTEL -->|OTLP| INGEST
    SDK -->|"decision_trace, overrides"| INGEST
Loading

Data Model

Data Model

erDiagram
    TraceRun {
        string id PK
        string agent_id
        string session_id
        datetime started_at
        datetime ended_at
        enum status
        json metadata
    }
    Step {
        string id PK
        string trace_id FK
        string parent_step_id FK
        enum step_type
        datetime started_at
        datetime ended_at
        json input
        json output
        json decision_trace
    }
    Annotation {
        string id PK
        string step_id FK
        string content
        string author_id
        datetime created_at
    }
    HumanOverride {
        string id PK
        string step_id FK
        enum override_type
        json original_value
        json new_value
        string reasoning
    }
    Eval {
        string id PK
        string trace_id FK
        string step_id FK
        string grader_type
        float score
        string grader_version
    }

    TraceRun ||--o{ Step : "has"
    Step ||--o{ Step : "parent_step_id"
    Step ||--o{ Annotation : "has"
    Step ||--o{ HumanOverride : "has"
    TraceRun ||--o{ Eval : "has"
    Step ||--o{ Eval : "has"
Loading

Monorepo Structure

tracegraph/
├── packages/
│   ├── data-model/        # MobX observable classes (TraceRun, Step, Annotation…)
│   ├── sync-engine/       # Delta protocol, TracePool, out-of-order buffering
│   ├── ingestion-api/     # OTLP-compatible Express API + Zod schemas
│   └── frontend/          # React + Vite app shell, TraceListStore, TraceDetailStore
├── docs/
│   ├── architecture.md    # Architecture Design Doc
│   └── system-design.md   # System Design Doc
└── assets/                # Rendered diagrams (SVG)

Getting Started

# Requires Node 25+ and pnpm
corepack enable
pnpm install

# Type-check all packages
pnpm -r run typecheck

# Lint all packages
pnpm -r run lint

# Run all tests
pnpm -r run test

46/46 tests pass. TypeScript strict mode. ESLint clean.


Python SDK (planned)

from tracegraph import trace

@trace.run(agent="insurance-claims")
async def process_claim(claim_id: str):
    with trace.step("retrieve_policy", step_type="tool_use") as s:
        policy = await search_policies(claim_id)
        s.record_output(policy)
        s.record_decision_trace(
            alternatives=[("search_claims", 0.3), ("search_policies", 0.7)],
            reasoning="User mentioned 'coverage', routing to policy search"
        )

TODO

  • Sync engine core: IndexedDB store, MobX observable pool, WebSocket delta protocol
  • Data model: TraceRun, Step, Annotation in Postgres + IndexedDB schema
  • Ingestion API: OTLP-compatible endpoint → Postgres + delta broadcast
  • App shell: Vite build, inline CSS, modulepreload, service worker scaffold
  • Trace list view: hydrate from local store, j/k navigation, live status updates
  • Trace detail: timeline scrubber, step expansion, full payload rendering
  • Context window replay view
  • Annotation system: local-first ProseMirror annotations on steps
  • Command palette (⌘K): search traces, steps, commands from local store
  • Keyboard shortcut system (j/k, ←/→, a, e, t)
  • Step-level and run-level eval score ingestion
  • Eval dashboard: aggregate charts, entropy scores, drill-down
  • Human override capture and diff view
  • Decision trace rendering (alternatives, confidence, reasoning)
  • Similar trace detection via pgvector
  • Lazy hydration tuning (10K, 50K, 100K traces)
  • Offline mode: full read + annotate without network
  • Python SDK (@trace.run / trace.step decorators)
  • Animation audit: composited only, < 150ms, asymmetric

License

MIT

About

Local-first agent trace & trajectory platform — Linear speed for agent observability

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages