UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

416 lines (415 loc) • 17.6 kB
/** * Robust Error Handling Utilities for NeuroLink * Provides structured error management for tool execution and system operations */ import { ErrorCategory, ErrorSeverity } from "../constants/enums.js"; import type { StructuredError } from "../types/index.js"; export declare const ERROR_CODES: { readonly TOOL_NOT_FOUND: "TOOL_NOT_FOUND"; readonly TOOL_EXECUTION_FAILED: "TOOL_EXECUTION_FAILED"; readonly TOOL_TIMEOUT: "TOOL_TIMEOUT"; readonly TOOL_VALIDATION_FAILED: "TOOL_VALIDATION_FAILED"; readonly INVALID_PARAMETERS: "INVALID_PARAMETERS"; readonly MISSING_REQUIRED_PARAM: "MISSING_REQUIRED_PARAM"; readonly MEMORY_EXHAUSTED: "MEMORY_EXHAUSTED"; readonly NETWORK_ERROR: "NETWORK_ERROR"; readonly PERMISSION_DENIED: "PERMISSION_DENIED"; readonly PROXY_WORKER_LIFECYCLE_FAILED: "PROXY_WORKER_LIFECYCLE_FAILED"; readonly PROVIDER_NOT_AVAILABLE: "PROVIDER_NOT_AVAILABLE"; readonly PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED"; readonly PROVIDER_QUOTA_EXCEEDED: "PROVIDER_QUOTA_EXCEEDED"; readonly OPERATION_ABORTED: "OPERATION_ABORTED"; readonly INVALID_CONFIGURATION: "INVALID_CONFIGURATION"; readonly MISSING_CONFIGURATION: "MISSING_CONFIGURATION"; readonly INVALID_VIDEO_RESOLUTION: "INVALID_VIDEO_RESOLUTION"; readonly INVALID_VIDEO_LENGTH: "INVALID_VIDEO_LENGTH"; readonly INVALID_VIDEO_ASPECT_RATIO: "INVALID_VIDEO_ASPECT_RATIO"; readonly INVALID_VIDEO_AUDIO: "INVALID_VIDEO_AUDIO"; readonly INVALID_VIDEO_MODE: "INVALID_VIDEO_MODE"; readonly MISSING_VIDEO_IMAGE: "MISSING_VIDEO_IMAGE"; readonly EMPTY_VIDEO_PROMPT: "EMPTY_VIDEO_PROMPT"; readonly VIDEO_PROMPT_TOO_LONG: "VIDEO_PROMPT_TOO_LONG"; readonly EMPTY_IMAGE_PATH: "EMPTY_IMAGE_PATH"; readonly INVALID_IMAGE_TYPE: "INVALID_IMAGE_TYPE"; readonly IMAGE_TOO_LARGE: "IMAGE_TOO_LARGE"; readonly IMAGE_TOO_SMALL: "IMAGE_TOO_SMALL"; readonly INVALID_IMAGE_FORMAT: "INVALID_IMAGE_FORMAT"; readonly INVALID_IMAGE_SIZE: "INVALID_IMAGE_SIZE"; readonly IMAGE_BUFFER_INVALID: "IMAGE_BUFFER_INVALID"; readonly FILE_PROCESSING_FAILED: "FILE_PROCESSING_FAILED"; readonly CSV_PROCESSING_FAILED: "CSV_PROCESSING_FAILED"; readonly PDF_PAGE_LIMIT_EXCEEDED: "PDF_PAGE_LIMIT_EXCEEDED"; readonly PDF_AGGREGATE_PAGE_LIMIT_EXCEEDED: "PDF_AGGREGATE_PAGE_LIMIT_EXCEEDED"; readonly PDF_AGGREGATE_SIZE_LIMIT_EXCEEDED: "PDF_AGGREGATE_SIZE_LIMIT_EXCEEDED"; readonly PDF_PAGE_COUNT_UNVERIFIABLE: "PDF_PAGE_COUNT_UNVERIFIABLE"; readonly PDF_PASSWORD_REQUIRED: "PDF_PASSWORD_REQUIRED"; readonly PDF_INCORRECT_PASSWORD: "PDF_INCORRECT_PASSWORD"; readonly RATE_LIMITER_QUEUE_FULL: "RATE_LIMITER_QUEUE_FULL"; readonly RATE_LIMITER_QUEUE_TIMEOUT: "RATE_LIMITER_QUEUE_TIMEOUT"; readonly RATE_LIMITER_RESET: "RATE_LIMITER_RESET"; readonly SCORER_NOT_FOUND: "SCORER_NOT_FOUND"; readonly EVALUATION_VALIDATION_FAILED: "EVALUATION_VALIDATION_FAILED"; readonly EVALUATION_TIMEOUT: "EVALUATION_TIMEOUT"; readonly EVALUATION_EXECUTION_FAILED: "EVALUATION_EXECUTION_FAILED"; readonly MISSING_PPT_PROPERTIES: "MISSING_PPT_PROPERTIES"; readonly INVALID_PPT_PAGES: "INVALID_PPT_PAGES"; readonly INVALID_PPT_FORMAT: "INVALID_PPT_FORMAT"; readonly INVALID_PPT_PROVIDER: "INVALID_PPT_PROVIDER"; readonly INVALID_PPT_OUTPUT_OPTIONS: "INVALID_PPT_OUTPUT_OPTIONS"; readonly INVALID_PPT_OUTPUT_PATH: "INVALID_PPT_OUTPUT_PATH"; readonly INVALID_PPT_LOGO_PATH: "INVALID_PPT_LOGO_PATH"; readonly INVALID_PPT_MODE: "INVALID_PPT_MODE"; readonly INVALID_PPT_PROMPT: "INVALID_PPT_PROMPT"; readonly CSV_INVALID_INPUT: "CSV_INVALID_INPUT"; readonly CSV_ROW_INVALID: "CSV_ROW_INVALID"; readonly CSV_FILE_ACCESS_FAILED: "CSV_FILE_ACCESS_FAILED"; readonly CSV_PARSE_FAILED: "CSV_PARSE_FAILED"; }; /** * Enhanced error class with structured information */ export declare class NeuroLinkError extends Error { readonly code: string; readonly category: ErrorCategory; readonly severity: ErrorSeverity; readonly retriable: boolean; readonly retryAfterMs?: number; readonly context: Record<string, unknown>; readonly timestamp: Date; readonly toolName?: string; readonly serverId?: string; constructor(options: { code: string; message: string; category: ErrorCategory; severity: ErrorSeverity; retriable: boolean; retryAfterMs?: number; context?: Record<string, unknown>; originalError?: Error; toolName?: string; serverId?: string; }); /** * Convert to JSON for logging and serialization */ toJSON(): StructuredError; } /** * Error factory for common error scenarios */ export declare class ErrorFactory { /** * Create a tool not found error */ static toolNotFound(toolName: string, availableTools?: string[]): NeuroLinkError; /** * Create a tool execution failed error */ static toolExecutionFailed(toolName: string, originalError: Error, serverId?: string): NeuroLinkError; /** * Create a tool timeout error. * * `timeoutMs` is the bound that was actually exceeded. Pass `budget` when * the timeout happened inside a retry loop, so the error can say which * number it is reporting: a reader who sees `timeoutMs: 120000` next to an * `executionTime` of 483005 will otherwise conclude the timeout was never * enforced, when in fact four attempts of 120s each were. * * `budget.exhausted` marks the case where the whole-execution ceiling ran * out rather than a single attempt overrunning; that error is NOT retriable, * because there is no budget left to retry into. */ static toolTimeout(toolName: string, timeoutMs: number, serverId?: string, budget?: { attempt?: number; maxAttempts?: number; attemptTimeoutMs?: number; totalTimeoutMs?: number; elapsedMs?: number; exhausted?: boolean; }): NeuroLinkError; /** * Create a parameter validation error */ static invalidParameters(toolName: string, validationError: Error, providedParams?: unknown): NeuroLinkError; /** * Create a network error */ static networkError(toolName: string, originalError: Error, serverId?: string): NeuroLinkError; /** * Create a memory exhaustion error */ static memoryExhausted(toolName: string, memoryUsageMB: number): NeuroLinkError; /** * Create a typed abort error preserving the originating exception. Callers * can switch on `error.category === ErrorCategory.ABORT` and * `error.code === ERROR_CODES.OPERATION_ABORTED` instead of message-string * matching DOMException / AI SDK error wrappers. * * `error.name` is intentionally set to "AbortError" (overriding the default * "NeuroLinkError") so existing callers that branch on * `err.name === "AbortError"` keep working without code changes — the new * structured fields (category, code, retriable) are additive. */ static aborted(originalError?: Error): NeuroLinkError; /** * Create a missing configuration error (e.g., missing API key) */ static missingConfiguration(configName: string, context?: Record<string, unknown>): NeuroLinkError; /** * Create an invalid configuration error (e.g., NaN for numeric values) */ static invalidConfiguration(configName: string, reason: string, context?: Record<string, unknown>): NeuroLinkError; /** * Create an invalid CSV input error (e.g. an empty filePath/csvString argument). */ static csvInvalidInput(message: string): NeuroLinkError; /** * Create a CSV row-shape violation error (#384) — a parsed row that isn't a * string-keyed object with string values. */ static csvRowInvalid(message: string, rowNumber: number): NeuroLinkError; /** * Create a CSV file access error (bad path, permissions, ENOENT/EACCES) (#375). */ static csvFileAccessFailed(message: string, filePath: string, originalError?: Error): NeuroLinkError; /** * Create a CSV read/parse failure error (#375) — source stream errors and * csv-parser errors both route through this, carrying the enriched * `buildCsvParseErrorMessage` context in `message`. */ static csvParseFailed(message: string, context: Record<string, unknown>, originalError?: Error): NeuroLinkError; /** * Create an invalid video resolution error */ static invalidVideoResolution(resolution: string): NeuroLinkError; /** * Create an invalid video length error */ static invalidVideoLength(length: number): NeuroLinkError; /** * Create an invalid video aspect ratio error */ static invalidVideoAspectRatio(aspectRatio: string): NeuroLinkError; /** * Create an invalid video audio option error */ static invalidVideoAudio(audio: unknown): NeuroLinkError; /** * Create an invalid video mode error */ static invalidVideoMode(): NeuroLinkError; /** * Create a missing video image error */ static missingVideoImage(): NeuroLinkError; /** * Create an empty video prompt error */ static emptyVideoPrompt(): NeuroLinkError; /** * Create a video prompt too long error */ static videoPromptTooLong(length: number, maxLength: number): NeuroLinkError; /** * Create an empty image path error */ static emptyImagePath(): NeuroLinkError; /** * Create an invalid image type error */ static invalidImageType(): NeuroLinkError; /** * Create a PDF page limit exceeded error */ static pdfPageLimitExceeded(estimatedPages: number, maxPages: number, provider: string): NeuroLinkError; /** * The combined page count across every PDF in one request exceeds what the * provider accepts (#309). Distinct from `pdfPageLimitExceeded`, which is * per-file — here each document can be individually legal. */ static pdfAggregatePageLimitExceeded(fileCount: number, totalPages: number, maxPages: number, provider: string): NeuroLinkError; /** * The combined byte size across every PDF in one request exceeds what the * provider accepts (#309). */ static pdfAggregateSizeLimitExceeded(fileCount: number, totalMB: number, maxSizeMB: number, provider: string): NeuroLinkError; /** * A PDF supplied through the untrusted `input.content` surface could not be * parsed for a page count (#309). Caller-supplied `metadata.pages` is not * authoritative there, so an unreadable document is rejected rather than * admitted with an assumed count of zero. */ static pdfPageCountUnverifiable(filenames: string[], provider: string): NeuroLinkError; /** * The PDF is encrypted and no password was supplied (#258). */ static pdfPasswordRequired(): NeuroLinkError; /** * A password was supplied for an encrypted PDF but it was incorrect (#258). */ static pdfIncorrectPassword(): NeuroLinkError; /** * Create an image too large error */ static imageTooLarge(sizeMB: string, maxMB: string): NeuroLinkError; /** * Create an invalid image size error (NaN/Infinity/negative byte length). * Distinct from `imageTooLarge` — this rejects a malformed size value * before it reaches the max-size comparison, since `NaN > maxSize` and * `-1 > maxSize` both evaluate to `false` and would otherwise let a * corrupted stat/header value silently skip the guard. */ static invalidImageSize(size: number): NeuroLinkError; /** * Create an image too small error */ static imageTooSmall(): NeuroLinkError; /** * Create an invalid image format error */ static invalidImageFormat(): NeuroLinkError; /** * Create an image buffer validation error: empty, undersized, or a * truncated buffer detected by `ImageProcessor.validateBufferNotEmpty()`. * Takes the fully-formed message so call sites keep their specific * byte-count detail instead of a fixed generic message. */ static imageBufferInvalid(message: string): NeuroLinkError; /** * Create a rate limiter queue full error */ static rateLimiterQueueFull(maxQueueSize: number): NeuroLinkError; /** * Create a rate limiter queue timeout error */ static rateLimiterQueueTimeout(timeoutMs: number): NeuroLinkError; /** * Create a rate limiter reset error */ static rateLimiterReset(): NeuroLinkError; /** * Create a generic missing PPT property error */ static missingPPTProperty(field: string, suggestions?: string[]): NeuroLinkError; /** * Create an invalid PPT pages error */ static invalidPPTPages(pages: unknown, reason: string): NeuroLinkError; /** * Create an invalid PPT format error */ static invalidPPTFormat(format: string): NeuroLinkError; /** * Create a generic invalid PPT output options error */ static invalidPPTOutputOptions(field: string, value: unknown, validOptions?: string[]): NeuroLinkError; /** * Create an invalid PPT output path error */ static invalidPPTOutputPath(path: unknown, reason: string): NeuroLinkError; /** * Create an invalid PPT mode error */ static invalidPPTMode(): NeuroLinkError; /** * Create an invalid PPT prompt error */ static invalidPPTPrompt(reason: string): NeuroLinkError; /** * Create an invalid PPT logo path error */ static invalidPPTLogoPath(path: unknown, reason: string): NeuroLinkError; /** * Create an invalid PPT provider error */ static invalidPPTProvider(provider: unknown): NeuroLinkError; /** * Create a scorer not found error */ static scorerNotFound(scorerId: string, availableScorers?: string[]): NeuroLinkError; /** * Create an evaluation validation error */ static evaluationValidationFailed(scorerId: string, errors: string[]): NeuroLinkError; /** * Create an evaluation timeout error */ static evaluationTimeout(operation: string, timeoutMs: number): NeuroLinkError; /** * Create an evaluation execution failed error */ static evaluationExecutionFailed(operation: string, originalError: Error): NeuroLinkError; /** * Create a proxy rolling-worker lifecycle error. Preserves the caller's exact * message so existing substring-based assertions keep working, while giving * callers a typed `code`/`category` to branch on instead of string-matching * generic `Error` instances. */ static proxyWorkerLifecycle(message: string, context?: Record<string, unknown>): NeuroLinkError; /** * Create a generic file-processing-failed error (e.g. an unrecognized or * corrupt file in `processUnifiedFilesArray`). Preserves the original error * as `originalError` (stack + message copied onto the new error's context). */ static fileProcessingFailed(filename: string, originalError: Error): NeuroLinkError; /** * Create a generic CSV-processing-failed error (explicit `csvFiles` path, * distinct from `fileProcessingFailed` so callers can classify CSV-specific * failures separately). Preserves the original error as `originalError`. */ static csvProcessingFailed(filename: string, originalError: Error): NeuroLinkError; } /** * Timeout wrapper for async operations */ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, timeoutError?: Error): Promise<T>; /** * Retry mechanism for retriable operations */ export declare function withRetry<T>(operation: () => Promise<T>, options: { maxAttempts: number; delayMs: number; isRetriable?: (error: Error) => boolean; onRetry?: (attempt: number, error: Error) => void; }): Promise<T>; /** * Circuit breaker for preventing cascading failures */ export declare class CircuitBreaker { private readonly failureThreshold; private readonly resetTimeoutMs; private failures; private lastFailureTime; private state; private name; constructor(failureThreshold?: number, resetTimeoutMs?: number, name?: string); execute<T>(operation: () => Promise<T>): Promise<T>; private onSuccess; private onFailure; getState(): "closed" | "open" | "half-open"; getFailureCount(): number; } /** * Detect AbortError from any source (DOMException, plain Error, or message-based). * Used to short-circuit retry/fallback loops when an abort signal fires. * * Uses `includes()` for message checks because provider error handlers * (e.g., googleVertex.formatProviderError) wrap the original AbortError * in a formatted error like "❌ Provider Error\n\nThis operation was aborted\n\n..." * which destroys the exact message match. */ export declare function isAbortError(error: unknown): boolean; /** * Error handler that decides whether to retry based on error type */ export declare function isRetriableError(error: Error): boolean; /** * Determines if an error is likely recoverable (rate limit, timeout, network issues). * Useful for deciding whether to retry or fail fast. */ export declare function isRecoverableError(error: Error): boolean; /** * Enhanced error logger that provides structured logging */ export declare function logStructuredError(error: NeuroLinkError, context?: Record<string, unknown>): void;