Skip to content

fix(rag): remove inline images and keep the text between them - #45202

Open
TINGyu123644 wants to merge 2 commits into
langgenius:mainfrom
TINGyu123644:fix/markdown-image-link-removal
Open

TINGyu123644 wants to merge 2 commits into
langgenius:mainfrom
TINGyu123644:fix/markdown-image-link-removal

Conversation

@TINGyu123644

Copy link
Copy Markdown

What this fixes

Both reports point at the same two helpers in api/core/rag/extractor/markdown_extractor.py:

How

remove_images now matches both forms with non-greedy character classes, so only the image markup is dropped and the surrounding text stays:

content = re.sub(r"!\[\[[^\]]*\]\]", "", content)
return re.sub(r"!\[[^\]]*\]\([^)]*\)", "", content)

remove_hyperlinks skips anything preceded by !, so an image is never half-unwrapped:

return re.sub(r"(?<!!)\[([^\]]*)\]\([^)]*\)", r"\1", content)

The replacement for a link is still its text, as before.

Tests

Four cases added to api/tests/unit_tests/core/rag/extractor/test_markdown_extractor.py, covering both issues at helper level and through extract() with a real temporary file.

# on main
4 failed, 1 passed  (the four new cases fail)

# with this change
17 passed

Only that unit module was run locally (pytest, API deps installed via uv); the rest of the suite is left to CI.

The change and the tests were drafted with an AI assistant and reviewed by me before opening this PR.

remove_images only knew the wiki form `![[name]]`, so a standard
`![alt](url)` survived even with remove_images on, and its greedy `(.*)`
deleted everything between two wiki images.

remove_hyperlinks also matched the `[alt](url)` part of an image, so with
image removal off a link sweep left a stray `!` and destroyed the image.

Match both image forms with non-greedy character classes, and skip images
when unwrapping links. Adds unit tests for both reports.

Fixes langgenius#45183
Fixes langgenius#45184
@github-actions github-actions Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Oct 31, 2026

@GYJ99 GYJ99 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for consolidating #45183 and #45184 and adding regressions for both image syntaxes. I found one valid Markdown URL shape that the new inline-image regex does not consume completely: destinations containing balanced parentheses.

With the exact new expression re.sub(r"!\\[[^\\]]*\\]\\([^)]*\\)", "", text), an isolated Python check gives:

input:  before ![plot](figures/plot(v2).png) after
actual: before .png) after

The first ) in (v2) terminates the regex before the actual closing parenthesis, so remove_images=True leaves a broken filename fragment in the indexed content. Another example, ![chart](https://example.com/charts/(a)/render.png), leaves /render.png). Both forms are legitimate Markdown destinations.

Could you add a regression for a parenthesized image destination with meaningful surrounding prose and use balanced-destination handling (or a Markdown-aware parser) rather than [^)]*? The new image-removal behavior should not leave URL debris. I reproduced only the regex behavior with Python, not the full Dify extractor tests.

Also, #45185 and #45186 overlap these same helper functions; coordination before merging would avoid competing fixes. AI-assisted code review with ChatGPT.

Review of this PR found that the inline-image matcher stopped at the
first ")" in the destination, so a destination like
![plot](figures/plot(v2).png) left a ".png)" fragment in the indexed
text. Link destinations may contain one level of balanced parentheses
(and backslash escapes), which a [^)]* class cannot express.

Match a destination as a sequence of plain characters, escapes, or one
balanced parenthesized group - in both remove_images and
remove_hyperlinks - and add regressions for parenthesized destinations
with surrounding prose.
@TINGyu123644

Copy link
Copy Markdown
Author

Replying to @GYJ99's review (balanced parentheses in image destinations):

Thanks for the careful review - you found a real gap. The inline-image matcher stopped at the first ) in the destination, so ![plot](figures/plot(v2).png) left a .png) fragment behind.

Fixed in 5006de6 with balanced-destination handling instead of [^)]*: a destination now matches a sequence of plain characters, backslash escapes, or one level of balanced parentheses ((?:[^()\\]|\\.|\([^()]*\))*). The same handling is applied to remove_hyperlinks, which had the identical debris issue on [plot](figures/plot(v2).png).

Regressions added (all fail on the previous commit, pass now - verified locally with the shimmed extractor test suite, 17 -> 20 passed):

  • ![plot](figures/plot(v2).png) and ![chart](https://example.com/charts/(a)/render.png) are fully removed
  • an escaped destination ![esc](a\(b\).png) is fully removed
  • an extract()-level case with surrounding prose (Quarterly revenue rose. ![plot](figures/plot(v2).png) Costs fell.) leaves no debris
  • [plot](figures/plot(v2).png) unwraps to plot under remove_hyperlinks

On coordination: #45185 and #45186 (Zhi-Xiang-Guo, opened earlier the same day) do overlap these helpers - this PR intentionally consolidates both halves (#45183 + #45184) plus the balanced-destination handling. Happy to close this one if maintainers prefer landing #45185/#45186; just say the word.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants