Skip to content

refactor(parsers): remove legacy loaders and doc-serializer shims - #513

Merged
Ahmath-Gadji merged 8 commits into
refactor/hexagonalfrom
refactor/remove-parser-shims
Jun 22, 2026
Merged

refactor(parsers): remove legacy loaders and doc-serializer shims#513
Ahmath-Gadji merged 8 commits into
refactor/hexagonalfrom
refactor/remove-parser-shims

Conversation

@Ahmath-Gadji

@Ahmath-Gadji Ahmath-Gadji commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

What

Migrates document parsing entirely onto the new core/indexing/parsers stack and removes the transitional shims, so there is a single parser path for both indexing and the extractText tool.

Before this PR, two parsing stacks coexisted: the legacy legacy_loaders/ (BaseLoader registry) reached through the DocSerializer Ray actor + adapter + bridge, and the new DocumentParser registry. This consolidates on the latter.

Commits (reviewable in order)

  1. add content-type parser dispatcher and in-process file serializerParserDispatcher routes each Document to a concrete parser by content_type, resolving the PDF/audio backends from config.loader.file_loaders (so behaviour matches the GPU pools bootstrap provisions). ParserFileSerializer runs the dispatcher in-process for extractText. Adds svg/gif/webp/bmp image detection.
  2. route indexing and extractText through the parser dispatcher — wires IndexerPool and the conversion service to the new stack, builds the captioning VLM from config and injects it into the indexing pipeline, and requires metadata['file_id'] in _load_document so chunks persist under the caller's file id.
  3. remove legacy loaders and doc-serializer shims — deletes legacy_loaders/** and the DocSerializer actor / adapter / bridge, plus their tests.
  4. docs — drops references to the removed PyMuPDF4LLMLoader.

Not included (separate PRs, by design)

  • Image-captioning improvements (progress bar, concurrent fan-out, VLM semaphore restoration)
  • Recursive-chunking change
  • Deterministic file_id for the data_indexer dev script
  • Local docker-compose tweaks

Verification

  • uv run ruff check clean
  • Full unit suite: 1238 passed on this branch in isolation; the single remaining failure (test_seed_defaults_preserves_endpoint_api_keys) is pre-existing and environment-driven (a global API key overrides the per-endpoint seed defaults), unrelated to these changes.
  • No dangling imports to the deleted modules remain.

Summary by CodeRabbit

Release Notes

  • Documentation

    • Simplified CPU-only PDF parsing guidance and PDFLoader examples/options to prioritize PyMuPDFLoader.
  • New Features

    • Added a global image_captioning option to control captioning for embedded images (standalone images can still be captioned when available).
  • Refactoring

    • Updated the document parsing/serialization flow to run in-process rather than actor-based serialization.
  • Bug Fixes

    • Expanded image type detection to include svg, gif, webp, and bmp.
    • Document loading now requires a valid file_id, and internal metadata keys no longer appear in document metadata.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removes the Ray actor-based document serialization stack (DocSerializer, SerializerRayShim, DocSerializerBridgeParser, and all legacy_loaders shims) and replaces it with an in-process ParserDispatcher and ParserFileSerializer. Updates bootstrap, DI container, indexer pool, indexer actor wiring, and pipeline captioning policy accordingly. Tightens _load_document to require file_id and removes PyMuPDF4LLMLoader from docs.

Changes

In-process Parser Stack Replacing Ray-backed Legacy Loaders

Layer / File(s) Summary
Document model: new image extension support
openrag/core/models/document.py
detect_content_type() maps svg, gif, webp, and bmp to DocumentType.IMAGE, enabling the new dispatcher to route these as images.
ParserDispatcher: backend routing, lazy construction, VLM gating
openrag/services/workers/parsers/parser_dispatcher.py
Introduces ParserDispatcher routing Document.content_type to lazily built, cached concrete parsers via parser_registry. Translates legacy loader config names to backend identifiers for PDF/audio. Builds .eml attachment sub-parsers with graceful fallback. Adds build_parser_dispatcher and build_caption_vlm factories.
ParserDispatcher unit tests
tests/unit/services/workers/parsers/test_parser_dispatcher.py
Comprehensive tests for backend resolution (filename suffix + DocumentType mapping), PDF/audio variant selection, unsupported config error handling, dispatch-to-cached-backend behavior, and VLM gating.
ParserFileSerializer: in-process FileSerializer
openrag/services/workers/parsers/file_serializer.py
Adds ParserFileSerializer that reads file bytes asynchronously, constructs a Document, dispatches through ParserDispatcher, optionally captions images via VLM, and returns joined text blocks. Adds build_file_serializer factory.
DI container and orchestrator wiring
openrag/di/container.py, openrag/core/indexing/serializer.py, openrag/services/orchestrators/conversion_service.py
ServiceContainer.conversion_service replaces SerializerRayShim/from_ray_namespace() with build_file_serializer(). Module docstrings in serializer port and conversion service updated to describe in-process FileSerializer path.
Worker bootstrap cleanup
openrag/services/workers/bootstrap.py
Removes get_serializer() call and DocSerializer from startup actor list. Changes Docling loader dispatch to match "DoclingLoader" instead of "DoclingLoader2".
IndexerPool: parser dispatcher and VLM wiring
openrag/services/workers/indexer_pool.py
IndexerPool.__init__ builds parser dispatcher and optional caption VLM via build_parser_dispatcher and build_caption_vlm factories instead of DocSerializerBridgeParser. Passes vlm to build_indexing_pipeline.
IndexerPool tests: dispatcher and VLM wiring updates
tests/unit/services/workers/test_indexer_pool.py
Test updates imports and monkeypatches to stub build_parser_dispatcher and build_caption_vlm instead of DocSerializerBridgeParser. Extends test config with image_captioning flag.
IndexerWorker: strict file_id contract
openrag/services/workers/indexer_actor.py
_load_document drops indexation_config parameter, requires metadata["file_id"] (raises ValueError if absent), sets Document.id/filename from it, and passes metadata as-is without injecting internal keys.
IndexerWorker tests: file_id requirement and metadata contract
tests/unit/services/workers/test_indexer_worker.py
Tests updated to require file_id, assert doc.id matches, verify _openrag-prefixed internal keys do not leak, update failure-path metadata calls with required file_id. Removes tests for dropped indexation_config attachment.
IndexingPipeline: captioning policy gate and document-type awareness
openrag/services/workers/pipeline_builder.py
Introduces image_captioning: bool = True flag on IndexingPipeline and build_indexing_pipeline. Moves captioning policy logic into _should_caption: always caption standalone DocumentType.IMAGE; caption embedded images only when both global flag and per-partition config are enabled.
IndexingPipeline tests: captioning policy validation
tests/unit/services/workers/test_pipeline_builder.py
Adds tests validating standalone images are always captioned when image_captioning=False, while embedded images are not captioned when globally disabled.
API and module comments
openrag/api/main.py, openrag/api/routers/admin/tools.py
Inline comments and module docstrings updated to reflect removal of Ray DocSerializer and adoption of in-process FileSerializer.
User documentation: PDFLoader guidance
README.md, docs/content/docs/documentation/env_vars.md, docs/content/docs/getting_started/quickstart.mdx
Removes PyMuPDF4LLMLoader from CPU-only deployment guidance, PDFLoader option lists, and example environment variable values. Uses PyMuPDFLoader as the sole lightweight PDF option.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    Note over ConversionService,ParserFileSerializer: New in-process path (replaces Ray DocSerializer)
  end
  participant ConversionService
  participant ParserFileSerializer
  participant ParserDispatcher
  participant ConcreteParser

  ConversionService->>ParserFileSerializer: serialize(file_path, metadata)
  ParserFileSerializer->>ParserFileSerializer: asyncio.to_thread(read file bytes)
  ParserFileSerializer->>ParserFileSerializer: Document.detect_content_type(filename)
  ParserFileSerializer->>ParserDispatcher: parse(document)
  ParserDispatcher->>ParserDispatcher: _resolve_backend(content_type, filename)
  ParserDispatcher->>ParserDispatcher: _get(backend_name) → _build if cache miss
  ParserDispatcher->>ConcreteParser: parse(document)
  ConcreteParser-->>ParserDispatcher: ProcessedDocument
  ParserDispatcher-->>ParserFileSerializer: ProcessedDocument
  opt VLM configured and image_captioning enabled
    ParserFileSerializer->>ParserFileSerializer: caption_images(vlm, text_blocks)
  end
  ParserFileSerializer-->>ConversionService: joined text blocks
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • linagora/openrag#438: Both PRs touch the worker bootstrap/doc-serializer legacy loader wiring (e.g., openrag/services/workers/bootstrap.py and the doc_serializer*/legacy loader registry modules), with #438 repointing/remapping legacy loader paths and the main PR removing/retiring that legacy serializer/bridge layer in favor of the new in-process serialization/dispatch flow.
  • linagora/openrag#516: The main PR adds global/per-partition gating so caption_stage only runs for allowed image documents, while the retrieved PR changes caption_stage itself to caption images concurrently under a shared VLM semaphore with proper sibling-task cancellation on failures.

