@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
TypeScript
/**
* 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;