Skip to content

Add image_captioning_url config param to control indexing of image URL - #217

Merged
paultranvan merged 4 commits into
devfrom
fix/error-image-desc
Jan 21, 2026
Merged

Add image_captioning_url config param to control indexing of image URL#217
paultranvan merged 4 commits into
devfrom
fix/error-image-desc

Conversation

@paultranvan

@paultranvan paultranvan commented Jan 19, 2026

Copy link
Copy Markdown
Collaborator

When image_captioning is true, we used to try to caption with VLM any image found as a URL in markdown.
This can lead to errors if the image is wrong or not reachable. Also, this capability might be not expected, because it is a costly operation and the image could be irrelevant to caption, because it is an icon for example.

So we add a new param image_captioning_url to control this behaviour.

This also make a refactoring of the loaders, to mutualize code and be consistent for image captioning

Summary by CodeRabbit

  • New Features

    • Image captioning is enabled by default for supported file types.
    • Optionally caption HTTP/HTTPS image URLs in markdown via a new IMAGE_CAPTIONING_URL setting.
    • Unified, centralized image captioning flow across PDFs, Word, PowerPoint, and Markdown.
  • Documentation

    • Added documentation entry for IMAGE_CAPTIONING_URL (default: true).
  • Tests

    • Added tests and a fixture covering captioning for embedded data-URI and HTTP markdown images.

✏️ Tip: You can customize this high-level summary in your review settings.

@paultranvan paultranvan added the feat Add a new feature label Jan 19, 2026
@coderabbitai

coderabbitai Bot commented Jan 19, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Centralizes image-captioning into BaseLoader (new async caption utilities and regex patterns), adds loader.image_captioning_url config, updates loaders to delegate captioning (removing per-loader helpers), and adds tests/fixture for data-URI and HTTP image captioning.

Changes

