@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
403 lines (402 loc) • 15.4 kB
JavaScript
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