Skip to content

Bump dependencies, drop unused ones, OCR images embedded in PDFs - #19

Open
dvejsada wants to merge 4 commits into
mainfrom
claude/mistral-image-ocr
Open

dvejsada wants to merge 4 commits into
mainfrom
claude/mistral-image-ocr

Conversation

@dvejsada

@dvejsada dvejsada commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Upstream: nothing to merge; upstream/main (LibreChat-AI/rag-api, latest 2026-09-23) is already an ancestor of main.

1. Dependency bumps (chore(deps))

  • Every pin moved to its latest release within its current major line.
  • The langchain family moves as one set (langchain-core 1.7.0, langchain 1.4.7, langchain-openai 1.7.2, langchain-aws 1.9.0, langchain-google-genai 4.4.2, langchain-huggingface 1.2.3, langchain-mongodb 0.12.1), because all of their new versions need core ≥ 1.6.
  • Other notable bumps: fastapi 0.161.1, starlette 1.9.0, uvicorn 0.54.0, unstructured 0.27.34, pandas 2.3.3, sqlalchemy 2.0.55, pypdf 6.21.2, boto3 1.43.118, aiofiles 25.1.0 (needed by current unstructured-client).
  • tenacity stays at 9.1.4, because google-genai requires <9.2.
  • Held on purpose (breaking majors): pandas 3, SQLAlchemy 2.1, Pillow 13, chardet 7, sentence-transformers 6, mistralai 3, pgvector 0.5, asyncpg 0.32.

2. Removed dependencies the fork no longer uses

  • rapidocr-onnxruntime, opencv-python-headless: nothing imports them, and OCR is remote via Mistral. Also removed the libgl1 and libglib2.0-0 apt packages from the full Dockerfile, which only they needed.
  • networkx: nothing imports it; only an optional langchain-mongodb extra lists it.
  • pypdf: removed from the runtime deps and moved to test_requirements.txt, since only a test fixture uses it. unstructured-client still pulls it in.
  • mongomock: nothing references it.

3. OCR images embedded in PDFs (feat(ocr))