Suggested reviewers

  • andyne13

Poem

🐇 Hop hop, no more Ray actors in sight,
The legacy loaders took their final flight.
ParserDispatcher now routes with glee,
In-process and swift, as parsing should be!
file_id required—no fallback, no fuss,
The rabbit refactored—all aboard the bus! 🚌

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: removing legacy loaders and doc-serializer shims as part of a parser consolidation refactor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/remove-parser-shims

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added breaking-change Change of behavior after upgrade refactor labels Jun 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/documentation/env_vars.md`:
- Line 25: The PDFLoader documentation has two issues to fix: First, remove the
duplicate `PyMuPDFLoader` entry from the available options list in the table row
so each loader (PyMuPDFLoader, MarkerLoader, and DotsOCRLoader) is listed only
once. Second, fix the subject-verb disagreement in the description by changing
"PyMuPDFLoader are lightweight" to "PyMuPDFLoader is a lightweight" to match the
singular noun form of the class name.

In `@openrag/services/workers/bootstrap.py`:
- Around line 67-77: The match statement in the get_marker_pool() function only
handles the "DoclingLoader" case, but the _PDF_BACKENDS dictionary in
parser_dispatcher.py also includes "DoclingLoader2" mapped to the "docling"
backend. This creates a contract mismatch where runtime config using
"DoclingLoader2" will cause the dispatcher to request a DoclingPool actor that
bootstrap never creates. Add a match case in get_marker_pool() to handle
"DoclingLoader2" that returns the same actor as "DoclingLoader", or
alternatively remove "DoclingLoader2" from _PDF_BACKENDS if it's no longer
supported per the deferred integration decision.

In `@openrag/services/workers/parsers/parser_dispatcher.py`:
- Around line 22-25: The logger import at lines 22-25 is using
core.utils.logging instead of the standardized openrag/utils/logger utility.
Change the import statement to use get_logger from openrag/utils/logger instead.
Additionally, the logging calls at lines 129 and 145 in the parser_dispatcher
module need to bind contextual information (backend, extension, and when
available file_id and partition) to the log messages to maintain queryability
and consistency with the repository's structured logging guidelines. Update each
logger call to include this bound context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2940d1b0-efa4-4119-8d36-0aa27a5c8544

📥 Commits

Reviewing files that changed from the base of the PR and between 14301a4 and 18a8383.

📒 Files selected for processing (46)
  • README.md
  • docs/content/docs/documentation/env_vars.md
  • docs/content/docs/getting_started/quickstart.mdx
  • openrag/api/main.py
  • openrag/api/routers/admin/tools.py
  • openrag/core/indexing/serializer.py
  • openrag/core/models/document.py
  • openrag/di/container.py
  • openrag/services/orchestrators/conversion_service.py
  • openrag/services/workers/bootstrap.py
  • openrag/services/workers/indexer_actor.py
  • openrag/services/workers/indexer_pool.py
  • openrag/services/workers/parsers/doc_serializer.py
  • openrag/services/workers/parsers/doc_serializer_adapter.py
  • openrag/services/workers/parsers/doc_serializer_bridge.py
  • openrag/services/workers/parsers/file_serializer.py
  • openrag/services/workers/parsers/legacy_loaders/CustomDocLoader.py
  • openrag/services/workers/parsers/legacy_loaders/CustomHTMLLoader.py
  • openrag/services/workers/parsers/legacy_loaders/__init__.py
  • openrag/services/workers/parsers/legacy_loaders/audio/__init__.py
  • openrag/services/workers/parsers/legacy_loaders/audio/local_whisper.py
  • openrag/services/workers/parsers/legacy_loaders/audio/openai.py
  • openrag/services/workers/parsers/legacy_loaders/base.py
  • openrag/services/workers/parsers/legacy_loaders/doc.py
  • openrag/services/workers/parsers/legacy_loaders/docx.py
  • openrag/services/workers/parsers/legacy_loaders/eml_loader.py
  • openrag/services/workers/parsers/legacy_loaders/image.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/__init__.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling2.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/dotsocr.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/marker.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/openai.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/pymupdf.py
  • openrag/services/workers/parsers/legacy_loaders/pptx_loader.py
  • openrag/services/workers/parsers/legacy_loaders/txt_loader.py
  • openrag/services/workers/parsers/parser_dispatcher.py
  • tests/unit/services/workers/parsers/legacy_loaders/audio/test_openai.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_base_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_customdocloader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_doc_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_docx_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_eml_recursion.py
  • tests/unit/services/workers/parsers/test_doc_serializer_bridge.py
  • tests/unit/services/workers/parsers/test_parser_dispatcher.py
  • tests/unit/services/workers/test_indexer_worker.py
💤 Files with no reviewable changes (30)
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/init.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/openai.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_customdocloader.py
  • openrag/services/workers/parsers/legacy_loaders/docx.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_base_loader.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/pymupdf.py
  • openrag/services/workers/parsers/legacy_loaders/base.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling2.py
  • openrag/services/workers/parsers/legacy_loaders/image.py
  • openrag/services/workers/parsers/legacy_loaders/txt_loader.py
  • openrag/services/workers/parsers/legacy_loaders/CustomDocLoader.py
  • openrag/services/workers/parsers/legacy_loaders/CustomHTMLLoader.py
  • tests/unit/services/workers/parsers/test_doc_serializer_bridge.py
  • openrag/services/workers/parsers/legacy_loaders/pptx_loader.py
  • openrag/services/workers/parsers/legacy_loaders/init.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling.py
  • openrag/services/workers/parsers/legacy_loaders/audio/init.py
  • openrag/services/workers/parsers/doc_serializer_adapter.py
  • openrag/services/workers/parsers/legacy_loaders/eml_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_eml_recursion.py
  • openrag/services/workers/parsers/doc_serializer_bridge.py
  • openrag/services/workers/parsers/legacy_loaders/audio/openai.py
  • openrag/services/workers/parsers/legacy_loaders/doc.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/dotsocr.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_docx_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/audio/test_openai.py
  • openrag/services/workers/parsers/doc_serializer.py
  • openrag/services/workers/parsers/legacy_loaders/audio/local_whisper.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/marker.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_doc_loader.py

Comment thread docs/content/docs/documentation/env_vars.md Outdated
Comment thread openrag/services/workers/bootstrap.py
Comment thread openrag/services/workers/parsers/parser_dispatcher.py
…file serializer

Introduce ParserDispatcher, which routes each Document to a concrete parser in
the core/indexing/parsers stack by content type, resolving the PDF and audio
backends from config.loader.file_loaders so behaviour matches the GPU pools
bootstrap provisions. Add ParserFileSerializer, an in-process FileSerializer
that runs the dispatcher for the extractText path.

Extend DocumentType detection with svg/gif/webp/bmp image extensions and update
the FileSerializer port docstring to describe the in-process implementation.
…dispatcher

Wire IndexerPool and the conversion service to the parser dispatcher and the
in-process file serializer, and build the captioning VLM from config to inject
into the indexing pipeline. Require metadata['file_id'] in _load_document so
chunks persist under the caller's file id instead of a random uuid. Drop the
DocSerializer actor from bootstrap and refresh the now-stale docstrings.
Delete the legacy_loaders stack and the DocSerializer actor / adapter / bridge
shims, now that indexing and extractText run entirely on the parser dispatcher,
together with their unit tests.
@Ahmath-Gadji
Ahmath-Gadji force-pushed the refactor/remove-parser-shims branch from 18a8383 to e2cc7cc Compare June 18, 2026 12:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/services/workers/parsers/test_parser_dispatcher.py`:
- Around line 42-56: The parametrized test matrix in the
`@pytest.mark.parametrize` decorator only includes a single test case for
DocumentType.IMAGE with the .png extension, lacking coverage for the newly
supported image formats. Add four additional test cases to the parametrize
matrix for the newly supported image suffixes (.svg, .gif, .webp, and .bmp),
each paired with DocumentType.IMAGE as the content_type and "image" as the
expected_backend value, positioned alongside the existing .png case to ensure
comprehensive routing test coverage for all supported image formats.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3b8daf5d-3820-4de4-831f-135770cfc93e

