241 issue expand document loader coverage#262
Conversation
…te() Changes the default chunker that ``GraphRAG.ingest()`` and ``GraphRAG.update()`` fall back to when the caller doesn't pass an explicit ``chunker=``. Was ``FixedSizeChunking()``; now ``SentenceTokenCapChunking()`` (sentence-aware, max_tokens=512, overlap_sentences=2 — the strategy's own defaults). Why --- ``FixedSizeChunking`` splits on a hard character window with no awareness of sentence, word, or paragraph boundaries. When the window cuts through an entity name, the per-chunk LLM extractor produces a stub entity for the fragment (``"Wayne Enterprises"`` → ``"Wayne En"`` in chunk N plus unparsable text in chunk N+1). These stubs never merge with their full forms during resolution because their embeddings differ enough that LLMVerifiedResolution scores them below the soft threshold. This silently inflates cypher counts and pollutes "which X" lists. The strategy that surfaced this — ``CypherFirstAggregationStrategy`` — was hitting a 6/7 ceiling on the internal aggregation benchmark with one question failing because of these stubs. Switching to ``SentenceTokenCapChunking`` cleared the benchmark to 7/7 stable across three runs, and the post-ingest graph state went from 11-14 organization nodes (including ``Glo`` / ``Initech System`` / ``Wayne En``) to exactly 10 clean orgs, and from 66-80 ``Person`` nodes (with ``Carla`` / ``Carla Okafor`` duplicates) to exactly 56 distinct persons — matching the corpus. A side benefit: sentence-aware chunks with 2-sentence overlap almost always keep a person's first mention in the same chunk as their later short-form references, so per-chunk FastCoref now binds ``Carla → Carla Okafor`` reliably. That eliminates the short-form-duplicate class too, not just the truncation stubs. Compatibility ------------- ``FixedSizeChunking`` remains exported and fully supported — callers who explicitly pass ``chunker=FixedSizeChunking()`` get unchanged behavior. Existing tests (748 passed, 24 skipped) pass without modification: no test in the suite asserts on chunk count or content shape from the default chunker, so switching defaults doesn't break the suite. Callers who relied on the previous default and want to keep it should pass ``chunker=FixedSizeChunking()`` explicitly. The docstrings call out the new default and reference ``FixedSizeChunking`` as the opt-in character-window alternative. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR extends GraphRAG SDK with docling-based loaders for multiple document formats (DOCX, XLSX, PPTX, HTML, CSV), updates packaging dependencies, shifts the default ingestion chunker from ChangesDocling-based document loaders and API updates
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 `@graphrag_sdk/pyproject.toml`:
- Around line 59-67: The extras specification is inconsistent: the standalone
"docling" extra pins docling>=2.91.0 while the "all" extras list contains
docling>=2.0.0; update the "all" extras entry to require the same minimum
(docling>=2.91.0) so installing graphrag-sdk[all] cannot pull an older
incompatible docling version—edit the "docling" entry inside the all array in
pyproject.toml to match the docling extra's minimum version.
In `@graphrag_sdk/src/graphrag_sdk/api/main.py`:
- Around line 329-333: Update the stale loader-default docstring that currently
reads "Loader: auto-detected from file extension (PDF or text)" so it reflects
the new extension routing; locate the docstring containing that exact phrase in
graphrag_sdk/api/main.py (the help/usage text shown in the diff) and replace it
with a concise description like "Loader: auto-detected from file extension (PDF,
DOCX, XLSX, PPTX, HTML/XHTML, CSV, or plain text)" so the user-facing docs list
the supported formats.
In `@graphrag_sdk/tests/test_docling_loaders.py`:
- Around line 24-27: The test is breaking the import system by having the
patched builtins.__import__ return None for non-target imports; change the patch
side_effect to delegate to the real importer for all names except the one you
want to simulate failing (i.e., capture the original_import =
builtins.__import__ and in the side_effect for the patch used around loader.load
call, raise ImportError("module not found") when name ==
"docling.document_converter" and otherwise return original_import(name, *args,
**kwargs)); keep the pytest.raises assertion around await loader.load(str(file),
ctx) unchanged and reference the patched builtins.__import__ side_effect that
delegates for everything except "docling.document_converter".
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6566ed76-11b6-4ce9-8ef8-6fb548181321
📒 Files selected for processing (10)
graphrag_sdk/pyproject.tomlgraphrag_sdk/src/graphrag_sdk/api/main.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/__init__.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/csv_loader.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/docling_base.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/docx_loader.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/html_loader.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/pptx_loader.pygraphrag_sdk/src/graphrag_sdk/ingestion/loaders/xlsx_loader.pygraphrag_sdk/tests/test_docling_loaders.py
| docling = ["docling>=2.91.0"] | ||
| all = [ | ||
| "openai>=1.0,<2.0", | ||
| "anthropic>=0.20,<1.0", | ||
| "cohere>=5.0", | ||
| "sentence-transformers>=2.0", | ||
| "pypdf>=6.9.2", | ||
| "litellm>=1.83.0,<2.0", | ||
| "docling>=2.0.0", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="graphrag_sdk/pyproject.toml"
echo "Docling constraints found in optional dependencies:"
rg -n 'docling>=([0-9]+\.){1,2}[0-9]+' "$FILE"
echo
echo "Expected verification result:"
echo "- The dedicated 'docling' extra and the 'all' extra should use the same minimum version."Repository: FalkorDB/GraphRAG-SDK
Length of output: 295
Align docling minimum version across extras.
Line 59 requires docling>=2.91.0, but Line 67 allows docling>=2.0.0 in all. Installing graphrag-sdk[all] can pull an older docling than the dedicated docling extra, which breaks compatibility for the new loaders.
Proposed fix
all = [
@@
- "docling>=2.0.0",
+ "docling>=2.91.0",
]📝 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.
| docling = ["docling>=2.91.0"] | |
| all = [ | |
| "openai>=1.0,<2.0", | |
| "anthropic>=0.20,<1.0", | |
| "cohere>=5.0", | |
| "sentence-transformers>=2.0", | |
| "pypdf>=6.9.2", | |
| "litellm>=1.83.0,<2.0", | |
| "docling>=2.0.0", | |
| docling = ["docling>=2.91.0"] | |
| all = [ | |
| "openai>=1.0,<2.0", | |
| "anthropic>=0.20,<1.0", | |
| "cohere>=5.0", | |
| "sentence-transformers>=2.0", | |
| "pypdf>=6.9.2", | |
| "litellm>=1.83.0,<2.0", | |
| "docling>=2.91.0", | |
| ] |
🤖 Prompt for 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.
In `@graphrag_sdk/pyproject.toml` around lines 59 - 67, The extras specification
is inconsistent: the standalone "docling" extra pins docling>=2.91.0 while the
"all" extras list contains docling>=2.0.0; update the "all" extras entry to
require the same minimum (docling>=2.91.0) so installing graphrag-sdk[all]
cannot pull an older incompatible docling version—edit the "docling" entry
inside the all array in pyproject.toml to match the docling extra's minimum
version.
| - Loader: auto-detected from file extension (PDF or text) | ||
| - Chunker: FixedSizeChunking(chunk_size=1000) | ||
| - Chunker: SentenceTokenCapChunking(max_tokens=512, overlap_sentences=2) | ||
| — sentence-aware, never splits entity names at chunk boundaries. | ||
| Override with ``chunker=FixedSizeChunking(...)`` if you need | ||
| character-window chunking. |
There was a problem hiding this comment.
Update the loader-default docstring to match the new extension routing.
Line 329 still says “PDF or text”, but the default loader now supports DOCX/XLSX/PPTX/HTML/XHTML/CSV as well. This user-facing doc is now stale.
✏️ Proposed docstring fix
- - Loader: auto-detected from file extension (PDF or text)
+ - Loader: auto-detected from file extension
+ (PDF, Markdown, DOCX, XLSX, PPTX, HTML/XHTML, CSV, or text fallback)🤖 Prompt for 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.
In `@graphrag_sdk/src/graphrag_sdk/api/main.py` around lines 329 - 333, Update the
stale loader-default docstring that currently reads "Loader: auto-detected from
file extension (PDF or text)" so it reflects the new extension routing; locate
the docstring containing that exact phrase in graphrag_sdk/api/main.py (the
help/usage text shown in the diff) and replace it with a concise description
like "Loader: auto-detected from file extension (PDF, DOCX, XLSX, PPTX,
HTML/XHTML, CSV, or plain text)" so the user-facing docs list the supported
formats.
| with patch("builtins.__import__", side_effect=lambda name, *args, **kwargs: | ||
| (exec('raise ImportError("module not found")') if name == "docling.document_converter" else None)): | ||
| with pytest.raises(LoaderError, match=r"DOCX parsing requires 'docling'"): | ||
| await loader.load(str(file), ctx) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C3 'patch\("builtins\.__import__"|side_effect=lambda name|else None' graphrag_sdk/tests/test_docling_loaders.pyRepository: FalkorDB/GraphRAG-SDK
Length of output: 515
🏁 Script executed:
# Check for other similar import mocking patterns in the test file
rg -n 'patch.*__import__|side_effect.*lambda' graphrag_sdk/tests/test_docling_loaders.pyRepository: FalkorDB/GraphRAG-SDK
Length of output: 152
🏁 Script executed:
# Find the loader implementation to understand what imports happen during load()
fd -name '*loader*' -type f graphrag_sdk/ | head -20Repository: FalkorDB/GraphRAG-SDK
Length of output: 296
🏁 Script executed:
# Check what docling_base_loader.load() actually does
rg -n 'def load|async def load' graphrag_sdk/ -A 15 | head -100Repository: FalkorDB/GraphRAG-SDK
Length of output: 9229
Mocking builtins.__import__ with a lambda that returns None for non-target imports breaks the import system.
The current pattern at lines 24-25 causes any unrelated import during loader.load(...) to fail because builtins.__import__ must return a module object or delegate to the real importer—not None. This can cause test flakiness.
Recommended fix
- with patch("builtins.__import__", side_effect=lambda name, *args, **kwargs:
- (exec('raise ImportError("module not found")') if name == "docling.document_converter" else None)):
+ real_import = __import__
+
+ def _import(name, *args, **kwargs):
+ if name == "docling.document_converter":
+ raise ImportError("module not found")
+ return real_import(name, *args, **kwargs)
+
+ with patch("builtins.__import__", side_effect=_import):
with pytest.raises(LoaderError, match=r"DOCX parsing requires 'docling'"):
await loader.load(str(file), ctx)📝 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.
| with patch("builtins.__import__", side_effect=lambda name, *args, **kwargs: | |
| (exec('raise ImportError("module not found")') if name == "docling.document_converter" else None)): | |
| with pytest.raises(LoaderError, match=r"DOCX parsing requires 'docling'"): | |
| await loader.load(str(file), ctx) | |
| real_import = __import__ | |
| def _import(name, *args, **kwargs): | |
| if name == "docling.document_converter": | |
| raise ImportError("module not found") | |
| return real_import(name, *args, **kwargs) | |
| with patch("builtins.__import__", side_effect=_import): | |
| with pytest.raises(LoaderError, match=r"DOCX parsing requires 'docling'"): | |
| await loader.load(str(file), ctx) |
🤖 Prompt for 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.
In `@graphrag_sdk/tests/test_docling_loaders.py` around lines 24 - 27, The test is
breaking the import system by having the patched builtins.__import__ return None
for non-target imports; change the patch side_effect to delegate to the real
importer for all names except the one you want to simulate failing (i.e.,
capture the original_import = builtins.__import__ and in the side_effect for the
patch used around loader.load call, raise ImportError("module not found") when
name == "docling.document_converter" and otherwise return original_import(name,
*args, **kwargs)); keep the pytest.raises assertion around await
loader.load(str(file), ctx) unchanged and reference the patched
builtins.__import__ side_effect that delegates for everything except
"docling.document_converter".
Path C in retrieve_chunks used `COLLECT(c)[..3]` with no ORDER BY, so hub entities (which can be MENTIONED_IN hundreds of chunks) returned an arbitrary 3 — almost never including the chunks most relevant to the current query. Add an ORDER BY on `vec.cosineDistance(c.embedding, query_vector)` before the COLLECT so per-entity chunk selection is query-aware. Refs FalkorDB#258 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
This feature extends the loader interface using Docling to add support for DOCX, XLSX, PPTX, CSV, HTML, and XHTML.
Changes
Test Plan
pytest tests/ -q)ruff check src/)Notes
Currently, the
GraphRAG_SDKloader interface maps one loader to one extension. Since Docling supports multiple extensions, this PR updates the current loader implementation paradigm to support multi-format loaders.Summary by CodeRabbit
Release Notes
New Features
Chores