Support documented Markdown, JSON, and shell text inputs#2321
Conversation
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Greptile SummaryThis PR routes
|
| Filename | Overview |
|---|---|
| nemo_retriever/src/nemo_retriever/common/input_files.py | Adds .md/.json/.sh to INPUT_TYPE_PATTERNS["txt"] and rewrites resolve_input_files to use case-insensitive suffix matching; functionally correct with one minor gap noted. |
| nemo_retriever/src/nemo_retriever/service/utils/file_type.py | Adds .md, .json, .sh to SUFFIX_MAP as FileCategory.TEXT / text/plain; consistent with how .txt is handled and correctly flows through infer_extraction_mode_from_filename. |
| nemo_retriever/tests/service/utils/test_file_type.py | New file; SPDX header present, tests all three new extensions including uppercase README.MD to verify case folding in classify(). |
| nemo_retriever/tests/test_ingest_interface.py | Three new tests covering markdown file, JSON file via extract_txt(), and shell script via buffers(); all follow existing test patterns. |
| nemo_retriever/tests/test_ingest_manifest.py | Parametrized test confirms manifest planner routes all three new extensions to the txt/text branch correctly. |
| nemo_retriever/tests/test_root_cli_workflow.py | Adds parametrized CLI routing test, case-insensitive discovery test (README.MD), glob expansion test, and updates the renamed directory-inclusion test. |
| nemo_retriever/tests/test_graph_pipeline_cli.py | Updates resolve_input_patterns assertion to reflect the expanded txt pattern tuple; straightforward and correct. |
| nemo_retriever/tests/test_service_pipeline_spec.py | Extends parametrized extraction-mode table test and replaces notes.txt with README.md in the typed-shortcuts test. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
Input["File Input\n(path / buffer / glob / directory)"]
Input --> TypeDetect["Extension lookup\ninput_type_for_path()\nINPUT_TYPE_EXTENSIONS"]
TypeDetect -->|".pdf / .docx / .pptx"| DocBranch["Document branch"]
TypeDetect -->|".txt / .md / .json / .sh"| TxtBranch["Text branch\nfamily=txt / extraction_mode=text"]
TypeDetect -->|".html"| HtmlBranch["HTML branch"]
TypeDetect -->|"image / audio / video"| MediaBranch["Media branch"]
TypeDetect -->|"unknown"| Reject["ValueError: Unsupported input type"]
TxtBranch --> ServiceClassify["Service: FileClassifier.classify()\nFileCategory.TEXT / text/plain"]
TxtBranch --> CLIResolve["CLI: resolve_input_files()\nrglob + suffix.lower() (case-insensitive)"]
TxtBranch --> GraphPatterns["GraphIngestor: resolve_input_patterns()\n*.txt / *.md / *.json / *.sh"]
ServiceClassify --> TextExtract["Plain-text extraction"]
CLIResolve --> TextExtract
GraphPatterns --> TextExtract
TextExtract --> LanceDB["LanceDB row: text + path + metadata"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
Input["File Input\n(path / buffer / glob / directory)"]
Input --> TypeDetect["Extension lookup\ninput_type_for_path()\nINPUT_TYPE_EXTENSIONS"]
TypeDetect -->|".pdf / .docx / .pptx"| DocBranch["Document branch"]
TypeDetect -->|".txt / .md / .json / .sh"| TxtBranch["Text branch\nfamily=txt / extraction_mode=text"]
TypeDetect -->|".html"| HtmlBranch["HTML branch"]
TypeDetect -->|"image / audio / video"| MediaBranch["Media branch"]
TypeDetect -->|"unknown"| Reject["ValueError: Unsupported input type"]
TxtBranch --> ServiceClassify["Service: FileClassifier.classify()\nFileCategory.TEXT / text/plain"]
TxtBranch --> CLIResolve["CLI: resolve_input_files()\nrglob + suffix.lower() (case-insensitive)"]
TxtBranch --> GraphPatterns["GraphIngestor: resolve_input_patterns()\n*.txt / *.md / *.json / *.sh"]
ServiceClassify --> TextExtract["Plain-text extraction"]
CLIResolve --> TextExtract
GraphPatterns --> TextExtract
TextExtract --> LanceDB["LanceDB row: text + path + metadata"]
Comments Outside Diff (1)
-
nemo_retriever/src/nemo_retriever/common/input_files.py, line 125-126 (link)Case-sensitivity gap between the two directory-discovery paths
resolve_input_files(used by the root CLI) now usesmatch.suffix.lower()and is correctly case-insensitive on Linux. Butresolve_input_patterns(used by GraphIngestor for directory inputs) still generates raw glob patterns like**/*.md, whichglob.globresolves case-sensitively on Linux. A file namedREADME.MDwill be picked up by the root CLI path but silently skipped when the same directory is explored viaGraphIngestor.files(directory). The gap existed for.txtbefore this PR, but the new testtest_root_ingest_directory_discovers_text_extensions_case_insensitivelyonly exercises the CLI path — there is no parallel test for theresolve_input_patternsbranch, so the asymmetry is now undocumented.Prompt To Fix With AI
This is a comment left during a code review. Path: nemo_retriever/src/nemo_retriever/common/input_files.py Line: 125-126 Comment: **Case-sensitivity gap between the two directory-discovery paths** `resolve_input_files` (used by the root CLI) now uses `match.suffix.lower()` and is correctly case-insensitive on Linux. But `resolve_input_patterns` (used by GraphIngestor for directory inputs) still generates raw glob patterns like `**/*.md`, which `glob.glob` resolves case-sensitively on Linux. A file named `README.MD` will be picked up by the root CLI path but silently skipped when the same directory is explored via `GraphIngestor.files(directory)`. The gap existed for `.txt` before this PR, but the new test `test_root_ingest_directory_discovers_text_extensions_case_insensitively` only exercises the CLI path — there is no parallel test for the `resolve_input_patterns` branch, so the asymmetry is now undocumented. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
nemo_retriever/src/nemo_retriever/common/input_files.py:125-126
**Case-sensitivity gap between the two directory-discovery paths**
`resolve_input_files` (used by the root CLI) now uses `match.suffix.lower()` and is correctly case-insensitive on Linux. But `resolve_input_patterns` (used by GraphIngestor for directory inputs) still generates raw glob patterns like `**/*.md`, which `glob.glob` resolves case-sensitively on Linux. A file named `README.MD` will be picked up by the root CLI path but silently skipped when the same directory is explored via `GraphIngestor.files(directory)`. The gap existed for `.txt` before this PR, but the new test `test_root_ingest_directory_discovers_text_extensions_case_insensitively` only exercises the CLI path — there is no parallel test for the `resolve_input_patterns` branch, so the asymmetry is now undocumented.
Reviews (1): Last reviewed commit: "Support documented plain-text input exte..." | Re-trigger Greptile
Summary
.md,.json, and.shthrough the existing plain-text extraction pathtext/plainservice behaviorWhy
The public documentation already lists Markdown, JSON, and shell files as supported text inputs, but the shared local and service extension registries only accepted
.txt. As a result,retriever ingestand GraphIngestor rejected documented formats before extraction.This change only restores the documented routing behavior. It does not add parsers, new chunking behavior, or retrieval changes.
Validation
241 passed, 2 deselectednvidia/llama-nemotron-embed-1b-v2: 3 files (.md,.json,.sh) -> 3 LanceDB rowsFollow-up