📥 Commits

Reviewing files that changed from the base of the PR and between 18a8383 and e2cc7cc.

📒 Files selected for processing (46)
  • README.md
  • docs/content/docs/documentation/env_vars.md
  • docs/content/docs/getting_started/quickstart.mdx
  • openrag/api/main.py
  • openrag/api/routers/admin/tools.py
  • openrag/core/indexing/serializer.py
  • openrag/core/models/document.py
  • openrag/di/container.py
  • openrag/services/orchestrators/conversion_service.py
  • openrag/services/workers/bootstrap.py
  • openrag/services/workers/indexer_actor.py
  • openrag/services/workers/indexer_pool.py
  • openrag/services/workers/parsers/doc_serializer.py
  • openrag/services/workers/parsers/doc_serializer_adapter.py
  • openrag/services/workers/parsers/doc_serializer_bridge.py
  • openrag/services/workers/parsers/file_serializer.py
  • openrag/services/workers/parsers/legacy_loaders/CustomDocLoader.py
  • openrag/services/workers/parsers/legacy_loaders/CustomHTMLLoader.py
  • openrag/services/workers/parsers/legacy_loaders/__init__.py
  • openrag/services/workers/parsers/legacy_loaders/audio/__init__.py
  • openrag/services/workers/parsers/legacy_loaders/audio/local_whisper.py
  • openrag/services/workers/parsers/legacy_loaders/audio/openai.py
  • openrag/services/workers/parsers/legacy_loaders/base.py
  • openrag/services/workers/parsers/legacy_loaders/doc.py
  • openrag/services/workers/parsers/legacy_loaders/docx.py
  • openrag/services/workers/parsers/legacy_loaders/eml_loader.py
  • openrag/services/workers/parsers/legacy_loaders/image.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/__init__.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling2.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/dotsocr.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/marker.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/openai.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/pymupdf.py
  • openrag/services/workers/parsers/legacy_loaders/pptx_loader.py
  • openrag/services/workers/parsers/legacy_loaders/txt_loader.py
  • openrag/services/workers/parsers/parser_dispatcher.py
  • tests/unit/services/workers/parsers/legacy_loaders/audio/test_openai.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_base_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_customdocloader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_doc_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_docx_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_eml_recursion.py
  • tests/unit/services/workers/parsers/test_doc_serializer_bridge.py
  • tests/unit/services/workers/parsers/test_parser_dispatcher.py
  • tests/unit/services/workers/test_indexer_worker.py
