@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
296 lines • 8.98 kB
JavaScript
import { generateStreamId, L0RecordedEventTypes } from "../types/events";
export class InMemoryEventStore {
streams = new Map();
snapshots = new Map();
async append(streamId, event) {
let events = this.streams.get(streamId);
if (!events) {
events = [];
this.streams.set(streamId, events);
}
const envelope = {
streamId,
seq: events.length,
event,
};
events.push(envelope);
}
async getEvents(streamId) {
return this.streams.get(streamId) ?? [];
}
async exists(streamId) {
return this.streams.has(streamId);
}
async getLastEvent(streamId) {
const events = this.streams.get(streamId);
if (!events || events.length === 0) {
return null;
}
return events[events.length - 1];
}
async getEventsAfter(streamId, afterSeq) {
const events = this.streams.get(streamId);
if (!events) {
return [];
}
return events.filter((e) => e.seq > afterSeq);
}
async delete(streamId) {
this.streams.delete(streamId);
this.snapshots.delete(streamId);
}
async listStreams() {
return Array.from(this.streams.keys());
}
async saveSnapshot(snapshot) {
let snapshots = this.snapshots.get(snapshot.streamId);
if (!snapshots) {
snapshots = [];
this.snapshots.set(snapshot.streamId, snapshots);
}
snapshots.push(snapshot);
}
async getSnapshot(streamId) {
const snapshots = this.snapshots.get(streamId);
if (!snapshots || snapshots.length === 0) {
return null;
}
return snapshots[snapshots.length - 1];
}
async getSnapshotBefore(streamId, seq) {
const snapshots = this.snapshots.get(streamId);
if (!snapshots || snapshots.length === 0) {
return null;
}
let best = null;
for (const snapshot of snapshots) {
if (snapshot.seq <= seq) {
if (!best || snapshot.seq > best.seq) {
best = snapshot;
}
}
}
return best;
}
clear() {
this.streams.clear();
this.snapshots.clear();
}
getTotalEventCount() {
let count = 0;
for (const events of this.streams.values()) {
count += events.length;
}
return count;
}
getStreamCount() {
return this.streams.size;
}
}
export class L0EventRecorder {
streamId;
eventStore;
seq = 0;
constructor(eventStore, streamId) {
this.eventStore = eventStore;
this.streamId = streamId ?? generateStreamId();
}
getStreamId() {
return this.streamId;
}
getSeq() {
return this.seq;
}
async record(event) {
await this.eventStore.append(this.streamId, event);
this.seq++;
}
async recordStart(options) {
await this.record({
type: L0RecordedEventTypes.START,
ts: Date.now(),
options,
});
}
async recordToken(value, index) {
await this.record({
type: L0RecordedEventTypes.TOKEN,
ts: Date.now(),
value,
index,
});
}
async recordCheckpoint(at, content) {
await this.record({
type: L0RecordedEventTypes.CHECKPOINT,
ts: Date.now(),
at,
content,
});
}
async recordGuardrail(at, result) {
await this.record({
type: L0RecordedEventTypes.GUARDRAIL,
ts: Date.now(),
at,
result,
});
}
async recordDrift(at, result) {
await this.record({
type: L0RecordedEventTypes.DRIFT,
ts: Date.now(),
at,
result,
});
}
async recordRetry(reason, attempt, countsTowardLimit) {
await this.record({
type: L0RecordedEventTypes.RETRY,
ts: Date.now(),
reason,
attempt,
countsTowardLimit,
});
}
async recordFallback(to) {
await this.record({
type: L0RecordedEventTypes.FALLBACK,
ts: Date.now(),
to,
});
}
async recordContinuation(checkpoint, at) {
await this.record({
type: L0RecordedEventTypes.CONTINUATION,
ts: Date.now(),
checkpoint,
at,
});
}
async recordComplete(content, tokenCount) {
await this.record({
type: L0RecordedEventTypes.COMPLETE,
ts: Date.now(),
content,
tokenCount,
});
}
async recordError(error, failureType, recoveryStrategy, policy) {
await this.record({
type: L0RecordedEventTypes.ERROR,
ts: Date.now(),
error,
failureType,
recoveryStrategy,
policy,
});
}
}
export class L0EventReplayer {
eventStore;
constructor(eventStore) {
this.eventStore = eventStore;
}
async *replay(streamId, options = {}) {
const { speed = 0, fromSeq = 0, toSeq = Infinity } = options;
const events = await this.eventStore.getEvents(streamId);
let lastTs = null;
for (const envelope of events) {
if (envelope.seq < fromSeq)
continue;
if (envelope.seq > toSeq)
break;
if (speed > 0 && lastTs !== null) {
const delay = (envelope.event.ts - lastTs) / speed;
if (delay > 0) {
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
lastTs = envelope.event.ts;
yield envelope;
}
}
async replayToState(streamId) {
const state = {
content: "",
tokenCount: 0,
checkpoint: "",
violations: [],
driftDetected: false,
retryAttempts: 0,
networkRetryCount: 0,
fallbackIndex: 0,
completed: false,
error: null,
startTs: 0,
endTs: 0,
};
const events = await this.eventStore.getEvents(streamId);
for (const envelope of events) {
const event = envelope.event;
switch (event.type) {
case L0RecordedEventTypes.START:
state.startTs = event.ts;
break;
case L0RecordedEventTypes.TOKEN:
state.content += event.value;
state.tokenCount = event.index + 1;
break;
case L0RecordedEventTypes.CHECKPOINT:
state.checkpoint = event.content;
break;
case L0RecordedEventTypes.GUARDRAIL:
state.violations.push(...event.result.violations);
break;
case L0RecordedEventTypes.DRIFT:
if (event.result.detected) {
state.driftDetected = true;
}
break;
case L0RecordedEventTypes.RETRY:
if (event.countsTowardLimit) {
state.retryAttempts++;
}
else {
state.networkRetryCount++;
}
break;
case L0RecordedEventTypes.FALLBACK:
state.fallbackIndex = event.to;
break;
case L0RecordedEventTypes.CONTINUATION:
state.content = event.checkpoint;
break;
case L0RecordedEventTypes.COMPLETE:
state.completed = true;
state.content = event.content;
state.tokenCount = event.tokenCount;
state.endTs = event.ts;
break;
case L0RecordedEventTypes.ERROR:
state.error = event.error;
state.endTs = event.ts;
break;
}
}
return state;
}
async *replayTokens(streamId, options = {}) {
for await (const envelope of this.replay(streamId, options)) {
if (envelope.event.type === L0RecordedEventTypes.TOKEN) {
yield envelope.event.value;
}
}
}
}
export function createInMemoryEventStore() {
return new InMemoryEventStore();
}
export function createEventRecorder(eventStore, streamId) {
return new L0EventRecorder(eventStore, streamId);
}
export function createEventReplayer(eventStore) {
return new L0EventReplayer(eventStore);
}
//# sourceMappingURL=eventStore.js.map