@space-operator/mistral-ocr-pipeline (0.4.0)
Installation
@space-operator:registry=npm install @space-operator/mistral-ocr-pipeline@0.4.0"@space-operator/mistral-ocr-pipeline": "0.4.0"About this package
@space-operator/mistral-ocr-pipeline
A server-side OCR pipeline plugin built on the official Mistral TypeScript SDK.
It defaults to the pinned mistral-ocr-4-1 model and exposes OCR, text
correction, vision correction, and vision verification through one client
contract. Version 0.4 adds remote document and image inputs, existing-file
processing, JSON-schema annotations, usage reporting, and normalized rich page
metadata without changing the 0.3 PDF APIs.
Requirements
- Node.js 20 or newer
- A Mistral API key
Install
After publishing the package:
npm install @space-operator/mistral-ocr-pipeline
To consume it from this repository without publishing, create a tarball and install that tarball in the other project:
npm run plugin:pack
npm install /path/to/space-operator-mistral-ocr-pipeline-0.4.0.tgz
Use as a pipeline plugin
import { mistralOcrPipelinePlugin } from "@space-operator/mistral-ocr-pipeline";
const plugins = new Map([
[mistralOcrPipelinePlugin.id, mistralOcrPipelinePlugin],
]);
const client = mistralOcrPipelinePlugin.createClient({
apiKey: process.env.MISTRAL_API_KEY!,
visionFixModel: "mistral-medium-3-5",
correctionMaxTokens: 4_096,
timeoutMs: 90_000,
retryConfig: {
strategy: "backoff",
retryConnectionErrors: true,
backoff: {
initialInterval: 1_000,
maxInterval: 10_000,
exponent: 2,
maxElapsedTime: 240_000,
},
},
ocr: {
includeBlocks: true,
tableFormat: "markdown",
confidenceScoresGranularity: "word",
},
});
const pages = await client.processPdf({
source: { kind: "path", path: "./document.pdf" },
pages: [0, 1, 2],
chunkSize: 10,
onProgress: ({ phase, processedPages, totalPages }) => {
console.log(phase, `${processedPages}/${totalPages}`);
},
});
processPdf uploads once, processes pages in bounded chunks, and deletes the
temporary Mistral upload whether processing succeeds, fails, or is cancelled.
Chunk responses are checked for missing, duplicate, invalid, and unexpected
page indexes, then returned in the same order as the requested pages.
To keep a successful upload for another operation and receive its ID, use the detailed API:
const { pages, uploadedFileId } = await client.processPdfDetailed({
source: { kind: "path", path: "./document.pdf" },
pages: [0, 1, 2],
retainUploadedFile: true,
});
// Later, when no other operation needs it:
await client.deleteFile(uploadedFileId!);
Failed and cancelled operations are cleaned up even when
retainUploadedFile is enabled.
Remote documents, images, and existing files
processDocument and processDocumentDetailed accept sources that do not need
a temporary upload:
const remotePages = await client.processDocument({
source: {
kind: "document-url",
url: "https://example.com/report.pdf",
fileName: "report.pdf",
},
pages: [0, 1],
});
const imagePages = await client.processDocument({
source: { kind: "image-url", url: "https://example.com/scan.png" },
pages: [0],
});
const existingPages = await client.processDocument({
source: { kind: "file-id", fileId: "mistral-file-id" },
pages: [0, 1],
});
Existing file IDs remain caller-owned by default. Set
deleteAfterProcessing: true on a file-id source to have the pipeline delete
it after success, failure, or cancellation. Document and image URLs are sent
directly to Mistral and never enter the upload lifecycle.
The package does not read environment variables itself. Consumers pass all
credentials and model overrides through createClient, which keeps the plugin
portable across Next.js apps, CLIs, background jobs, and other Node.js
pipelines. The package also exports the generic OcrPipelinePlugin interface
for typed registries.
In-memory documents
const pages = await client.processPdf({
source: {
kind: "bytes",
content: pdfBytes,
fileName: "document.pdf",
},
pages: [0, 1],
});
OCR options
Client-level defaults and per-call overrides support:
includeBlocksandincludeImageBase64tableFormat: "markdown" | "html"confidenceScoresGranularity: "word" | "page" | "block"extractHeaderandextractFooterimageLimitandimageMinSizebboxAnnotationFormatfor structured image/bounding-box annotationsdocumentAnnotationFormatanddocumentAnnotationPromptfor structured document extraction
Every normalized page can include aggregate page confidence. Blocks can include
OCR 4.1 content and type confidence when block granularity is requested. Word
granularity continues to populate lowConfidenceWords using the configured
threshold.
Normalized pages can also expose extracted images, tables, hyperlinks,
header, footer, and dimensions. Image and document annotations retain the
provider's raw string and include parsed when it is valid JSON.
Structured output and detailed metadata
Structured output uses a provider-neutral JSON-schema shape:
const result = await client.processDocumentDetailed({
source: {
kind: "document-url",
url: "https://example.com/invoice.pdf",
},
pages: [0, 1],
ocr: {
documentAnnotationFormat: {
type: "json_schema",
jsonSchema: {
name: "invoice",
strict: true,
schema: {
type: "object",
properties: {
invoiceNumber: { type: "string" },
total: { type: "number" },
},
required: ["invoiceNumber", "total"],
},
},
},
documentAnnotationPrompt: "Extract the invoice fields.",
},
});
console.log(result.model, result.usage.pagesProcessed);
console.log(result.chunks[0]?.documentAnnotation?.parsed);
Detailed results include the response model, aggregate usage, and one metadata
entry per chunk. Document annotations stay attached to their chunk because a
chunked run makes multiple OCR requests. documentSizeBytes is retained once
using the largest value reported across those requests rather than being
double-counted.
Cancellation and progress
processPdf and processPdfDetailed accept an AbortSignal. The same signal
is passed into file reads, uploads, and OCR requests; any upload created before
cancellation is still deleted.
const controller = new AbortController();
const pending = client.processPdf({
source: { kind: "path", path: "./document.pdf" },
pages: [0, 1, 2],
signal: controller.signal,
onProgress: (event) => {
if (event.processedPages === 2) controller.abort();
},
});
Progress phases are uploading, processing, cleaning-up, and completed.
Callbacks may be synchronous or asynchronous. Direct URL and caller-owned file
sources start at processing; lifecycle phases are emitted only when an upload
or managed existing file requires them.
Regional endpoints (global, eu, or us), custom server URLs, request
timeouts, SDK retry policies, model names, bounded correction output, and a
custom fetch implementation are configurable on the client.
Direct client use
import { MistralOcrClient } from "@space-operator/mistral-ocr-pipeline";
const client = new MistralOcrClient({
apiKey: process.env.MISTRAL_API_KEY!,
});
MistralOcrClient implements the exported OcrPipelineClient interface. It
also provides uploadPdfBytes(content, fileName) for pipelines that already
hold documents in memory, plus deleteFile(fileId) for explicit low-level
lifecycle control. Low-level consumers can call processDocumentPages or
processDocumentPagesDetailed with a document URL, image URL, or file ID.
Build and package
From the repository root:
npm run plugin:build
npm run plugin:pack
Dependencies
Dependencies
| ID | Version |
|---|---|
| @mistralai/mistralai | ^2.6.4 |
Development dependencies
| ID | Version |
|---|---|
| @types/node | ^20 |
| typescript | ^5 |