💤 Files with no reviewable changes (30)
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/init.py
  • openrag/services/workers/parsers/doc_serializer.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_customdocloader.py
  • openrag/services/workers/parsers/legacy_loaders/init.py
  • openrag/services/workers/parsers/legacy_loaders/CustomDocLoader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_base_loader.py
  • openrag/services/workers/parsers/legacy_loaders/audio/local_whisper.py
  • openrag/services/workers/parsers/legacy_loaders/CustomHTMLLoader.py
  • openrag/services/workers/parsers/legacy_loaders/docx.py
  • openrag/services/workers/parsers/legacy_loaders/audio/openai.py
  • openrag/services/workers/parsers/doc_serializer_adapter.py
  • tests/unit/services/workers/parsers/test_doc_serializer_bridge.py
  • openrag/services/workers/parsers/legacy_loaders/audio/init.py
  • openrag/services/workers/parsers/legacy_loaders/base.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_docx_loader.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_doc_loader.py
  • openrag/services/workers/parsers/legacy_loaders/pptx_loader.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/marker.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/pymupdf.py
  • openrag/services/workers/parsers/legacy_loaders/txt_loader.py
  • openrag/services/workers/parsers/legacy_loaders/image.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/openai.py
  • openrag/services/workers/parsers/legacy_loaders/doc.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/docling2.py
  • openrag/services/workers/parsers/doc_serializer_bridge.py
  • openrag/services/workers/parsers/legacy_loaders/pdf_loaders/dotsocr.py
  • tests/unit/services/workers/parsers/legacy_loaders/test_eml_recursion.py
  • tests/unit/services/workers/parsers/legacy_loaders/audio/test_openai.py
  • openrag/services/workers/parsers/legacy_loaders/eml_loader.py
