Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
34d2f34
feat(interleaved): foundation IO improvements — schema utilities, bas…
VibhuJawa Mar 24, 2026
7702bfd
simplify(interleaved): move resolve_schema to shared utils, remove de…
VibhuJawa Mar 24, 2026
a1eeccc
Merge remote-tracking branch 'origin/main' into feat/interleaved-io-f…
VibhuJawa Mar 24, 2026
33088b1
chore: remove unrelated nixl-cu12 from override-dependencies
VibhuJawa Mar 24, 2026
6f0e655
chore: restore nixl-cu12 override-dependency
VibhuJawa Mar 24, 2026
57263df
Remove source_id_field from WebDataset reader
VibhuJawa Mar 24, 2026
27cc69a
simplify: fix align_table, type hints, and add dict-type comment
VibhuJawa Mar 24, 2026
987f5d4
fix(tutorial): update quickstart notebook and set source_files in rea…
VibhuJawa Mar 24, 2026
575e505
Apply suggestion from @claude[bot]
VibhuJawa Mar 24, 2026
7df12f3
ci: update secrets baseline for notebook image outputs (false positives)
VibhuJawa Mar 24, 2026
1a4dce4
fix: make source_files unique per split in webdataset reader
VibhuJawa Mar 24, 2026
472b21c
fix: source_files per split lists only contributing tars in webdatase…
VibhuJawa Mar 24, 2026
be9e7c5
fix: source_files per split lists only contributing tars in webdatase…
VibhuJawa Mar 24, 2026
86de41b
test: add schema_utils tests and expand interleaved coverage
VibhuJawa Mar 31, 2026
9c35387
Merge remote-tracking branch 'origin/main' into feat/interleaved-io-f…
VibhuJawa Apr 1, 2026
802ab23
Merge branch 'main' into feat/interleaved-io-foundation
VibhuJawa Apr 1, 2026
5a82f0e
feat: add interleaved_cpu to all extras in pyproject.toml
VibhuJawa Apr 1, 2026
54d1378
Merge branch 'main' into feat/interleaved-io-foundation
VibhuJawa Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 20 additions & 24 deletions .github/workflows/config/.secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ data/
# macOS Files
.DS_Store
AGENTS.md
alm_output/
benchmark_results/