Cohort / File(s) Summary
Configuration & Docs
\.hydra_config/config.yaml, docs/content/docs/documentation/env_vars.md, CLAUDE.md
Add loader.image_captioning_url (env var IMAGE_CAPTIONING_URL, default true). Update documentation describing Loader base API, image-captioning behavior, and router list.
Base Loader Core
openrag/components/indexer/loaders/base.py
Add HTTP_IMAGE_PATTERN and DATA_URI_IMAGE_PATTERN; set image_captioning default to True and add image_captioning_url; introduce async caption_images() and replace_markdown_images_with_captions() with concurrent captioning and refined VLM/error handling.
Loader Delegation / Refactors
openrag/components/indexer/loaders/docx.py, openrag/components/indexer/loaders/pptx_loader.py, openrag/components/indexer/loaders/txt_loader.py, openrag/components/indexer/loaders/pdf_loaders/*
Remove per-loader get/_get_captions helpers and tqdm imports; replace inline captioning with BaseLoader methods (caption_images, replace_markdown_images_with_captions); use self.image_captioning and image_captioning_url flags; adjust markdown/image placeholder handling.
Tests & Fixtures
tests/api_tests/conftest.py, tests/api_tests/test_indexer.py
Add sample_markdown_with_image fixture (data-URI image) and TestImageCaptioning tests for data-URI and HTTP image captioning; tests gated by IMAGE_CAPTIONING_ENABLED.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Loader as Loader
  participant Base as BaseLoader
  participant VLM as VLM API
  participant Indexer as Indexer

  Client->>Loader: Upload file (md/docx/pdf/...)
  Loader->>Loader: extract content and images (embedded & linked)
  Loader->>Base: caption_images(list_of_PIL_images)
  Base->>VLM: async requests for image descriptions
  VLM-->>Base: captions
  Base-->>Loader: captions (ordered)
  Loader->>Base: replace_markdown_images_with_captions(content, flags)
  Base-->>Loader: content with captions substituted
  Loader->>Indexer: save indexed document
  Indexer-->>Client: indexing complete / content available
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

fix

Poem

🐰
I hop through bytes and sniff each frame,
BaseLoader learns and gives each picture a name,
Embedded or linked, captions now align,
One burrow for captions — tidy and fine,
Hooray! I munch bugs and nibble time. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a new image_captioning_url config parameter to control whether HTTP/HTTPS image URLs are fetched and captioned during indexing.

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

✨ Finishing touches
  • 📝 Generate docstrings

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 the fix Fix issue label Jan 19, 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: 2

🤖 Fix all issues with AI agents
In @.hydra_config/config.yaml:
- Around line 92-95: The YAML default for loader.image_captioning_url is
inverted; change its decoded default from true to false so HTTP/HTTPS captioning
is opt-in. Update the loader.image_captioning_url entry (and verify
loader.image_captioning remains true by default if intended) to use
${oc.decode:${oc.env:IMAGE_CAPTIONING_URL, false}} and ensure the environment
variable handling matches the intended opt-in behavior.

In `@openrag/components/indexer/loaders/base.py`:
- Around line 38-39: The loader currently enables external HTTP/HTTPS captioning
by default because self.image_captioning_url uses
self.config.loader.get("image_captioning_url", True); change this to opt-in by
using False as the default so external fetching is disabled unless explicitly
set. Update the reference in openrag/components/indexer/loaders/base.py where
image_captioning_url is assigned, and verify any consumers of the attribute
(e.g., code paths in the image captioning flow) respect the boolean; also adjust
any tests or docs that assumed the old default.
🧹 Nitpick comments (2)
tests/api_tests/test_indexer.py (2)

292-320: Make the data‑URI assertion deterministic.
The "image" fallback can pass even if captioning didn’t occur; assert the wrapper tag instead to reduce false positives.

Proposed tweak
-        assert "<image_description>" in indexed_content or "image" in indexed_content.lower()
+        assert "<image_description>" in indexed_content, "Expected caption wrapper in indexed content"

321-366: Consider tying the HTTP-URL expectation to config.
As written, the test passes regardless of image_captioning_url, which can hide regressions. Parameterizing by the flag (or reading it from test config) would make the behavior explicit.

Comment thread .hydra_config/config.yaml
Comment thread openrag/components/indexer/loaders/base.py
@paultranvan paultranvan removed the fix Fix issue label Jan 19, 2026

@Ahmath-Gadji Ahmath-Gadji 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.

LGTM.
I've tested PDFs files with images and it works fine

@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

🤖 Fix all issues with AI agents
In `@tests/api_tests/test_indexer.py`:
- Around line 321-324: The test's fallback assertion is too broad and can match
original alt text; update the assertions in test_indexer.py to require the
explicit caption tag and also assert no markdown image syntax remains: keep the
existing check that "data:image/png;base64" is not in indexed_content, replace
the or condition with a strict assert that "<image_description>" is present in
indexed_content, and add an assertion that the markdown image pattern "![" (or
"![") followed by "](" is not present in indexed_content to ensure images were
captioned and not left as markdown embeds; locate these changes around the
indexed_content assembly using the documents variable in the test.
♻️ Duplicate comments (1)
openrag/components/indexer/loaders/base.py (1)

39-40: Default image_captioning_url should be opt‑in.

This currently enables external URL fetching when the flag is omitted; the PR objective says it should default to disabled.

✅ Proposed fix
-        self.image_captioning_url = self.config.loader.get("image_captioning_url", True)
+        self.image_captioning_url = self.config.loader.get("image_captioning_url", False)

Comment on lines +321 to +324
# Verify image was replaced with caption (not raw data URI)
indexed_content = " ".join(doc.get("page_content", "") for doc in documents)
assert "data:image/png;base64" not in indexed_content, "Image should be captioned, not raw"
assert "<image_description>" in indexed_content or "image" in indexed_content.lower()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assertion may pass spuriously due to alt text containing "image".

The fallback condition "image" in indexed_content.lower() could match the original alt text (e.g., "test image") rather than an actual generated caption, causing false positives if captioning silently fails.

Consider tightening the assertion to require the <image_description> tag specifically, or verify the absence of the markdown image syntax ![...](...) as additional confirmation.

Suggested fix
         # Verify image was replaced with caption (not raw data URI)
         indexed_content = " ".join(doc.get("page_content", "") for doc in documents)
         assert "data:image/png;base64" not in indexed_content, "Image should be captioned, not raw"
-        assert "<image_description>" in indexed_content or "image" in indexed_content.lower()
+        assert "<image_description>" in indexed_content, "Expected caption tag in indexed content"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Verify image was replaced with caption (not raw data URI)
indexed_content = " ".join(doc.get("page_content", "") for doc in documents)
assert "data:image/png;base64" not in indexed_content, "Image should be captioned, not raw"
assert "<image_description>" in indexed_content or "image" in indexed_content.lower()
# Verify image was replaced with caption (not raw data URI)
indexed_content = " ".join(doc.get("page_content", "") for doc in documents)
assert "data:image/png;base64" not in indexed_content, "Image should be captioned, not raw"
assert "<image_description>" in indexed_content, "Expected caption tag in indexed content"
🤖 Prompt for AI Agents
In `@tests/api_tests/test_indexer.py` around lines 321 - 324, The test's fallback
assertion is too broad and can match original alt text; update the assertions in
test_indexer.py to require the explicit caption tag and also assert no markdown
image syntax remains: keep the existing check that "data:image/png;base64" is
not in indexed_content, replace the or condition with a strict assert that
"<image_description>" is present in indexed_content, and add an assertion that
the markdown image pattern "![" (or "![") followed by "](" is not present in
indexed_content to ensure images were captioned and not left as markdown embeds;
locate these changes around the indexed_content assembly using the documents
variable in the test.

paultranvan and others added 4 commits January 21, 2026 18:24
- Add IMAGE_CAPTIONING_URL config option (default: false) to control
  whether HTTP/HTTPS image URLs should be fetched and captioned
- MarkdownLoader: add missing self.image_captioning guard and skip
  HTTP URLs when IMAGE_CAPTIONING_URL is false (fixes badge errors)
- DocxLoader: add HTTP URL detection and optional captioning for
  linked images based on IMAGE_CAPTIONING_URL setting
- PPTXLoader: add missing self.image_captioning guard for consistency
- Update CLAUDE.md with loader image captioning pattern documentation
- Document IMAGE_CAPTIONING_URL env var in docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add shared regex patterns HTTP_IMAGE_PATTERN and DATA_URI_IMAGE_PATTERN
- Add caption_images() for list of PIL images with cancellation handling
- Add replace_markdown_images_with_captions() for markdown content
- Simplify MarkdownLoader, DocxLoader, PPTXLoader, MarkerLoader, DoclingLoader
  to use the new shared methods instead of duplicated code
- Update CLAUDE.md with new BaseLoader methods documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add sample_markdown_with_image fixture
- Add TestImageCaptioning class with tests for data URI and HTTP URL handling
- Skip tests when IMAGE_CAPTIONING is disabled (CI)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Catch BadRequestError separately to avoid full stack traces
- Log as warning with truncated error message

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added the fix Fix issue label Jan 21, 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: 1

🤖 Fix all issues with AI agents
In `@docs/content/docs/documentation/env_vars.md`:
- Around line 21-22: Update the docs to make IMAGE_CAPTIONING_URL opt-in: change
its default value from `true` to `false` and adjust the description to state
that when set to `true` the app will fetch HTTP/HTTPS image URLs in markdown
files and use the VLM to describe them; keep IMAGE_CAPTIONING description as-is
but ensure wording clarifies that URL fetching only happens if
IMAGE_CAPTIONING_URL is enabled.
♻️ Duplicate comments (2)
.hydra_config/config.yaml (1)

92-94: Default for image_captioning_url should be opt‑in (false).

The current default enables HTTP/HTTPS captioning by default, which conflicts with the PR intent.

✅ Proposed fix
-  image_captioning_url: ${oc.decode:${oc.env:IMAGE_CAPTIONING_URL, true}}
+  image_captioning_url: ${oc.decode:${oc.env:IMAGE_CAPTIONING_URL, false}}
openrag/components/indexer/loaders/base.py (1)

39-40: Default image_captioning_url should be False per PR objectives.

The PR description states HTTP/HTTPS URLs should not be fetched/captioned by default (opt-in). The current default of True contradicts this intent.

-        self.image_captioning_url = self.config.loader.get("image_captioning_url", True)
+        self.image_captioning_url = self.config.loader.get("image_captioning_url", False)
🧹 Nitpick comments (1)
openrag/components/indexer/loaders/base.py (1)

180-186: Dead cancellation logic—coroutines lack .cancel().

The tasks list contains coroutines, not asyncio.Task objects. Coroutines don't have a cancel() method, so hasattr(task, "cancel") will be False and this loop never executes. The code is harmless but misleading.

Either remove the ineffective block (as done in replace_markdown_images_with_captions) or wrap coroutines with asyncio.create_task() if cancellation is truly needed.

Option 1: Remove dead code (simpler)
         try:
             results = await tqdm.gather(*tasks, desc=desc)
         except asyncio.CancelledError:
-            for task in tasks:
-                if hasattr(task, "cancel"):
-                    task.cancel()
             raise
         return results

Comment on lines 21 to +22
| `IMAGE_CAPTIONING` | `bool` | `true` | If `true`, an LLM is used to describe images and convert them into text using a [specific prompt](https://github.com/linagora/openrag/blob/main/prompts/example1/image_captioning_tmpl.txt). The image in files are replaced by their descriptions |
| `IMAGE_CAPTIONING_URL` | `bool` | `true` | If `true`, HTTP/HTTPS image URLs in markdown files are fetched and described by the VLM. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Align IMAGE_CAPTIONING_URL default with opt‑in behavior.

Docs show default true, but the PR intent is opt‑in to avoid fetching HTTP/HTTPS images by default. Please update the default and wording accordingly.

✏️ Proposed doc fix
-| `IMAGE_CAPTIONING_URL` | `bool` | `true` | If `true`, HTTP/HTTPS image URLs in markdown files are fetched and described by the VLM. |
+| `IMAGE_CAPTIONING_URL` | `bool` | `false` | If `true`, HTTP/HTTPS image URLs in markdown files are fetched and described by the VLM. |
🤖 Prompt for AI Agents
In `@docs/content/docs/documentation/env_vars.md` around lines 21 - 22, Update the
docs to make IMAGE_CAPTIONING_URL opt-in: change its default value from `true`
to `false` and adjust the description to state that when set to `true` the app
will fetch HTTP/HTTPS image URLs in markdown files and use the VLM to describe
them; keep IMAGE_CAPTIONING description as-is but ensure wording clarifies that
URL fetching only happens if IMAGE_CAPTIONING_URL is enabled.

@paultranvan
paultranvan merged commit 8e273d7 into dev Jan 21, 2026
4 checks passed
@Ahmath-Gadji
Ahmath-Gadji deleted the fix/error-image-desc branch January 23, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat Add a new feature fix Fix issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants