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:
| 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);
}
Related Pages¶
- TraceStore Interface -- the store that provides trace data
- FirestoreTraceStore -- production store for large exports
- CostCalculator -- cost data included in exported records