UNPKG

@ai2070/l0

Version:

L0: The Missing Reliability Substrate for AI

296 lines 8.98 kB
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