# Fern: generated API reference from `libraries` in docs.yml (`fern docs md generate`).
# Regenerated in CI (fern-docs-ci.yml, publish-fern-docs.yml); do not commit.
Expand Down
1 change: 0 additions & 1 deletion benchmarking/scripts/multimodal_mint1t_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def create_pipeline(args: argparse.Namespace) -> Pipeline:
)
pipeline.add_stage(
WebdatasetReader(
source_id_field="pdf_name",

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.

I think this change will affect pdf PR too. Why was this removed? Is the reading happening via schema now ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I just removed it , because it was excessive to have around. It now is just a passthrough column and not given special treatment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think if you remove it and its in the dataset, it will just work. Happy to help with your PR too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We will revisit this discussion in 1657

file_paths=args.input_path,
files_per_partition=args.files_per_partition,
blocksize=args.input_blocksize,
Expand Down
6 changes: 2 additions & 4 deletions nemo_curator/stages/interleaved/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ WebDataset tar shards
|
v
┌─────────────────────────┐
│ WebdatasetReader │ CompositeStage: FilePartitioning + WebdatasetReaderStage
│ WebdatasetReader │ CompositeStage: FilePartitioning + InterleavedWebdatasetReaderStage
│ (io/reader.py) │ Parses tar members -> normalized interleaved rows
└────────┬────────────────┘
| InterleavedBatch (Arrow/Pandas)
Expand Down Expand Up @@ -52,7 +52,6 @@ Extra fields from the source data flow through the pipeline as additional column

```python
reader = WebdatasetReader(
source_id_field="pdf_name",
file_paths="/data/shards/",
fields=("p_hash", "score", "aux"), # These become extra columns
)
Expand Down Expand Up @@ -117,7 +116,6 @@ from nemo_curator.stages.interleaved.stages import InterleavedAspectRatioFilterS

pipeline = Pipeline(name="mint1t_pipeline")
pipeline.add_stage(WebdatasetReader(
source_id_field="pdf_name",
file_paths="/data/mint1t/shards/",
))
pipeline.add_stage(InterleavedAspectRatioFilterStage(drop_invalid_rows=True))
Expand All @@ -141,7 +139,7 @@ stages/interleaved/
│ ├── reader.py # WebdatasetReader (CompositeStage)
│ ├── readers/
│ │ ├── base.py # BaseInterleavedReader
│ │ └── webdataset.py # WebdatasetReaderStage (ProcessingStage)
│ │ └── webdataset.py # InterleavedWebdatasetReaderStage (ProcessingStage)
│ └── writers/
│ ├── base.py # BaseInterleavedWriter (filesystem + materialization + process)
│ └── tabular.py # InterleavedParquetWriterStage
Expand Down
5 changes: 4 additions & 1 deletion nemo_curator/stages/interleaved/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@
from nemo_curator.stages.interleaved.io.reader import WebdatasetReader
from nemo_curator.stages.interleaved.io.writers.tabular import InterleavedParquetWriterStage

__all__ = ["InterleavedParquetWriterStage", "WebdatasetReader"]
__all__ = [
"InterleavedParquetWriterStage",
"WebdatasetReader",
]
10 changes: 4 additions & 6 deletions nemo_curator/stages/interleaved/io/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

from nemo_curator.stages.base import CompositeStage
from nemo_curator.stages.file_partitioning import FilePartitioningStage
from nemo_curator.stages.interleaved.io.readers.webdataset import WebdatasetReaderStage
from nemo_curator.stages.interleaved.io.readers.webdataset import InterleavedWebdatasetReaderStage
from nemo_curator.stages.interleaved.utils import (
DEFAULT_IMAGE_EXTENSIONS,
DEFAULT_JSON_EXTENSIONS,
DEFAULT_WEBDATASET_EXTENSIONS,
require_source_id_field,
resolve_storage_options,
)
from nemo_curator.tasks import InterleavedBatch, _EmptyTask
Expand All @@ -41,7 +42,6 @@ class WebdatasetReader(CompositeStage[_EmptyTask, InterleavedBatch]):
file_extensions: list[str] = field(default_factory=lambda: list(DEFAULT_WEBDATASET_EXTENSIONS))
json_extensions: list[str] = field(default_factory=lambda: list(DEFAULT_JSON_EXTENSIONS))
image_extensions: list[str] = field(default_factory=lambda: list(DEFAULT_IMAGE_EXTENSIONS))
source_id_field: str = ""
sample_id_field: str | None = None
texts_field: str = "texts"
images_field: str = "images"
Expand All @@ -53,7 +53,6 @@ class WebdatasetReader(CompositeStage[_EmptyTask, InterleavedBatch]):

def __post_init__(self):
super().__init__()
self.source_id_field = require_source_id_field(self.source_id_field)
self.storage_options = resolve_storage_options(io_kwargs=self.read_kwargs)

def decompose(self) -> list:
Expand All @@ -65,13 +64,12 @@ def decompose(self) -> list:
file_extensions=self.file_extensions,
storage_options=self.storage_options,
),
WebdatasetReaderStage(
InterleavedWebdatasetReaderStage(
read_kwargs=self.read_kwargs,
materialize_on_read=self.materialize_on_read,
max_batch_bytes=self.max_batch_bytes,
json_extensions=tuple(self.json_extensions),
image_extensions=tuple(self.image_extensions),
source_id_field=self.source_id_field,
sample_id_field=self.sample_id_field,
texts_field=self.texts_field,
images_field=self.images_field,
Expand Down
4 changes: 2 additions & 2 deletions nemo_curator/stages/interleaved/io/readers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from nemo_curator.stages.interleaved.io.readers.webdataset import WebdatasetReaderStage
from nemo_curator.stages.interleaved.io.readers.webdataset import InterleavedWebdatasetReaderStage

__all__ = ["WebdatasetReaderStage"]
__all__ = ["InterleavedWebdatasetReaderStage"]
34 changes: 33 additions & 1 deletion nemo_curator/stages/interleaved/io/readers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,51 @@
from dataclasses import dataclass, field
from typing import Any

import pyarrow as pa

from nemo_curator.stages.base import ProcessingStage
from nemo_curator.stages.interleaved.utils.schema import align_table, reconcile_schema, resolve_schema
from nemo_curator.tasks import FileGroupTask, InterleavedBatch


@dataclass
class BaseInterleavedReader(ProcessingStage[FileGroupTask, InterleavedBatch]):
"""Base contract for interleaved readers."""
"""Base contract for interleaved readers.

By default (``schema=None``) user-added passthrough columns are preserved
and only reserved-column types are reconciled via ``reconcile_schema``.

If *schema* is set explicitly, every output table is strictly aligned to it
(missing columns become typed nulls, extra columns are dropped).

Use *schema_overrides* to add or override individual field types relative to
``INTERLEAVED_SCHEMA`` while keeping strict alignment:

.. code-block:: python

reader = InterleavedParquetReader(
"data.parquet",
schema_overrides={"url": pa.string(), "timestamp": pa.int64()},
)
"""

read_kwargs: dict[str, Any] = field(default_factory=dict)
schema: pa.Schema | None = None
schema_overrides: dict[str, pa.DataType] | None = None
name: str = "base_interleaved_reader"

def __post_init__(self) -> None:
if self.schema is not None or self.schema_overrides is not None:
self.schema = resolve_schema(self.schema, self.schema_overrides)

def inputs(self) -> tuple[list[str], list[str]]:
return ["data"], []

def outputs(self) -> tuple[list[str], list[str]]:
return ["data"], ["sample_id", "position", "modality"]

def _align_output(self, table: pa.Table) -> pa.Table:
"""Reconcile or align *table* to the declared schema."""
if self.schema is not None:
return align_table(table, self.schema)
return table.cast(reconcile_schema(table.schema))
Loading
Loading