✅ Files skipped from review due to trivial changes (7)
  • README.md
  • openrag/api/routers/admin/tools.py
  • openrag/api/main.py
  • docs/content/docs/documentation/env_vars.md
  • openrag/core/indexing/serializer.py
  • openrag/services/orchestrators/conversion_service.py
  • docs/content/docs/getting_started/quickstart.mdx
🚧 Files skipped from review as they are similar to previous changes (8)
  • openrag/core/models/document.py
  • openrag/services/workers/parsers/file_serializer.py
  • openrag/di/container.py
  • openrag/services/workers/indexer_pool.py
  • tests/unit/services/workers/test_indexer_worker.py
  • openrag/services/workers/bootstrap.py
  • openrag/services/workers/indexer_actor.py
  • openrag/services/workers/parsers/parser_dispatcher.py

Comment thread tests/unit/services/workers/parsers/test_parser_dispatcher.py
The parser migration removed the PyMuPDF4LLMLoader backend; update the README,
env-vars reference, and quickstart so PyMuPDFLoader is the only lightweight PDF
option listed.
@Ahmath-Gadji
Ahmath-Gadji force-pushed the refactor/remove-parser-shims branch from e2cc7cc to 40dcac6 Compare June 18, 2026 13:05
…GE_CAPTIONING

