UNPKG

@ai2070/l0

Version:

L0: The Missing Reliability Substrate for AI

417 lines 16.1 kB
import { RETRY_DEFAULTS, ERROR_TYPE_DELAY_DEFAULTS, ErrorCategory, } from "../types/retry"; export { ErrorCategory }; export const L0ErrorCodes = { STREAM_ABORTED: "STREAM_ABORTED", INITIAL_TOKEN_TIMEOUT: "INITIAL_TOKEN_TIMEOUT", INTER_TOKEN_TIMEOUT: "INTER_TOKEN_TIMEOUT", ZERO_OUTPUT: "ZERO_OUTPUT", GUARDRAIL_VIOLATION: "GUARDRAIL_VIOLATION", FATAL_GUARDRAIL_VIOLATION: "FATAL_GUARDRAIL_VIOLATION", INVALID_STREAM: "INVALID_STREAM", ALL_STREAMS_EXHAUSTED: "ALL_STREAMS_EXHAUSTED", NETWORK_ERROR: "NETWORK_ERROR", DRIFT_DETECTED: "DRIFT_DETECTED", ADAPTER_NOT_FOUND: "ADAPTER_NOT_FOUND", FEATURE_NOT_ENABLED: "FEATURE_NOT_ENABLED", }; export function getErrorCategory(code) { switch (code) { case L0ErrorCodes.NETWORK_ERROR: return ErrorCategory.NETWORK; case L0ErrorCodes.INITIAL_TOKEN_TIMEOUT: case L0ErrorCodes.INTER_TOKEN_TIMEOUT: return ErrorCategory.TRANSIENT; case L0ErrorCodes.GUARDRAIL_VIOLATION: case L0ErrorCodes.FATAL_GUARDRAIL_VIOLATION: case L0ErrorCodes.DRIFT_DETECTED: case L0ErrorCodes.ZERO_OUTPUT: return ErrorCategory.CONTENT; case L0ErrorCodes.INVALID_STREAM: case L0ErrorCodes.ADAPTER_NOT_FOUND: case L0ErrorCodes.FEATURE_NOT_ENABLED: return ErrorCategory.INTERNAL; case L0ErrorCodes.STREAM_ABORTED: case L0ErrorCodes.ALL_STREAMS_EXHAUSTED: default: return ErrorCategory.PROVIDER; } } export class L0Error extends Error { code; context; timestamp; constructor(message, context) { super(message); this.name = "L0Error"; this.code = context.code; this.context = context; this.timestamp = Date.now(); Object.setPrototypeOf(this, L0Error.prototype); } get category() { return getErrorCategory(this.code); } get hasCheckpoint() { return (this.context.checkpoint !== undefined && this.context.checkpoint.length > 0); } get isRecoverable() { return this.hasCheckpoint; } getCheckpoint() { return this.context.checkpoint; } toDetailedString() { const parts = [this.message]; if (this.context.tokenCount !== undefined) { parts.push(`Tokens: ${this.context.tokenCount}`); } if (this.context.modelRetryCount !== undefined) { parts.push(`Retries: ${this.context.modelRetryCount}`); } if (this.context.fallbackIndex !== undefined && this.context.fallbackIndex > 0) { parts.push(`Fallback: ${this.context.fallbackIndex}`); } if (this.context.checkpoint) { parts.push(`Checkpoint: ${this.context.checkpoint.length} chars`); } return parts.join(" | "); } toJSON() { return { name: this.name, code: this.code, category: this.category, message: this.message, timestamp: this.timestamp, hasCheckpoint: this.hasCheckpoint, checkpoint: this.context.checkpoint, tokenCount: this.context.tokenCount, modelRetryCount: this.context.modelRetryCount, networkRetryCount: this.context.networkRetryCount, fallbackIndex: this.context.fallbackIndex, metadata: this.context.metadata, context: this.context.context, }; } } export function isL0Error(error) { return error instanceof L0Error; } function hasErrorCode(error) { return "code" in error && typeof error.code === "string"; } function getErrorCode(error) { return hasErrorCode(error) ? error.code : undefined; } export var NetworkErrorType; (function (NetworkErrorType) { NetworkErrorType["CONNECTION_DROPPED"] = "connection_dropped"; NetworkErrorType["FETCH_ERROR"] = "fetch_error"; NetworkErrorType["ECONNRESET"] = "econnreset"; NetworkErrorType["ECONNREFUSED"] = "econnrefused"; NetworkErrorType["SSE_ABORTED"] = "sse_aborted"; NetworkErrorType["NO_BYTES"] = "no_bytes"; NetworkErrorType["PARTIAL_CHUNKS"] = "partial_chunks"; NetworkErrorType["RUNTIME_KILLED"] = "runtime_killed"; NetworkErrorType["BACKGROUND_THROTTLE"] = "background_throttle"; NetworkErrorType["DNS_ERROR"] = "dns_error"; NetworkErrorType["SSL_ERROR"] = "ssl_error"; NetworkErrorType["TIMEOUT"] = "timeout"; NetworkErrorType["UNKNOWN"] = "unknown"; })(NetworkErrorType || (NetworkErrorType = {})); export function isConnectionDropped(error) { const message = error.message.toLowerCase(); return (message.includes("connection dropped") || message.includes("connection closed") || message.includes("connection lost") || message.includes("connection reset") || message.includes("econnreset") || message.includes("pipe broken") || message.includes("broken pipe")); } export function isFetchTypeError(error) { return (error.name === "TypeError" && (error.message.toLowerCase().includes("fetch") || error.message.toLowerCase().includes("failed to fetch") || error.message.toLowerCase().includes("network request failed"))); } export function isECONNRESET(error) { const message = error.message.toLowerCase(); return (message.includes("econnreset") || message.includes("connection reset by peer") || getErrorCode(error) === "ECONNRESET"); } export function isECONNREFUSED(error) { const message = error.message.toLowerCase(); return (message.includes("econnrefused") || message.includes("connection refused") || getErrorCode(error) === "ECONNREFUSED"); } export function isSSEAborted(error) { const message = error.message.toLowerCase(); return (message.includes("sse") || message.includes("server-sent events") || (message.includes("stream") && message.includes("abort")) || message.includes("stream aborted") || message.includes("eventstream") || error.name === "AbortError"); } export function isNoBytes(error) { const message = error.message.toLowerCase(); return (message.includes("no bytes") || message.includes("empty response") || message.includes("zero bytes") || message.includes("no data received") || message.includes("content-length: 0")); } export function isPartialChunks(error) { const message = error.message.toLowerCase(); return (message.includes("partial chunk") || message.includes("incomplete chunk") || message.includes("truncated") || message.includes("premature close") || message.includes("unexpected end of data") || message.includes("incomplete data")); } export function isRuntimeKilled(error) { const message = error.message.toLowerCase(); return ((message.includes("worker") && message.includes("terminated")) || (message.includes("runtime") && message.includes("killed")) || message.includes("edge runtime") || message.includes("lambda timeout") || message.includes("function timeout") || message.includes("execution timeout") || message.includes("worker died") || message.includes("process exited") || message.includes("sigterm") || message.includes("sigkill")); } export function isBackgroundThrottle(error) { const message = error.message.toLowerCase(); return ((message.includes("background") && message.includes("suspend")) || message.includes("background throttle") || message.includes("tab suspended") || message.includes("page hidden") || message.includes("visibility hidden") || message.includes("inactive tab") || message.includes("background tab")); } export function isDNSError(error) { const message = error.message.toLowerCase(); return (message.includes("dns") || message.includes("enotfound") || message.includes("name resolution") || message.includes("host not found") || message.includes("getaddrinfo") || getErrorCode(error) === "ENOTFOUND"); } export function isSSLError(error) { const message = error.message.toLowerCase(); return (message.includes("ssl") || message.includes("tls") || message.includes("certificate") || message.includes("cert") || message.includes("handshake") || message.includes("self signed") || message.includes("unable to verify")); } export function isTimeoutError(error) { const message = error.message.toLowerCase(); return (error.name === "TimeoutError" || message.includes("timeout") || message.includes("timed out") || message.includes("time out") || message.includes("deadline exceeded") || message.includes("etimedout") || getErrorCode(error) === "ETIMEDOUT"); } export function analyzeNetworkError(error) { if (isConnectionDropped(error)) { return { type: NetworkErrorType.CONNECTION_DROPPED, retryable: true, countsTowardLimit: false, suggestion: "Retry with exponential backoff - connection was interrupted", }; } if (isFetchTypeError(error)) { return { type: NetworkErrorType.FETCH_ERROR, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - fetch() failed to initiate", }; } if (isECONNRESET(error)) { return { type: NetworkErrorType.ECONNRESET, retryable: true, countsTowardLimit: false, suggestion: "Retry with backoff - connection was reset by peer", }; } if (isECONNREFUSED(error)) { return { type: NetworkErrorType.ECONNREFUSED, retryable: true, countsTowardLimit: false, suggestion: "Retry with longer delay - server refused connection", context: { possibleCause: "Server may be down or not accepting connections", }, }; } if (isSSEAborted(error)) { return { type: NetworkErrorType.SSE_ABORTED, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - SSE stream was aborted", }; } if (isNoBytes(error)) { return { type: NetworkErrorType.NO_BYTES, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - server sent no data", context: { possibleCause: "Empty response or connection closed before data sent", }, }; } if (isPartialChunks(error)) { return { type: NetworkErrorType.PARTIAL_CHUNKS, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - received incomplete data", context: { possibleCause: "Connection closed mid-stream", }, }; } if (isRuntimeKilled(error)) { return { type: NetworkErrorType.RUNTIME_KILLED, retryable: true, countsTowardLimit: false, suggestion: "Retry with shorter timeout - runtime was terminated (likely timeout)", context: { possibleCause: "Edge runtime timeout or Lambda timeout - consider breaking into smaller requests", }, }; } if (isBackgroundThrottle(error)) { return { type: NetworkErrorType.BACKGROUND_THROTTLE, retryable: true, countsTowardLimit: false, suggestion: "Retry when page becomes visible - mobile/browser throttling", context: { possibleCause: "Browser suspended network activity for background tab", resolution: "Wait for visibilitychange event", }, }; } if (isDNSError(error)) { return { type: NetworkErrorType.DNS_ERROR, retryable: true, countsTowardLimit: false, suggestion: "Retry with longer delay - DNS lookup failed", context: { possibleCause: "Network connectivity issue or invalid hostname", }, }; } if (isSSLError(error)) { return { type: NetworkErrorType.SSL_ERROR, retryable: false, countsTowardLimit: false, suggestion: "Don't retry - SSL/TLS error (configuration issue)", context: { possibleCause: "Certificate validation failed or SSL handshake error", resolution: "Check server certificate or SSL configuration", }, }; } if (isTimeoutError(error)) { return { type: NetworkErrorType.TIMEOUT, retryable: true, countsTowardLimit: false, suggestion: "Retry with longer timeout - request timed out", }; } return { type: NetworkErrorType.UNKNOWN, retryable: true, countsTowardLimit: false, suggestion: "Retry with caution - unknown network error", }; } export function isNetworkError(error) { return (isConnectionDropped(error) || isFetchTypeError(error) || isECONNRESET(error) || isECONNREFUSED(error) || isSSEAborted(error) || isNoBytes(error) || isPartialChunks(error) || isRuntimeKilled(error) || isBackgroundThrottle(error) || isDNSError(error) || isTimeoutError(error)); } export function describeNetworkError(error) { const analysis = analyzeNetworkError(error); let description = `Network error: ${analysis.type}`; if (analysis.context?.possibleCause) { description += ` (${analysis.context.possibleCause})`; } return description; } export function createNetworkError(originalError, analysis) { const error = new Error(`${originalError.message} [${analysis.type}]`); error.name = originalError.name; error.stack = originalError.stack; error.analysis = analysis; return error; } export function isStreamInterrupted(error, tokenCount) { if (tokenCount > 0 && isNetworkError(error)) { return true; } const message = error.message.toLowerCase(); return (message.includes("stream interrupted") || message.includes("stream closed unexpectedly") || message.includes("connection lost mid-stream") || (isPartialChunks(error) && tokenCount > 0)); } export function suggestRetryDelay(error, attempt, customDelays, maxDelay = RETRY_DEFAULTS.networkMaxDelay) { const analysis = analyzeNetworkError(error); const defaultDelays = { [NetworkErrorType.CONNECTION_DROPPED]: ERROR_TYPE_DELAY_DEFAULTS.connectionDropped, [NetworkErrorType.FETCH_ERROR]: ERROR_TYPE_DELAY_DEFAULTS.fetchError, [NetworkErrorType.ECONNRESET]: ERROR_TYPE_DELAY_DEFAULTS.econnreset, [NetworkErrorType.ECONNREFUSED]: ERROR_TYPE_DELAY_DEFAULTS.econnrefused, [NetworkErrorType.SSE_ABORTED]: ERROR_TYPE_DELAY_DEFAULTS.sseAborted, [NetworkErrorType.NO_BYTES]: ERROR_TYPE_DELAY_DEFAULTS.noBytes, [NetworkErrorType.PARTIAL_CHUNKS]: ERROR_TYPE_DELAY_DEFAULTS.partialChunks, [NetworkErrorType.RUNTIME_KILLED]: ERROR_TYPE_DELAY_DEFAULTS.runtimeKilled, [NetworkErrorType.BACKGROUND_THROTTLE]: ERROR_TYPE_DELAY_DEFAULTS.backgroundThrottle, [NetworkErrorType.DNS_ERROR]: ERROR_TYPE_DELAY_DEFAULTS.dnsError, [NetworkErrorType.SSL_ERROR]: 0, [NetworkErrorType.TIMEOUT]: ERROR_TYPE_DELAY_DEFAULTS.timeout, [NetworkErrorType.UNKNOWN]: ERROR_TYPE_DELAY_DEFAULTS.unknown, }; const baseDelay = customDelays?.[analysis.type] ?? defaultDelays[analysis.type]; if (baseDelay === 0) return 0; return Math.min(baseDelay * Math.pow(2, attempt), maxDelay); } //# sourceMappingURL=errors.js.map