Skip to content

Add inline text ingestion support - #2384

Merged
jioffe502 merged 22 commits into
NVIDIA:mainfrom
jioffe502:jioffe502/inline-text-ingestion
Aug 5, 2026
Merged

Add inline text ingestion support#2384
jioffe502 merged 22 commits into
NVIDIA:mainfrom
jioffe502:jioffe502/inline-text-ingestion

Conversation

@jioffe502

@jioffe502 jioffe502 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add .texts() for ingesting raw Python strings without temporary files
  • infer each source modality automatically, including inline://... text sources
  • allow inline text alongside files and buffers through the existing modality planner
  • keep chunking configuration on the established .extract(split_config=...) interface; no separate .extract_txt() or Graph .split() API
  • reuse the normal split, embed, VDB, and service paths in in-process, batch, and service modes

Closes #2345.

Validation

  • full local unit suite: 2,685 passed, 131 skipped, 12 deselected
  • focused ingestion, manifest, splitter, actor, and service suite: 307 passed, 2 skipped, 4 deselected
  • 2 local-Ray file/inline parity and mixed-source tests passed
  • service worker test passed with default auto-routing and preserved inline source identity
  • Black, Flake8, and diff whitespace checks passed on the changed files

@jioffe502
jioffe502 requested review from a team as code owners July 20, 2026 23:31
@jioffe502
jioffe502 requested a review from jdye64 July 20, 2026 23:31
@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a .texts() method to both GraphIngestor and ServiceIngestor enabling raw Python strings to be ingested without temporary files. Inline sources receive deterministic inline://NNNNNNNN identifiers and route through the existing text-split, embed, VDB, and service paths without any new graph stages.

  • New primitives in common/inline_text.py give the feature a clean, tested foundation; text_to_chunks_df is extracted from the file/bytes adapters and shared across all three paths.
  • Routing integration: _classified_input_paths, build_input_manifest, and infer_extraction_mode_from_filename all classify inline:// URIs as \"txt\" so the manifest planner and service router handle inline sources automatically alongside files.
  • Service mode: inline texts are packed as InMemoryUpload with a classification_filename header so the server-side auto-router can identify and process them without touching the filesystem; blank-corpus and empty-list early-exits prevent unnecessary HTTP round-trips.

Confidence Score: 4/5

Safe to merge with one caveat: extract_txt() was a documented public method and is removed outright without a deprecation wrapper; existing callers will get an AttributeError at runtime.

The inline text feature itself is well-implemented: correct routing through manifests, no temp-file leakage, blank-corpus short-circuits, proper logging on exceptions, and broad test coverage across inprocess/batch/service modes. The only unresolved concern is the hard removal of the public GraphIngestor.extract_txt() method without a deprecation cycle.

Files Needing Attention: nemo_retriever/src/nemo_retriever/ingestor/graph_ingestor.py — the removal of extract_txt() is the single change that needs a second look before merging.

Important Files Changed

Filename Overview
nemo_retriever/src/nemo_retriever/common/inline_text.py New module introducing inline text primitives: normalize/validate, deterministic source IDs, blank-corpus guard, and inline URI detection.
nemo_retriever/src/nemo_retriever/common/modality/txt/split.py Refactored to extract text_to_chunks_df from txt_file_to_chunks_df and txt_bytes_to_chunks_df, adding empty_text_chunks_df() and inline-source-path preservation in txt_bytes_to_chunks_df.
nemo_retriever/src/nemo_retriever/ingestor/graph_ingestor.py Adds .texts(), removes .extract_txt() (breaking API change flagged in previous review), changes _extraction_mode default from pdf to None, and adds inline-text routing for inprocess/batch/branch paths.
nemo_retriever/src/nemo_retriever/ingestor/branch_extraction.py Adds inline_rows field to ExtractionBranchExecutor; _partition_branch_inputs and _inline_rows_by_path helpers separate file, buffer, and inline inputs per branch for both inprocess and Ray batch paths.
nemo_retriever/src/nemo_retriever/service/service_ingestor.py Adds .texts() to ServiceIngestor; _collect_inputs packs inline texts as InMemoryUpload items without writing temp files; blank-corpus early exit and _has_mixed_inline_sources auto-routing for mixed payloads.
nemo_retriever/src/nemo_retriever/service/client.py Adds InMemoryUpload NamedTuple and UploadInput union type; extracts _upload_source helper; broadens ingest_documents and aingest_documents_stream to accept list[UploadInput] instead of list[Path].
nemo_retriever/src/nemo_retriever/operators/extract/txt/ray_data.py TxtSplitCPUActor now dispatches on text column (inline path) vs bytes column (file/buffer path); exception handler now logs with exc_info=True via logger.warning before continuing.
nemo_retriever/src/nemo_retriever/service/utils/file_type.py Adds early-return in infer_extraction_mode_from_filename for inline:// URIs so the server correctly routes in-memory text uploads to the text extractor.

Reviews (19): Last reviewed commit: "Remove redundant shutdown lease assertio..." | Re-trigger Greptile

Comment thread nemo_retriever/src/nemo_retriever/ingestor/graph_ingestor.py
Comment thread nemo_retriever/src/nemo_retriever/operators/extract/txt/ray_data.py
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
@jioffe502
jioffe502 force-pushed the jioffe502/inline-text-ingestion branch from 7888b78 to 57bc3e9 Compare July 22, 2026 00:30

def _execute_batch(self) -> Any:
_ray, cluster_resources = self.ensure_batch_runtime()
ray, cluster_resources = self.ensure_batch_runtime()

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.

why did you make this public? I think it should stay private no?

upload_failures: list[tuple[str, str]] = []

async def _upload_one_file(fpath: Path) -> None:
async def _upload_one_file(source: UploadInput) -> None:

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.

is there anyway to refactor this internal logic and that from the aingest_document_stream internal into a single helper, that maybe calls a another function that handles the differences?

if result_schema == "compact"
else ["text", "content", "path", "page_number", "metadata"]
)
return pd.DataFrame(columns=columns).astype({"page_number": "int64"})

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.

what is the difference between this and regular empty_text_chunks_df? Seems confusing to have both. Is this only because of the columns available, and the dtype conversion?

self._last_job_id = None
result = ServiceIngestResult()
if return_results:
result.dataframe = _empty_inline_text_dataframe(result_schema)

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.

Like here, why can't we use just empty dataframe.

Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
…xt-ingestion

# Conflicts:
#	nemo_retriever/src/nemo_retriever/common/modality/txt/split.py
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Comment thread nemo_retriever/src/nemo_retriever/ingestor/graph_ingestor.py Outdated
jioffe502 and others added 4 commits August 5, 2026 14:16
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
Signed-off-by: Jacob Ioffe <jioffe@nvidia.com>
@jioffe502
jioffe502 merged commit b243c2f into NVIDIA:main Aug 5, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEA]: Add support for arbitrary text chunks passed as list

2 participants