How to Convert Multiple PDFs with olmOCR: Batch Processing Guide
Use the WorkQueue class with LocalBackend or S3Backend to distribute per-page OCR jobs across async workers, calling process_page from olmocr.pipeline for each page.
olmOCR (allenai/olmocr) is an open-source OCR pipeline that converts PDF pages to markdown using vision language models. To convert multiple PDFs with olmOCR efficiently, you leverage its async WorkQueue infrastructure that treats every PDF page as an independent work unit, enabling parallel processing across local files or cloud storage.
Understanding the olmOCR Batch Architecture
olmOCR employs a pipeline-driven architecture where PDF conversion is split into discrete, asynchronous operations. The system relies on three core components working together:
olmocr.pipeline– Implements the end-to-end OCR flow inolmocr/pipeline.py. The public entry point isprocess_page, which renders a PDF page to an image, builds a VLLM request, and parses the response into aPageResultobject.olmocr.work_queue.WorkQueue– Manages a queue of jobs and distributes them to a pool of workers. It supportsLocalBackendfor filesystem operations orS3Backendfor cloud storage.olmocr/bench/runners/run_olmocr_pipeline.py– Demonstrates the minimal async wrapper pattern (lines 42-84) for single-page execution, which scales to batch operations when integrated with the work queue.
The Data Flow for Bulk Conversion
- Discover PDFs – Collect absolute paths using standard file walkers or dataset utilities like
prepare_olmocrmix.py. - Create a
WorkQueue– Instantiate withLocalBackend(local disk) orS3Backend(S3 storage) and configure worker count. - Enqueue jobs – Add PDF paths; the queue automatically normalizes each into per-page tasks.
- Process pages – Workers invoke
process_page(args, worker_id, pdf_orig_path, pdf_local_path, page_num)for each page. - Collect results – Retrieve
PageResultobjects containing extracted markdown text. - Shutdown – Workers exit cleanly when the queue empties; the VLLM server remains available for reuse.
Converting Local PDFs in Batch
For processing directories of PDFs on your local machine, combine the WorkQueue with LocalBackend and ensure the VLLM server is running before enqueuing jobs.
Step 1: Configure Pipeline Arguments
Create a configuration object matching the expected arguments for process_page:
import asyncio
import os
from pathlib import Path
from olmocr.pipeline import MetricsKeeper, WorkerTracker, process_page, vllm_server_ready, vllm_server_host
from olmocr.work_queue import WorkQueue, LocalBackend
class Args:
model = "allenai/olmOCR-2-7B-1025-FP8"
server = "http://localhost:30044/v1"
port = 30044
model_chat_template = "qwen2-vl"
max_model_len = 16384
guided_decoding = False
gpu_memory_utilization = 0.8
target_longest_image_dim = 1288
target_anchor_text_len = -1
max_page_retries = 8
max_page_error_rate = 0.004
tensor_parallel_size = 1
data_parallel_size = 1
args = Args()
Step 2: Ensure VLLM Server Availability
Before processing, verify or start the VLLM server using patterns from run_olmocr_pipeline.py:
async def ensure_server():
"""Start a VLLM server if one is not already running."""
try:
await asyncio.wait_for(vllm_server_ready(args), timeout=5)
except Exception:
sem = asyncio.Semaphore(1)
asyncio.create_task(vllm_server_host(args.model, args, sem))
await vllm_server_ready(args)
Step 3: Process Files with WorkQueue
Initialize the queue, enqueue all PDFs, and run the conversion:
async def convert_pdf_directory(pdf_dir: str):
await ensure_server()
# Initialize backend and queue with CPU count workers
backend = LocalBackend()
queue = WorkQueue(backend=backend, max_workers=os.cpu_count())
# Enqueue every PDF found in directory
pdf_paths = list(Path(pdf_dir).rglob("*.pdf"))
for pdf_path in pdf_paths:
await queue.enqueue_job(str(pdf_path))
# Execute queue - workers call process_page internally for each page
await queue.run()
print(f"✅ Converted {len(pdf_paths)} PDFs")
if __name__ == "__main__":
asyncio.run(convert_pdf_directory("/path/to/pdfs"))
Under the hood, WorkQueue reads each PDF to determine page count, creates a task per page, and distributes them across workers respecting concurrency limits.
Processing PDFs from S3
When source files reside in cloud storage, swap LocalBackend for S3Backend without changing the processing logic:
from olmocr.work_queue import WorkQueue, S3Backend
# Configure S3 backend with your boto3 client
backend = S3Backend(
s3_client=boto3.client('s3'),
bucket="my-pdf-bucket",
prefix="documents/",
output_bucket="my-output-bucket" # Optional: for writing results back to S3
)
queue = WorkQueue(backend=backend, max_workers=4)
await queue.run()
The S3Backend automatically downloads PDFs to temporary local storage for processing and can upload markdown results back to the specified output bucket.
Managing Concurrency and Resource Limits
Concurrency is controlled by two key semaphores defined in olmocr/pipeline.py (lines 87-90):
max_concurrent_requests_limit– Defaults to 1, limiting simultaneous VLLM API calls.pdf_render_max_workers_limit– Derived from CPU count, limiting parallel PDF rendering operations.
The WorkQueue respects these limits automatically, ensuring you do not overwhelm the VLLM server or exhaust system memory when converting hundreds of PDFs simultaneously.
Summary
- Use
WorkQueuewithLocalBackendorS3Backendto orchestrate batch conversions without managing threads manually. - Call
process_pagefor each page; it handles rendering, VLLM communication, and retry logic defined inolmocr/pipeline.py. - Manage the VLLM server lifecycle using
vllm_server_readyandvllm_server_hostto ensure the model is available before queuing jobs. - Leverage async architecture to process dozens or hundreds of PDFs concurrently, limited only by GPU memory and the
max_concurrent_requests_limitsemaphore.
Frequently Asked Questions
How does olmOCR handle concurrency when converting multiple PDFs?
olmOCR uses an asyncio-based work queue that treats each PDF page as an independent task. The WorkQueue class spins up multiple worker coroutines (configurable via max_workers) that consume tasks concurrently. Actual concurrency is bounded by semaphores in olmocr/pipeline.py that limit simultaneous VLLM requests and PDF rendering operations to prevent resource exhaustion.
Can I process PDFs stored in S3 with olmOCR?
Yes. Replace LocalBackend with S3Backend from olmocr.work_queue, passing a boto3 client and bucket configuration. The backend handles downloading PDFs to temporary local storage before processing and supports writing markdown outputs directly back to S3 via the output_bucket parameter.
What is the recommended way to handle VLLM server startup for large batches?
Follow the pattern in olmocr/bench/runners/run_olmocr_pipeline.py: use vllm_server_ready() to check for an existing server, and if none responds within your timeout, launch vllm_server_host() as a background task. This ensures the model loads once and remains available for all subsequent PDFs in your batch, avoiding the overhead of repeated model initialization.
How do I retrieve the extracted markdown text from the pipeline results?
The process_page function returns a PageResult object. Access the extracted text via result.response.natural_text after verifying that result exists and result.is_fallback is False (indicating successful OCR rather than error fallback). When using WorkQueue, attach a callback to the queue or modify the result handler to write outputs to your preferred storage format.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →