UNPKG

@ai2070/l0

Version:

L0: The Missing Reliability Substrate for AI

403 lines (402 loc) 15.4 kB
import { RETRY_DEFAULTS, ERROR_TYPE_DELAY_DEFAULTS, ErrorCategory } from "../types/retry"; 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" }; 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; } } class L0Error extends Error { /** * Error code for programmatic handling */ code; /** * Error context with recovery information */ context; /** * Timestamp when error occurred */ 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 error category for routing decisions */ get category() { return getErrorCategory(this.code); } /** * Check if error has a checkpoint that can be used for continuation. * This indicates whether there's valid checkpoint content to resume from. */ get hasCheckpoint() { return this.context.checkpoint !== void 0 && this.context.checkpoint.length > 0; } /** * @deprecated Use hasCheckpoint instead. Will be removed in v2.0. */ get isRecoverable() { return this.hasCheckpoint; } /** * Get checkpoint content for recovery */ getCheckpoint() { return this.context.checkpoint; } /** * Create a descriptive string with context */ toDetailedString() { const parts = [this.message]; if (this.context.tokenCount !== void 0) { parts.push(`Tokens: ${this.context.tokenCount}`); } if (this.context.modelRetryCount !== void 0) { parts.push(`Retries: ${this.context.modelRetryCount}`); } if (this.context.fallbackIndex !== void 0 && 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(" | "); } /** * Serialize error for logging/transport */ 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 }; } } 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 : void 0; } var NetworkErrorType = /* @__PURE__ */ ((NetworkErrorType2) => { NetworkErrorType2["CONNECTION_DROPPED"] = "connection_dropped"; NetworkErrorType2["FETCH_ERROR"] = "fetch_error"; NetworkErrorType2["ECONNRESET"] = "econnreset"; NetworkErrorType2["ECONNREFUSED"] = "econnrefused"; NetworkErrorType2["SSE_ABORTED"] = "sse_aborted"; NetworkErrorType2["NO_BYTES"] = "no_bytes"; NetworkErrorType2["PARTIAL_CHUNKS"] = "partial_chunks"; NetworkErrorType2["RUNTIME_KILLED"] = "runtime_killed"; NetworkErrorType2["BACKGROUND_THROTTLE"] = "background_throttle"; NetworkErrorType2["DNS_ERROR"] = "dns_error"; NetworkErrorType2["SSL_ERROR"] = "ssl_error"; NetworkErrorType2["TIMEOUT"] = "timeout"; NetworkErrorType2["UNKNOWN"] = "unknown"; return NetworkErrorType2; })(NetworkErrorType || {}); 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"); } 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")); } function isECONNRESET(error) { const message = error.message.toLowerCase(); return message.includes("econnreset") || message.includes("connection reset by peer") || getErrorCode(error) === "ECONNRESET"; } function isECONNREFUSED(error) { const message = error.message.toLowerCase(); return message.includes("econnrefused") || message.includes("connection refused") || getErrorCode(error) === "ECONNREFUSED"; } 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"; } 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"); } 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"); } 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"); } 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"); } 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"; } 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"); } 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"; } function analyzeNetworkError(error) { if (isConnectionDropped(error)) { return { type: "connection_dropped" /* CONNECTION_DROPPED */, retryable: true, countsTowardLimit: false, suggestion: "Retry with exponential backoff - connection was interrupted" }; } if (isFetchTypeError(error)) { return { type: "fetch_error" /* FETCH_ERROR */, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - fetch() failed to initiate" }; } if (isECONNRESET(error)) { return { type: "econnreset" /* ECONNRESET */, retryable: true, countsTowardLimit: false, suggestion: "Retry with backoff - connection was reset by peer" }; } if (isECONNREFUSED(error)) { return { type: "econnrefused" /* 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: "sse_aborted" /* SSE_ABORTED */, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - SSE stream was aborted" }; } if (isNoBytes(error)) { return { type: "no_bytes" /* 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: "partial_chunks" /* PARTIAL_CHUNKS */, retryable: true, countsTowardLimit: false, suggestion: "Retry immediately - received incomplete data", context: { possibleCause: "Connection closed mid-stream" } }; } if (isRuntimeKilled(error)) { return { type: "runtime_killed" /* 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: "background_throttle" /* 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: "dns_error" /* 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: "ssl_error" /* 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: "timeout" /* TIMEOUT */, retryable: true, countsTowardLimit: false, suggestion: "Retry with longer timeout - request timed out" }; } return { type: "unknown" /* UNKNOWN */, retryable: true, countsTowardLimit: false, suggestion: "Retry with caution - unknown network error" }; } 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); } function describeNetworkError(error) { const analysis = analyzeNetworkError(error); let description = `Network error: ${analysis.type}`; if (analysis.context?.possibleCause) { description += ` (${analysis.context.possibleCause})`; } return description; } 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; } 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; } function suggestRetryDelay(error, attempt, customDelays, maxDelay = RETRY_DEFAULTS.networkMaxDelay) { const analysis = analyzeNetworkError(error); const defaultDelays = { ["connection_dropped" /* CONNECTION_DROPPED */]: ERROR_TYPE_DELAY_DEFAULTS.connectionDropped, ["fetch_error" /* FETCH_ERROR */]: ERROR_TYPE_DELAY_DEFAULTS.fetchError, ["econnreset" /* ECONNRESET */]: ERROR_TYPE_DELAY_DEFAULTS.econnreset, ["econnrefused" /* ECONNREFUSED */]: ERROR_TYPE_DELAY_DEFAULTS.econnrefused, ["sse_aborted" /* SSE_ABORTED */]: ERROR_TYPE_DELAY_DEFAULTS.sseAborted, ["no_bytes" /* NO_BYTES */]: ERROR_TYPE_DELAY_DEFAULTS.noBytes, ["partial_chunks" /* PARTIAL_CHUNKS */]: ERROR_TYPE_DELAY_DEFAULTS.partialChunks, ["runtime_killed" /* RUNTIME_KILLED */]: ERROR_TYPE_DELAY_DEFAULTS.runtimeKilled, ["background_throttle" /* BACKGROUND_THROTTLE */]: ERROR_TYPE_DELAY_DEFAULTS.backgroundThrottle, ["dns_error" /* DNS_ERROR */]: ERROR_TYPE_DELAY_DEFAULTS.dnsError, ["ssl_error" /* SSL_ERROR */]: 0, // Don't retry SSL errors ["timeout" /* TIMEOUT */]: ERROR_TYPE_DELAY_DEFAULTS.timeout, ["unknown" /* 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); } export { ErrorCategory, L0Error, L0ErrorCodes, NetworkErrorType, analyzeNetworkError, createNetworkError, describeNetworkError, getErrorCategory, isBackgroundThrottle, isConnectionDropped, isDNSError, isECONNREFUSED, isECONNRESET, isFetchTypeError, isL0Error, isNetworkError, isNoBytes, isPartialChunks, isRuntimeKilled, isSSEAborted, isSSLError, isStreamInterrupted, isTimeoutError, suggestRetryDelay }; //# sourceMappingURL=errors.js.map