The legacy ImageLoader captioned standalone image files unconditionally —
IMAGE_CAPTIONING only ever gated images *embedded* in other documents.
Routing indexing through the parser dispatcher collapsed both into a single
gate (build_caption_vlm returned None when captioning was off), so uploading
an image with IMAGE_CAPTIONING=false produced no text and zero indexed
documents (regressing the SVG indexing API test).

Decouple VLM availability from caption policy:
- build_caption_vlm builds the VLM whenever a VLM endpoint is configured.
- IndexingPipeline._should_caption() always captions standalone image files,
  and gates embedded images by the global image_captioning flag plus the
  per-partition enable_image_captioning setting.
- ParserFileSerializer (extractText) applies the same policy.
Lock the parser-migration contract that these extensions resolve to the
image backend (the IMAGE content-type mapping added in this PR).

@hedhoud hedhoud left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and found three points worth addressing before merge.

1. Nested .eml attachments are no longer parsed

.eml means an email file. Before, if an indexed email had another email attached inside it, OpenRAG could parse the attached email too.

With this PR, .eml attachments are excluded from the parser dispatcher. So for this kind of structure:

Email A
 └── attached Email B
      └── body text

Before, Email B body was indexed. Now, Email B body may be skipped and only the attachment header/metadata may remain.

This is fine if intentional, but then it should be documented. Otherwise, .eml parsing should be added back with a recursion limit so we avoid infinite nested emails.

2. Original filename is lost before parser dispatch

In indexer_actor.py, the document filename is set from the internal file_id. The problem is that file_id may not contain the real filename or extension.

Example:

real file: audio.flac
file_id: 123456789

The parser then sees 123456789, so it cannot know this was a .flac file. This is risky for audio/video files because parser selection can depend on extensions such as .mp4, .flac, or .ogg.

Better behavior would be to keep both concepts separate:

  • Document.id = OpenRAG internal file id
  • Document.filename = original uploaded filename, or the saved path filename

3. Large file reads block the async actor

The indexer actor is async, but the file content is read synchronously with read_bytes(). For small files this is not a big issue, but for large PDFs it can block the actor event loop and reduce parallelism.

This matters because even if Ray actor concurrency is configured, one large blocking file read can slow down other in-flight tasks. The read should be moved to a thread so the async actor can continue handling other work.

Merge note

Overall, I think the PR is directionally good, but the .eml regression and filename handling are important to clarify or fix before merge.

Resolve the parser-shim branch against the latest hexagonal work, restore bounded nested email attachment parsing, preserve original filenames for parser dispatch, and avoid blocking the async indexer actor on large file reads.
@hedhoud

hedhoud commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Quick update: I resolved the conflict with refactor/hexagonal and fixed the parser cleanup issues we discussed. GitHub Actions are green now; CodeRabbit is still running.

I’m not merging this myself. @Ahmath-Gadji could you review it when you have a moment?

…eanup

Resolves the indexer_actor.py conflict between the parser refactor and #529:
keeps the async, file_id-required _load_document from the refactor and unions
in #529's shared indexed_at threading (process_file reads row['indexed_at']
after pipeline.run and passes it to _write_catalog_record, which forwards it to
both the add/update catalog writes). All other #529 changes (store stage,
vector_store, milvus_store, document_repo, schema, migration) apply cleanly.
@Ahmath-Gadji
Ahmath-Gadji merged commit df20f8e into refactor/hexagonal Jun 22, 2026
6 checks passed
@Ahmath-Gadji
Ahmath-Gadji deleted the refactor/remove-parser-shims branch June 22, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Change of behavior after upgrade refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants