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

156 lines (155 loc) • 7.61 kB
/** * PDF Processor with Image Fallback Support * * Handles PDF processing for all providers: * - Native PDF support for providers that accept PDF directly (Google AI, Vertex, OpenAI, Anthropic, Bedrock) * - PDF → Image conversion for providers that don't support native PDF (Azure, Mistral, Ollama) * * Page counting, text extraction and page rendering all go through pdf-parse, * so the process only ever loads pdf-parse's pdfjs-dist. pdfjs keeps its Node * worker in a process-wide global, and a second pdfjs version fails its * API-vs-Worker version check against whichever copy loaded first. */ import type { FileProcessingResult, PDFProcessorOptions, PDFProviderConfig, PDFImageConversionOptions, PDFImageConversionResult, PDFImagePage } from "../types/index.js"; export declare class PDFProcessor { private static readonly PDF_SIGNATURE; static process(content: Buffer, options?: PDFProcessorOptions): Promise<FileProcessingResult>; /** * Check if a provider supports native PDF input * @param provider - Provider name * @returns true if provider can accept PDF directly, false if requires image conversion */ static supportsNativePDF(provider: string): boolean; static getProviderConfig(provider: string): PDFProviderConfig | null; /** * Best-effort page count for a PDF whose count the caller did not supply * (#309). Mirrors what `process()` derives for the `input.pdfFiles` path: * the accurate pdfjs count when the document parses, otherwise the header * regex estimate. Returns null when neither can determine a count. * * Exists so the aggregate page-limit guard can enforce against PDFs handed * in via `input.content`, where `metadata.pages` is optional and routinely * omitted — without it, an absent count silently sums as zero. */ static resolvePageCount(buffer: Buffer): Promise<number | null>; private static isValidPDF; private static extractBasicMetadata; /** * Accurate page count via pdf-parse (pdfjs) (#287). The header regex in * `extractBasicMetadata` only sees plaintext `/Type /Page` markers and misses * compressed/object-stream PDFs (and miscounts when a page dict spans a chunk * boundary). This parses the document properly, bounded by a timeout so a * pathological PDF can't block; returns null on timeout/failure so the caller * falls back to the regex estimate. */ private static getAccuratePageCount; /** * Font and CMap directories of the pdfjs-dist copy pdf-parse itself loads, * so non-embedded standard fonts and CJK text render instead of dropping * out. Resolved from pdf-parse's location, never our own, to stay on that * single copy. Undefined when resolution fails; rendering still works, with * pdfjs falling back to its built-in font substitution. */ private static pdfjsAssetDirs; private static resolvePdfjsAssetDirs; /** * Open a PDF for page-by-page PNG rendering through pdf-parse. Rejects with * pdf-parse's PasswordException for encrypted PDFs (#258), which the callers * map to typed errors. */ private static openForRendering; static estimateTokens(pageCount: number, mode?: "text-only" | "visual"): number; /** * Estimate the largest page's rendered pixel count from the PDF's MediaBox * entries, WITHOUT rendering (#260). Parses `/MediaBox [llx lly urx ury]` * (dimensions in points); rendered pixels ≈ (width·scale)·(height·scale). * Returns 0 when no plaintext MediaBox is found (e.g. compressed object * streams) — the caller then skips downscaling, matching prior behavior. * * A crafted/malformed MediaBox can have finite but astronomically large * width/height; `width * height * scale * scale` can then overflow past * `Number.MAX_VALUE` to `Infinity`. Clamp the product to * `Number.MAX_SAFE_INTEGER` so it stays finite — a large-but-finite pixel * estimate still triggers downscaling, whereas `Infinity` would collapse * the computed downscale factor to 0 (see `convertToImages`). */ private static largestPagePixels; /** * Convert a PDF buffer to an array of base64 PNG images * * This is used automatically when a provider (like Azure, Mistral, Ollama) doesn't * support native PDF input but does support image input. The PDF pages are converted * to PNG images and sent as vision content. * * @param pdfBuffer - PDF file content as Buffer * @param options - Conversion options * @returns Promise with conversion result including base64 images * * @example * ```typescript * // Check if conversion is needed * if (!PDFProcessor.supportsNativePDF('azure')) { * const result = await PDFProcessor.convertToImages(pdfBuffer, { * scale: 2, * maxPages: 10 * }); * // Use images in LLM input instead of PDF * options.input.images = result.images; * } * ``` */ static convertToImages(pdfBuffer: Buffer, options?: PDFImageConversionOptions): Promise<PDFImageConversionResult>; /** * Shared input validation for the image-conversion paths. Returns the PDF * size in MB (needed by the memory-estimate log). Throws on any invalid input * with the same messages the batch path has always used. */ private static validateImageConversionInput; /** * Streaming variant of {@link convertToImages} (#302): yields each page's * base64 PNG as soon as it renders instead of buffering the whole document, * and reports progress via `options.onProgress`. A page that fails to render * is yielded with `error` set (per-page isolation, #294) rather than aborting * the stream. * * NOT a wrapper of/over {@link convertToImages}, despite the similar * contract — the two are independent, parallel implementations (each does * its own document open, downscale calculation, and page loop) * rather than one delegating to the other. Keep behavior changes (page * isolation, downscale, password handling) in sync across both by hand. */ static convertToImagesStream(pdfBuffer: Buffer, options?: PDFImageConversionOptions): AsyncGenerator<PDFImagePage, void, void>; /** * Convert a PDF file path to an array of base64 PNG images * * @param pdfPath - Path to the PDF file * @param options - Conversion options * @returns Promise with conversion result */ static convertFromPath(pdfPath: string, options?: PDFImageConversionOptions): Promise<PDFImageConversionResult>; /** * Check if PDF to image conversion is available * Useful for feature detection * * @returns true if the pdf-parse renderer is available */ static isImageConversionAvailable(): Promise<boolean>; /** * Get estimated memory usage for converting a PDF * * @param pdfSizeBytes - Size of PDF file in bytes * @param pageCount - Estimated number of pages * @param scale - Scale factor * @returns Estimated memory usage in MB */ static estimateConversionMemoryUsage(pdfSizeBytes: number, pageCount: number, scale?: number): number; /** * Get list of providers that require PDF → Image conversion */ static getImageFallbackProviders(): string[]; /** * Get list of providers that support native PDF */ static getNativePDFProviders(): string[]; } export declare const PDFImageConverter: typeof PDFProcessor;