Mistral OCR returns only a placeholder (![img-0.jpeg](img-0.jpeg)) for a raster image embedded in a PDF page, such as a photo or scan of a page, and does not read the text inside it.

  • The PDF request now also asks for the extracted images (include_image_base64, image_min_size, image_limit).
  • Each image is OCR'd in its own call on a small thread pool.
  • The recognized text replaces the placeholder on that image's page. Image ids are matched per page, and results are applied in page order.
  • If one image's call fails, it logs a warning and the placeholder is kept. A failure of the main PDF call still raises.
  • With the feature off, the request is exactly as before.
  • New settings, documented in the README:
    • MISTRAL_OCR_EMBEDDED_IMAGES (default True)
    • MISTRAL_OCR_IMAGE_MIN_SIZE (100)
    • MISTRAL_OCR_MAX_EXTRACTED_IMAGES (200, minimum 1; how many images Mistral extracts, which bounds the response size)
    • MISTRAL_OCR_MAX_EMBEDDED_IMAGES (50, minimum 1; how many distinct images are OCR'd and billed, counted after the size filter and dedupe)
    • MISTRAL_OCR_IMAGE_MIN_PAGE_FRACTION (0.1; smaller images such as logos are skipped)
    • MISTRAL_OCR_IMAGE_CONCURRENCY (4; image OCR calls in flight across all documents and requests)
  • Identical images (e.g. a logo on every page) are OCR'd only once per document.
  • OCR calls retry on 429, 5xx and connection errors with backoff, using the SDK's RetryConfig.
  • If a placeholder shares its line with other content (a table cell, list item or sentence), the text goes inline instead of as a new paragraph.
  • ⚠️ Each distinct embedded image that is OCR'd is billed as one extra OCR page.

Test plan

  • Lite deps plus test deps install on Python 3.12, and pip check is clean. A dry run of the full requirements.txt resolves.
  • Unit tests in a Linux python:3.12 container: 453 passed, 1 skipped (the skip is expected).
  • Integration tests (pgvector via testcontainers): 35 passed.
  • New unit tests for the embedded-image path: placeholder replacement, request kwargs on and off, per-image failure isolation, cap, page-scoped ids, out-of-order completion, dict-shaped responses, dedupe, page-area filter, inline and table placement, and retries through the real SDK with a mocked HTTP transport.
  • Not verified: a live Mistral OCR call on a real PDF with an embedded photo. The exact image_base64 format is handled both as a data URI and as bare base64. The page-area filter assumes the bbox and page dimensions use the same pixel space.
  • CI is label-gated and has not run yet; add the CI label to run it.

Known, not addressed here

  • The huggingfacetei embeddings provider imports langchain_huggingface, which the lite image doesn't install.

🤖 Generated with Claude Code

Daniel Vejsada and others added 4 commits October 23, 2026 19:09
Bump every pin to its latest release within the current major line, and
move the langchain family together (langchain-core 1.7.0 is required by
the new langchain, langchain-openai, langchain-aws, langchain-google-genai
and langchain-huggingface). boto3 rises for langchain-aws 1.9; aiofiles
25.1 unblocks current unstructured-client. tenacity stays at 9.1.4:
google-genai requires <9.2.

Held on purpose (breaking majors): pandas 3, SQLAlchemy 2.1, Pillow 13,
chardet 7, sentence-transformers 6, mistralai 3, pgvector 0.5, asyncpg 0.32.

Removed, since PDFs/images are OCR'd remotely via Mistral:
- rapidocr-onnxruntime, opencv-python-headless (no imports) and the
  libgl1/libglib2.0-0 apt packages they needed in the full Dockerfile
- networkx (no imports; only an optional langchain-mongodb extra)
- pypdf from runtime deps (only a test fixture uses it; moved to
  test_requirements.txt, and unstructured-client still pulls it in)
- mongomock (unreferenced)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mistral OCR leaves only a placeholder (![img-0.jpeg](img-0.jpeg)) for a
raster image embedded in a PDF page, such as a photo or scan of a page,
without reading the text inside it. The PDF request now asks for the
extracted images (include_image_base64, image_min_size, image_limit);
each image is OCR'd in its own call on a small thread pool, and the
recognized text replaces its placeholder on the page it belongs to.

A failed image call logs a warning and keeps the placeholder; the main
PDF call still raises. With the feature off, the request is unchanged.

New settings: MISTRAL_OCR_EMBEDDED_IMAGES (default True),
MISTRAL_OCR_IMAGE_MIN_SIZE (100), MISTRAL_OCR_MAX_EMBEDDED_IMAGES (50,
<=0 for no cap), MISTRAL_OCR_IMAGE_CONCURRENCY (4). Each embedded image
is billed as one extra OCR page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- OCR each distinct embedded image once (sha256 of its bytes) and reuse
  the text, and skip images below MISTRAL_OCR_IMAGE_MIN_PAGE_FRACTION
  (default 0.1) of their page, so logos/letterheads repeated on every
  page don't cost an extra billed call each or pollute every page.
- Retry OCR calls on 429/5xx and connection errors with backoff via the
  SDK's RetryConfig, so rate limits no longer silently drop image text.
- Match placeholders on the link target with any alt text.
- MISTRAL_OCR_MAX_EMBEDDED_IMAGES is now at least 1, so the response
  size is always bounded; request cap+1 and warn only when exceeded.
- Insert text inline (newlines collapsed, `|` escaped in table rows)
  when the placeholder shares its line, instead of breaking tables.
- Use mimetypes for data-URI mime types.
- Pin MISTRAL_OCR_EMBEDDED_IMAGES=False in the older PDF tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Split the image caps: MISTRAL_OCR_MAX_EXTRACTED_IMAGES (default 200)
  bounds how many images Mistral extracts, while
  MISTRAL_OCR_MAX_EMBEDDED_IMAGES (50) now caps distinct images actually
  OCR'd, applied after the size filter and dedupe. Per-page logos no
  longer use up the cap and push real scans out.
- Log each size-filtered image at DEBUG with its page fraction; keep
  images whose box extends beyond the page (units mismatch) and warn.
- Send include_blocks=False; the SDK defaults it on and we don't use it.
- A placeholder behind a block marker (#, -, >, 1.) counts as alone on
  its line, so a page's text no longer becomes one huge heading or item.
- Bound image OCR calls in flight across all documents with a shared
  semaphore sized by MISTRAL_OCR_IMAGE_CONCURRENCY.
- Insert the text at the first placeholder only; drop repeats.
- Retry tests use a fast real RetryConfig instead of patching SDK internals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant