UNPKG

@ai2070/l0

Version:

L0: The Missing Reliability Substrate for AI

288 lines 10.4 kB
import { l0 } from "./runtime/l0"; import { chunkDocument, estimateTokenCount, mergeChunks, } from "./utils/chunking"; import { RETRY_DEFAULTS } from "./types/retry"; const DEFAULT_OPTIONS = { size: 2000, overlap: 200, strategy: "token", estimateTokens: estimateTokenCount, preserveParagraphs: true, preserveSentences: false, metadata: {}, }; export class DocumentWindowImpl { document; options; chunks; _currentIndex = 0; constructor(document, options = {}) { this.document = document; this.options = { ...DEFAULT_OPTIONS, ...options }; this.chunks = chunkDocument(document, this.options); if (this.chunks.length === 0) { throw new Error("Document resulted in zero chunks"); } } get totalChunks() { return this.chunks.length; } get currentIndex() { return this._currentIndex; } get(index) { if (index < 0 || index >= this.chunks.length) { return null; } return this.chunks[index] ?? null; } current() { return this.get(this._currentIndex); } next() { if (this._currentIndex < this.chunks.length - 1) { this._currentIndex++; return this.current(); } return null; } prev() { if (this._currentIndex > 0) { this._currentIndex--; return this.current(); } return null; } jump(index) { if (index < 0 || index >= this.chunks.length) { return null; } this._currentIndex = index; return this.current(); } reset() { this._currentIndex = 0; return this.current(); } getAllChunks() { return [...this.chunks]; } getRange(start, end) { const validStart = Math.max(0, start); const validEnd = Math.min(this.chunks.length, end); return this.chunks.slice(validStart, validEnd); } hasNext() { return this._currentIndex < this.chunks.length - 1; } hasPrev() { return this._currentIndex > 0; } async processAll(processFn) { return this.processParallel(processFn); } async processSequential(processFn) { const results = []; for (const chunk of this.chunks) { const startTime = Date.now(); try { const options = processFn(chunk); const result = await l0(options); for await (const _event of result.stream) { } results.push({ chunk, result, status: "success", duration: Date.now() - startTime, }); } catch (error) { results.push({ chunk, result: undefined, status: "error", error: error instanceof Error ? error : new Error(String(error)), duration: Date.now() - startTime, }); } } return results; } async processParallel(processFn, options = {}) { const { concurrency = 5 } = options; const results = new Array(this.chunks.length); const queue = [...this.chunks]; let activeCount = 0; let index = 0; return new Promise((resolve, _reject) => { const processNext = () => { while (activeCount < concurrency && queue.length > 0) { const chunk = queue.shift(); const chunkIndex = index++; activeCount++; const startTime = Date.now(); (async () => { try { const l0Options = processFn(chunk); const result = await l0(l0Options); for await (const _event of result.stream) { } results[chunkIndex] = { chunk, result, status: "success", duration: Date.now() - startTime, }; } catch (error) { results[chunkIndex] = { chunk, result: undefined, status: "error", error: error instanceof Error ? error : new Error(String(error)), duration: Date.now() - startTime, }; } finally { activeCount--; if (queue.length > 0) { processNext(); } else if (activeCount === 0) { resolve(results); } } })(); } }; processNext(); }); } getStats() { const totalChars = this.document.length; const totalTokens = this.options.estimateTokens(this.document); const avgChunkSize = this.chunks.reduce((sum, c) => sum + c.charCount, 0) / this.chunks.length; const avgChunkTokens = this.chunks.reduce((sum, c) => sum + c.tokenCount, 0) / this.chunks.length; return { totalChunks: this.chunks.length, totalChars, totalTokens, avgChunkSize: Math.round(avgChunkSize), avgChunkTokens: Math.round(avgChunkTokens), overlapSize: this.options.overlap, strategy: this.options.strategy, }; } getContext(index, options = {}) { const { before = 0, after = 0 } = options; const start = Math.max(0, index - before); const end = Math.min(this.chunks.length, index + after + 1); const contextChunks = this.chunks.slice(start, end); return mergeChunks(contextChunks, false); } findChunks(searchText, caseSensitive = false) { const search = caseSensitive ? searchText : searchText.toLowerCase(); return this.chunks.filter((chunk) => { const content = caseSensitive ? chunk.content : chunk.content.toLowerCase(); return content.includes(search); }); } getChunksInRange(startPos, endPos) { return this.chunks.filter((chunk) => (chunk.startPos >= startPos && chunk.startPos < endPos) || (chunk.endPos > startPos && chunk.endPos <= endPos) || (chunk.startPos <= startPos && chunk.endPos >= endPos)); } } export function createWindow(document, options) { return new DocumentWindowImpl(document, options); } export async function processWithWindow(document, processFn, options) { const window = createWindow(document, options); return window.processAll(processFn); } export async function l0WithWindow(options) { const { window, chunkIndex = 0, contextRestoration, ...l0Options } = options; if (!window) { throw new Error("Window is required"); } const chunk = window.get(chunkIndex); if (!chunk) { throw new Error(`Invalid chunk index: ${chunkIndex}`); } const { enabled = true, strategy = "adjacent", maxAttempts = RETRY_DEFAULTS.attempts, onRestore, } = contextRestoration || {}; let currentChunkIndex = chunkIndex; let attempts = 0; while (attempts <= maxAttempts) { try { const result = await l0(l0Options); if (result.state.driftDetected && enabled && attempts < maxAttempts) { let nextChunkIndex = null; switch (strategy) { case "adjacent": if (window.hasNext()) { nextChunkIndex = currentChunkIndex + 1; } else if (currentChunkIndex > 0) { nextChunkIndex = currentChunkIndex - 1; } break; case "overlap": if (window.hasNext()) { nextChunkIndex = currentChunkIndex + 1; } break; case "full": if (window.hasNext()) { nextChunkIndex = currentChunkIndex + 1; } else if (currentChunkIndex > 0) { nextChunkIndex = currentChunkIndex - 1; } break; } if (nextChunkIndex !== null) { currentChunkIndex = nextChunkIndex; attempts++; if (onRestore) { onRestore(chunkIndex, nextChunkIndex); } continue; } } return result; } catch (error) { if (attempts >= maxAttempts) { throw error; } attempts++; } } throw new Error("Context restoration failed after max attempts"); } export function mergeResults(results, separator = "\n\n") { return results .filter((r) => r.status === "success" && r.result?.state?.content) .map((r) => r.result.state.content) .join(separator); } export function getProcessingStats(results) { const total = results.length; const successful = results.filter((r) => r.status === "success").length; const failed = results.filter((r) => r.status === "error").length; const successRate = total > 0 ? (successful / total) * 100 : 0; const totalDuration = results.reduce((sum, r) => sum + r.duration, 0); const avgDuration = total > 0 ? totalDuration / total : 0; return { total, successful, failed, successRate, avgDuration: Math.round(avgDuration), totalDuration, }; } //# sourceMappingURL=window.js.map