@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
417 lines • 16.1 kB
JavaScript
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