Skip to content

JSONL Export

The export module reads traces from any TraceStore and produces newline-delimited JSON (JSONL) records. Each line is a self-contained JSON object containing the full Trace, its Span array, and the ConfigSnapshot (if available). The JSONL format is compatible with import into LLM eval frameworks such as Braintrust, Langfuse, and Arize Phoenix.

Import

import {
  exportTracesAsJsonl,
  exportTracesToJsonlString,
  TraceExportRecord,
} from "@modernpath/agent-framework";

Functions

exportTracesAsJsonl

async function* exportTracesAsJsonl(
  store: TraceStore,
  filter?: TraceFilter,
): AsyncGenerator<string>

Exports traces as an async generator of JSONL lines. Each yielded string is one JSON object followed by a newline character. Handles pagination internally -- fetches all matching traces regardless of the store's page size.

Parameter Type Description
store TraceStore Any TraceStore implementation (InMemory, Firestore, etc.).
filter TraceFilter Optional filter for selecting traces. Supports all TraceFilter fields including limit for total export cap.

exportTracesToJsonlString

async function exportTracesToJsonlString(
  store: TraceStore,
  filter?: TraceFilter,
): Promise<string>

Convenience function that collects all JSONL lines into a single string. Suitable for small datasets. For large exports, use the async generator version.

TraceExportRecord

Each JSONL line deserializes to a TraceExportRecord:

interface TraceExportRecord {
  trace: Trace;
  spans: Span[];
  configSnapshot?: ConfigSnapshot;
}
Field Type Description
trace Trace The full trace document with input, output, status, tokens, cost, tags, and scores.
spans Span[] All spans for this trace, ordered by startedAt.
configSnapshot ConfigSnapshot The config snapshot referenced by the trace (if configSnapshotId is set).

Config snapshots are cached during export to avoid re-fetching when multiple traces reference the same snapshot.

JSONL Format

Each line is a valid JSON object. Example:

{"trace":{"schemaVersion":1,"traceId":"abc-123","agentName":"IncidentResolver","agentVersion":"2.1.0","input":{"prompt":"Why is building cold?","parameters":{}},"output":{"success":true,"data":{"answer":"..."}},"status":"completed","userId":1,"auditingId":100,"startedAt":"2025-01-15T10:30:00.000Z","completedAt":"2025-01-15T10:30:02.500Z","durationMs":2500,"tokens":{"input":1500,"output":800,"total":2300},"cost":{"total":0.000705,"currency":"USD"},"tags":["production"]},"spans":[{"schemaVersion":1,"spanId":"span-1","traceId":"abc-123","kind":"llm","name":"gemini.generateContent","status":"ok","startedAt":"2025-01-15T10:30:00.100Z","durationMs":1200}]}

Code Example

Streaming Export to File

import { exportTracesAsJsonl } from "@modernpath/agent-framework";
import * as fs from "node:fs";

const writeStream = fs.createWriteStream("traces.jsonl");

for await (const line of exportTracesAsJsonl(traceStore, {
  agentName: "IncidentResolver",
  after: new Date("2025-01-01"),
  before: new Date("2025-02-01"),
})) {
  writeStream.write(line);
}

writeStream.end();
console.log("Export complete");

Export with Limit

import { exportTracesToJsonlString } from "@modernpath/agent-framework";

// Export the 100 most recent traces
const jsonl = await exportTracesToJsonlString(traceStore, {
  limit: 100,
});

fs.writeFileSync("recent-traces.jsonl", jsonl);

HTTP Endpoint for Export

app.get("/api/traces/export", async (req, res) => {
  res.setHeader("Content-Type", "application/x-ndjson");
  res.setHeader("Content-Disposition", "attachment; filename=traces.jsonl");

  for await (const line of exportTracesAsJsonl(traceStore, {
    agentName: req.query.agentName as string,
    limit: parseInt(req.query.limit as string) || 1000,
  })) {
    res.write(line);
  }

  res.end();
});

Import into Eval Framework

import * as fs from "node:fs";
import * as readline from "node:readline";

// Read JSONL file
const rl = readline.createInterface({
  input: fs.createReadStream("traces.jsonl"),
});

for await (const line of rl) {
  const record: TraceExportRecord = JSON.parse(line);

  // Convert to eval framework format
  const evalRow = {
    id: record.trace.traceId,
    input: record.trace.input.prompt,
    output: record.trace.output?.data,
    expected: null, // Add ground truth if available
    scores: record.trace.scores,
    metadata: {
      agentName: record.trace.agentName,
      model: record.configSnapshot?.model,
      durationMs: record.trace.durationMs,
      cost: record.trace.cost?.total,
    },
  };

  // Push to Braintrust, Langfuse, etc.
  await evalClient.insertRow(evalRow);
}