DevTools for AI Applications
AI Flight Recorder is an open-source developer tool for recording, replaying, and inspecting every interaction in an AI application — prompts, streamed tokens, tool calls, latency, and cost — all in one place.
Instead of piecing together console logs after the fact, you drop in a one-line SDK wrapper and get a full DevTools-style timeline you can , rewind, and hand off to a teammate as a .flight
file.
Session recording: capture every prompt, token, tool call, and completion as a structured event streamStreaming replay: watch a session play back in real time with speed controls (0.25×–8×)Timeline & Waterfall: visualize the full request lifecycle including parallel tool calls and streaming latencyCost Analysis: break down token usage and estimated spend per sessionSearch & Filter: filter events by type or keyword across the full timelineProvider Adapters: one-line wrappers for OpenAI, Anthropic, and Google Gemini (streaming and non-streaming)share a session as a portable file another developer can replay locally.flight
Export/Import:Plugin System: hook into the recorder lifecycle with custom observersTransport System: plug in any storage backend (in-memory, filesystem, your own API)OpenTelemetry Export: convert any session to an OTLP trace payload for ingestion into Jaeger, Grafana Tempo, Honeycomb, or any OTel-compatible backend (toOtlp
from@ai-flight-recorder/sdk
)
ai-flight-recorder/
├── apps/
│ ├── devtools/ Next.js DevTools application
│ ├── docs/ Starlight documentation site
│ └── vscode/ VS Code extension — custom editor for .flight files
├── packages/
│ ├── core/ Domain model — events, session, recorder, replay engine
│ ├── sdk/ Developer-facing API — FlightRecorder, adapters, plugins, transports
│ ├── ui/ Shared React components (future)
│ └── types/ Shared TypeScript types (future)
├── scripts/
│ └── smoke.ts SDK integration smoke test
└── examples/
├── nextjs-chat/ Full-stack chat app — OpenAI streaming + .flight export
├── node-anthropic/ Node.js example — Anthropic + FileTransport
└── node-gemini/ Node.js example — Google Gemini + FileTransport
- Node.js 18+
- pnpm 10+
pnpm install
pnpm dev
Open http://localhost:3000. The app loads with two demo sessions so you can explore the UI immediately — no API keys required.
pnpm smoke
Exercises recording, plugins, transport, serialization, and replay end-to-end. All 40 assertions should pass.
import { FlightRecorder } from "@ai-flight-recorder/sdk";
const fr = new FlightRecorder();
const session = fr.startSession({ label: "my-chat" });
fr.record({
type: "prompt",
model: "gpt-4o",
prompt: "What is the capital of France?",
});
fr.record({
type: "completion",
response: "Paris.",
finishReason: "stop",
totalTokens: 18,
});
const ended = fr.endSession();
Drop-in wrappers that intercept the provider client and record every call automatically.
OpenAI
import OpenAI from "openai";
import { FlightRecorder, wrapOpenAI } from "@ai-flight-recorder/sdk";
const fr = new FlightRecorder();
const openai = wrapOpenAI(new OpenAI(), fr.recorder);
fr.startSession({ label: "chat" });
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});
fr.endSession();
Anthropic
import Anthropic from "@anthropic-ai/sdk";
import { FlightRecorder, wrapAnthropic } from "@ai-flight-recorder/sdk";
const fr = new FlightRecorder();
const client = wrapAnthropic(new Anthropic(), fr.recorder);
fr.startSession({ label: "claude-chat" });
const message = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});
fr.endSession();
Google Gemini
import { GoogleGenerativeAI } from "@google/generative-ai";
import { FlightRecorder, wrapGeminiModel } from "@ai-flight-recorder/sdk";
const fr = new FlightRecorder();
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);
const model = wrapGeminiModel(
genAI.getGenerativeModel({ model: "gemini-1.5-pro" }),
fr.recorder,
);
fr.startSession({ label: "gemini-chat" });
const result = await model.generateContent("Hello");
fr.endSession();
All three adapters support streaming. Wrap your existing client and all calls are recorded automatically.
import { FlightRecorder, ConsoleLogPlugin } from "@ai-flight-recorder/sdk";
const fr = new FlightRecorder({
plugins: [
new ConsoleLogPlugin({ logEvents: true, logSummary: true }),
// Inline plugin
{
name: "my-plugin",
onSessionStart: (session) => console.log("Started:", session.id),
onEvent: (event) => myMetrics.record(event),
onSessionEnd: (session) => alerting.flush(session),
},
],
});
use()
is chainable and checks for duplicate names at registration time:
fr.use(pluginA).use(pluginB);
js
import { FlightRecorder, InMemoryTransport } from "@ai-flight-recorder/sdk";
const transport = new InMemoryTransport();
const fr = new FlightRecorder({ transport });
fr.startSession();
// ... record events ...
fr.endSession(); // automatically saves to transport
const sessions = transport.getAll();
Node.js filesystem transport:
import { FlightRecorder } from "@ai-flight-recorder/sdk";
import { FileTransport } from "@ai-flight-recorder/sdk/node";
const transport = new FileTransport("./recordings");
const fr = new FlightRecorder({ transport });
fr.startSession({ label: "my-session" });
// ... record events ...
fr.endSession();
// saves to ./recordings/<sessionId>.flight
const sessions = transport.loadAll();
python
import type { Transport } from "@ai-flight-recorder/sdk";
class MyApiTransport implements Transport {
async save(session) {
await fetch("/api/sessions", {
method: "POST",
body: JSON.stringify(session),
});
}
}
const fr = new FlightRecorder({ transport: new MyApiTransport() });
Sessions can be exported as portable .flight
files (JSON with a version envelope):
{
"version": "1",
"exportedAt": 1721484000000,
"session": {
"id": "...",
"label": "bug-report-123",
"status": "ended",
"startedAt": 1721484000000,
"endedAt": 1721484060000,
"events": [ ... ]
}
}
Export from the DevTools UI: click the Export button in the toolbar while a session is active.
Import into the DevTools UI: click Import and select a .flight
file. The session is added to the session list and becomes the active session immediately.
Programmatic export/import:
import { serializeSession, deserializeSession } from "@ai-flight-recorder/sdk";
import { writeFileSync, readFileSync } from "node:fs";
// Export
writeFileSync("bug-123.flight", serializeSession(endedSession));
// Import
const session = deserializeSession(readFileSync("bug-123.flight", "utf-8"));
The DevTools app (apps/devtools
) is a Next.js application providing a visual interface for recorded sessions.
Tabs:
Timeline: chronological event list with type badges, descriptions, and timing offsetsWaterfall: visual latency breakdown showing streaming spans and tool call durationsCost Analysis: token usage breakdown and estimated spend per request
Replay:
- Click "Replay Session" to enter replay mode
- Speed controls: 0.25×, 0.5×, 1×, 2×, 4×, 8×
- Seek bar for jumping to any point in the session
- Token stream assembles in real time as tokens replay
Search:
- Filter by event type using the chip row (Prompt, Token, Tool, Result, Completion, Error)
- Text search across event content
examples/nextjs-chat
is a minimal Next.js app showing a full end-to-end integration — streaming chat with GPT-4o-mini, automatic session recording, and .flight
export.
cd examples/nextjs-chat
cp .env.example .env.local
Edit .env.local
and add your OpenAI API key:
OPENAI_API_KEY=sk-...
pnpm dev
Open http://localhost:3000. Chat with the assistant, then click Export .flight in the header to download your session.
Open the DevTools app (pnpm dev
from the repo root), click Import in the toolbar, and select the .flight
file. Your session loads instantly — timeline, waterfall, cost breakdown, and full streaming replay.
The example wires up three things from the SDK:
FlightRecorder
: starts a session per requestwrapOpenAI
: intercepts the OpenAI client and records every prompt, token, and completion automaticallyserializeSession
: serializes the ended session to JSON for download
To use Anthropic or Gemini instead, swap wrapOpenAI
for wrapAnthropic
or wrapGeminiModel
in src/app/api/chat/route.ts
.
pnpm build
pnpm dev
pnpm typecheck
pnpm lint
pnpm smoke
-
Add the type literal to
packages/core/src/events/EventType.ts -
Create the interface in
packages/core/src/events/YourEvent.ts
extendingBaseEvent
- Add it to the
AIEvent
union inpackages/core/src/events/AIEvent.ts
-
Export it from
packages/core/src/events/index.ts -
Add a case to
eventMeta.ts
in the DevTools app for display metadata
Implement the Plugin
interface from @ai-flight-recorder/core
:
import type { Plugin, AIEvent, Session } from "@ai-flight-recorder/sdk";
export class MyPlugin implements Plugin {
readonly name = "my-plugin";
onSessionStart(session: Session) { ... }
onEvent(event: AIEvent) { ... }
onSessionEnd(session: Session) { ... }
}
This project is licensed under the MIT License - see the LICENSE file for details.