feat: add diagnostics events and otel exporter

This commit is contained in:
Peter Steinberger
2026-01-20 18:56:10 +00:00
parent b1f086b536
commit 5c4079f66c
14 changed files with 1030 additions and 13 deletions

View File

@@ -0,0 +1,28 @@
import { describe, expect, test } from "vitest";
import {
emitDiagnosticEvent,
onDiagnosticEvent,
resetDiagnosticEventsForTest,
} from "./diagnostic-events.js";
describe("diagnostic-events", () => {
test("emits monotonic seq", async () => {
resetDiagnosticEventsForTest();
const seqs: number[] = [];
const stop = onDiagnosticEvent((evt) => seqs.push(evt.seq));
emitDiagnosticEvent({
type: "model.usage",
usage: { total: 1 },
});
emitDiagnosticEvent({
type: "model.usage",
usage: { total: 2 },
});
stop();
expect(seqs).toEqual([1, 2]);
});
});

View File

@@ -0,0 +1,60 @@
import type { ClawdbotConfig } from "../config/config.js";
export type DiagnosticUsageEvent = {
type: "model.usage";
ts: number;
seq: number;
sessionKey?: string;
sessionId?: string;
channel?: string;
provider?: string;
model?: string;
usage: {
input?: number;
output?: number;
cacheRead?: number;
cacheWrite?: number;
promptTokens?: number;
total?: number;
};
context?: {
limit?: number;
used?: number;
};
costUsd?: number;
durationMs?: number;
};
export type DiagnosticEventPayload = DiagnosticUsageEvent;
let seq = 0;
const listeners = new Set<(evt: DiagnosticEventPayload) => void>();
export function isDiagnosticsEnabled(config?: ClawdbotConfig): boolean {
return config?.diagnostics?.enabled === true;
}
export function emitDiagnosticEvent(event: Omit<DiagnosticEventPayload, "seq" | "ts">) {
const enriched: DiagnosticEventPayload = {
...event,
seq: (seq += 1),
ts: Date.now(),
};
for (const listener of listeners) {
try {
listener(enriched);
} catch {
// Ignore listener failures.
}
}
}
export function onDiagnosticEvent(listener: (evt: DiagnosticEventPayload) => void): () => void {
listeners.add(listener);
return () => listeners.delete(listener);
}
export function resetDiagnosticEventsForTest(): void {
seq = 0;
listeners.clear();
}