Download Latest Version v1.4.4 source code.zip (3.2 MB)
Home / v1.4.3
Name Modified Size InfoDownloads / Week
Parent folder
README.md 2026-10-21 19.5 kB
v1.4.3 source code.tar.gz 2026-10-21 3.1 MB
v1.4.3 source code.zip 2026-10-21 3.2 MB
Totals: 3 Items   6.3 MB 0

Security

HTML is no longer converted to PDF with LibreOffice (#418)

Since 1.2.4, parse_document() and process_document_complete() sent .html, .htm and .xhtml files to the Office path when the parser was MinerU (and PaddleOCR, from its addition in 1.2.10). That path converts them to PDF with LibreOffice. The conversion fetches the remote URLs the page references, such as images and stylesheets. It also embeds local images the page references, such as file:// <img> sources, into the PDF that is then parsed. Ingesting an untrusted HTML file therefore made outbound requests and put referenced local images into the parsed PDF. BatchParser, the batch CLI, process_documents_batch() and process_documents_with_rag_batch() never picked up HTML. process_folder_complete() did so only when HTML extensions were added to file_extensions or SUPPORTED_FILE_EXTENSIONS.

  • HTML is now accepted only by parsers with a native parse_html entry point. Among the built-in parsers, that is Docling. MinerU, PaddleOCR and custom parsers without parse_html raise a ValueError before any conversion. The error tells you how to select Docling: RAGAnythingConfig(parser="docling"), the PARSER=docling environment variable, or --parser docling on the CLI (#418, [#420]).
  • Parser.convert_office_to_pdf() (and therefore the MinerU and PaddleOCR parse_office_doc()) and MineruParser.parse_document() (which used to try HTML as a PDF) reject HTML as well. PaddleOCRParser.parse_document() already refused HTML as an unsupported format and now raises the same Docling error.
  • Docling's HTML backend does not fetch the page's images. Their caption or alt text is indexed as text (#417).
  • HTML is recognised by file extension. HTML content saved under an Office extension, such as .doc or .xls, still goes to LibreOffice, which makes the same remote requests and embeds the same local images.

Upgrade note: to keep parsing HTML, install Docling (pip install docling; it is not part of raganything[all]) and set RAGAnythingConfig(parser="docling") or PARSER=docling. If you ingested untrusted HTML with MinerU or PaddleOCR, LibreOffice requested the URLs that HTML referenced, and the local images it referenced were embedded in the converted PDF and parsed with it.

What's new

HTML documents with Docling (#405, [#417], [#418])

With parser="docling", HTML files now go through RAG-Anything end to end. BatchParser, the batch CLI, process_documents_batch() and process_documents_with_rag_batch() now find .html, .htm and .xhtml files. parse_document() and process_document_complete() send these files to DoclingParser.parse_html. In 1.4.2, batches skipped HTML, and process_document_complete() with Docling failed with Unsupported office format: .html (#405, @er-s-an).

  • process_folder_complete() still filters on SUPPORTED_FILE_EXTENSIONS, which does not list HTML. Pass file_extensions=[".html", ".htm", ".xhtml"].
  • Pictures without image data, which includes every <img> from Docling's HTML backend, are now skipped. Previously they were indexed as the literal text [Image processing failed: ]. Docling emits the caption, or the alt text when there is no caption, as a separate text item (#417).

Streaming answers from VLM-enhanced image queries (#400)

With a vision model configured, aquery() and aquery_vlm_enhanced() with stream=True used to ignore the flag whenever retrieved images were sent to the vision model, and returned a complete string. They now call vision_model_func(..., stream=True) and return its async iterator unchanged. Non-streaming calls do not pass stream and behave as before. Queries that retrieve no images keep falling back to LightRAG, which streams (@IlyaKhalafi).

Incremental RAG batches finish incomplete ingestion (#393)

process_documents_with_rag_batch(..., incremental=True) only ingested the files it had just parsed. If a file parsed successfully but then failed to index, every later run skipped it as unchanged, so it was never indexed. Files that parsing skips are now checked against the current workspace's doc_status (@dafyy321-pixel).

  • Missing, failed, pending or otherwise incomplete documents go through process_document_complete() again. Their document ID comes from the parse cache. If the workspace has no cached result, the file is parsed once to find the ID.
  • A document is skipped only when both its text and its multimodal processing are complete, and force_multimodal_reprocess is not set. Skipped documents get the rag_results entry {"status": "skipped", "processed": False} and are counted in the new skipped_rag_files. on_batch_complete also receives a skipped count. failed_rag_files now counts only entries whose status is "failed".
  • When the text retry succeeds and the multimodal work had already finished, that work is skipped only if its completion flag survived. A handling document keeps the flag. A failed, pending or processing document loses it when LightRAG rewrites its doc_status (on the default JSON doc-status storage), so its multimodal processing runs again (documented in [#417]).
  • Six ingestion-only options are no longer passed to the parser or included in the incremental parse signature: display_stats, split_by_character, split_by_character_only, doc_id, file_name and force_multimodal_reprocess. If a 1.4.2 incremental RAG batch passed any of them with a value other than None, its files are reparsed once.

Concurrent ingestion keeps each document's multimodal context (#391)

A RAGAnything instance shares its modal processors across calls. Several documents can be ingested at once through one instance, for example by process_folder_complete() with more than one worker, or by concurrent process_document_complete() / insert_content_list() calls. A document's table, image or equation prompts could then contain surrounding text from another document, and both calls still succeeded. Now process_document_complete(), insert_content_list() and process_document_complete_lightrag_api() each keep their own content source, which their child tasks inherit. A modal processor used directly via set_content_source() behaves as before. During ingestion the processor's content_source attribute is no longer overwritten, so it no longer holds the last ingested document afterwards (@dafyy321-pixel).

Behaviour changes

  • HTML requires Docling (#418). MinerU, PaddleOCR and custom parsers without parse_html raise ValueError for .html, .htm and .xhtml files in parse_document() and process_document_complete() instead of converting them with LibreOffice. As in 1.4.2, BatchParser and the batch CLI skip HTML with these parsers, and a file passed explicitly is logged as "Unsupported file type". See Security.
  • Streaming image queries need a vision function that takes stream (#400). With stream=True, vision_model_func is now called with stream=True. A function that accepts neither stream nor **kwargs raises TypeError; previously the call returned a string. The README examples already take **kwargs. Non-streaming calls are unaffected.
  • Empty Docling documents fail (#417). An empty DOCX (or an empty HTML body or list) used to become a single empty table block, and an empty text item, such as an empty <title>, became an empty text block. An empty DOCX was therefore ingested with one empty table. Empty bodies, groups and text items now yield no content, so such a document makes parse_document() and process_document_complete() raise ValueError: Parsing failed: No content was extracted. BatchParser still reports the parse as successful (0 blocks), and the RAG phase of process_documents_with_rag_batch() counts the file as failed. An empty list inside a non-empty document no longer adds an empty table.
  • max_workers below 1 is rejected (#409, [#417]).
  • process_folder_complete() resolves max_workers from its argument or from config.max_concurrent_files (MAX_CONCURRENT_FILES). If the value is below 1, it raises ValueError("max_workers must be greater than zero") before initialising LightRAG. Previously 0 hung and a negative value failed later (#409, @IlyaKhalafi).
  • BatchParser(max_workers=...) raises the same error. Previously it logged Batch processing failed and returned with every file in neither successful_files nor failed_files, and the batch CLI with --workers 0 exited 0. This also applies to process_documents_batch() and process_documents_with_rag_batch(), which build a BatchParser, and to the CLI's --workers option (#417).

Fixes

Query & VLM

  • VLM-enhanced queries label each retrieved image with its real media type (image/png, image/gif, image/webp, image/bmp or image/tiff), detected from the image bytes. Previously every image was sent as image/jpeg, and strict providers reject a PNG labelled as JPEG. The new helper raganything.image_signature.image_mime_type() accepts exactly the files that looks_like_image() (#387) accepts (#389, @er-s-an).
  • With a vision model configured, mode="bypass" queries now go straight to LightRAG, once, as only_need_context and only_need_prompt already did. Previously a bypass query made two model calls, the first without the caller's system prompt, and a streaming bypass raised TypeError (#402, @IlyaKhalafi).
  • aquery_with_multimodal() now reads query content with the same helpers that ingestion uses (#398, @IlyaKhalafi):
  • string image captions and footnotes, used when the image file is missing, are no longer joined one character at a time;
  • tables given as table_body (MinerU), table_data or text reach the table prompt, where previously only table_data was read, and a list of rows is rendered as a Markdown table instead of a Python list;
  • list captions of tables and equations are joined into text instead of shown as a Python list;
  • equations given as text or equation instead of latex are no longer empty.

Batch

  • A non-incremental BatchParser run now removes the incremental manifest entries of the files it is about to reparse, before any worker starts. Previously, a later incremental run could reuse output that had been overwritten. This happened after a non-incremental run with different options (for example lang), or after a reparse that failed partway. Entries for other files are kept, dry runs leave the manifest unchanged, and no manifest is created when none exists (#388, @er-s-an).
  • process_documents_with_rag_batch() now runs the batch parse in an executor thread through process_documents_batch_async(). Previously it blocked the event loop (#411, @IlyaKhalafi).
  • An incremental manifest that is not valid UTF-8 is now treated like malformed JSON: a warning and a full reparse. Previously the batch crashed with UnicodeDecodeError (#414, @IlyaKhalafi).
  • process_documents_with_rag_batch(..., dry_run=True) returns the parse plan without initialising LightRAG or ingesting anything. rag_results is empty and all RAG counters are zero. Previously a "dry run" initialised LightRAG and passed every selected file, together with dry_run, to process_document_complete(), which parsed and indexed it (#415, @IlyaKhalafi).

Parsing

  • A Markdown image whose title is in single quotes or parentheses, such as ![a](fig.png 'Title') or ![a](fig.png (Title)), now becomes an image block with the alt text and the title as its captions, as a double-quoted title already did. Previously the reference stayed literal text. Only real image files qualify, as in [#387] (#407, @IlyaKhalafi).
  • .txt and .md files that start with a UTF-16 byte-order mark (Notepad's "Unicode", Excel's "Unicode Text") are decoded as UTF-16. Previously they were read one byte per character, with NUL bytes in every chunk. The legacy fallback, which still tries GBK first, now tries Windows-1252 before Latin-1, so curly quotes, dashes and € decode correctly unless the file also happens to be valid GBK. UTF-8 (with or without BOM) and GBK files decode as before. EnhancedMarkdownConverter.convert_file_to_pdf() gets the same Windows-1252-before-Latin-1 order but no UTF-16 detection (#403, @L4XB).
  • raganything.omml_extractor (the standalone OMML-to-LaTeX utility; the built-in parsers do not call it): an equation array (m:eqArr, used for systems of equations and cases) becomes \begin{aligned} … \\ … \end{aligned}. Previously its rows were run together. An empty delimiter is now dropped. Previously it became a literal . (#404, @L4XB).
  • Docling: picture captions and footnotes are now read from Docling's captions and footnotes references, as lists like MinerU's. The old code read a caption key that Docling never sets. Pictures whose image data cannot be decoded are skipped, and empty text items, such as an empty <title>, are dropped (#417).
  • Docling: .txt and .md files are now read with the shared text parser, as MinerU and PaddleOCR already did. BatchParser already selected these files for Docling batches, but DoclingParser.parse_document() raised Unsupported file format (#417).

Multimodal & media

  • Models often return single-backslash LaTeX such as \frac or \beta inside their JSON. json.loads read \f and \b as form-feed and backspace characters, which then went into entity summaries and the vector store. A form feed or backspace followed by a letter is now restored to \f or \b. \t, \n and \r (as in \theta, \nabla, \rho) are left as they are, because they cannot be told apart from real whitespace (#396, @porkedchop).
  • Video: speech outside the scenes kept for visual analysis is now kept as audio-only timeline rows. This covers speech before the first retained scene, between retained scenes and after the last one, including speech in scenes dropped by the minimum scene duration (5 seconds by default). Both generate_description_only() and generate_chunk_sections() keep it, and neither makes extra vision calls. Previously generate_description_only() lost all of this speech, and generate_chunk_sections() kept only speech after the last retained scene, without an error. A video with speech but no retained scene now yields audio-only sections instead of the fallback section. The duration in the generate_description_only() summary now includes speech after the last retained scene (#395, @dafyy321-pixel).

Install

  • The audio, video and all extras now require av<19. A fresh install resolved faster-whisper 1.2.1 (its latest release) with PyAV 19, which removed an av.open() argument faster-whisper uses, so every transcription failed: the audio processor raised TypeError, and videos were ingested without a transcript, with only a warning. The runtime install hints say pip install faster-whisper "av<19" too (#420).

Docs

  • The vision_model_func examples in the README (EN/ZH), examples/ and reproduce/ now label a single image with its real media type via raganything.image_signature.image_mime_type(). They used to hard-code data:image/jpeg. If you copied one of these examples, update your copy (#417).
  • docs/batch_processing.md now documents:
  • how incremental and non-incremental runs interact (#388);
  • incremental RAG retries, skipped_rag_files, and when multimodal work is repeated (#393, [#417]);
  • dry_run=True (#415);
  • HTML support and how each entry point treats HTML (#405, [#417], [#418]);
  • that BatchParser max_workers must be at least 1 (#417).
  • The README (EN/ZH) says that HTML requires Docling and explains why. The register_parser docstring documents the optional parse_html and HTML_FORMATS hooks for custom parsers (#418).
  • The README (EN/ZH) now:
  • gives the Docling install command (pip install docling, not included in raganything[all]);
  • shows how a parser is actually selected (RAGAnythingConfig(parser=...), PARSER, --parser). The examples used to pass parser="mineru" per call, which never selected a parser and with MinerU raised TypeError;
  • documents streaming VLM queries and the **kwargs requirement for vision_model_func (#400);
  • no longer shows the LiteWrite banner (#420).
  • env.example and docs/batch_processing.md say MAX_CONCURRENT_FILES / max_workers must be at least 1 (#420).

Build & tests

  • Dependencies: the audio, video and all extras add av<19 (#420). Otherwise Requires-Dist, the extras and Requires-Python >=3.10 are the same as in 1.4.2, and lightrag-hku<1.5 stays.
  • The wheel adds one module, raganything/document_context.py (#391).
  • Each fix comes with regression tests. The suite grows from 684 to 898 tests, all passing on Python 3.10 (LightRAG 1.4.15) and Python 3.12 (LightRAG 1.4.16).
  • Version set to 1.4.3 (#419).

Validation

Beyond the unit suites (898 tests, run from source and against the installed wheel on Python 3.10 with LightRAG 1.4.15 and Python 3.12 with LightRAG 1.4.16, plus CI on Linux 3.10–3.12), this release was checked as follows.

Packaging

  • twine check --strict passes.
  • Compared with 1.4.2, the wheel adds only raganything/document_context.py. The metadata changes only the version and av<19 in the audio/video/all extras.
  • Fresh installs on Python 3.10 and 3.12 resolve lightrag-hku 1.4.16, and pip check is clean.
  • [video] now resolves PyAV 18 and a generated speech clip transcribes. Without the pin it resolved PyAV 19 and transcription failed.

Clean Linux end-to-end (python:3.12-slim, wheel only; live gpt-4o-mini for LLM and VLM, text-embedding-3-small via OpenRouter):

  • A 4-page content list with text, table, equation and image answers from all three modalities, and its text chunks keep their page_idx.
  • Query/VLM:
  • a retrieved PNG reaches the vision model labelled image/png;
  • a streaming image query returns an async iterator;
  • bypass makes one LLM call;
  • the aquery_with_multimodal prompt contains the table rows and the whole caption.
  • 4 concurrent inserts: each image's vision context holds only its own document's text. 1.4.2 leaked another document's text into 3 of 4.
  • Batch:
  • after an LLM outage, the next incremental run retries the failed documents to processed, and a rerun makes 0 model calls;
  • dry_run=True makes 0 model calls and does not initialise LightRAG;
  • max_workers=0 raises;
  • an invalid-UTF-8 manifest triggers a full reparse;
  • the event loop stays responsive during batch parsing.
  • \frac/\beta in model JSON are stored intact. 1.4.2 stored control characters and produced an unparseable graph file.

Real parsers on macOS:

  • Docling 2.140:
  • .html/.htm/.xhtml ingest end to end with no placeholder text;
  • an empty HTML fails cleanly;
  • .txt/.md ingest in Docling batches.
  • MinerU and PaddleOCR reject HTML before LibreOffice runs. A canary server referenced by the page got 0 requests; LibreOffice run directly on the same page hit it 4 times.
  • UTF-16 and Windows-1252 text files decode correctly, and UTF-8/GBK are unchanged.
  • Titled Markdown images become VLM-described image blocks, while a text file named .png stays text.
  • OMML equation arrays become aligned rows.
  • Video speech outside the retained scenes is kept.

Full history: https://github.com/HKUDS/RAG-Anything/compare/v1.4.2...v1.4.3

Source: README.md, updated 2026-10-21