Skip to content

Add flexible data source discovery to load_raw_dataset (#661)#675

Merged
fynnsu merged 5 commits into
vllm-project:mainfrom
KKothuri:flexible-data-source
Jul 2, 2026
Merged

Add flexible data source discovery to load_raw_dataset (#661)#675
fynnsu merged 5 commits into
vllm-project:mainfrom
KKothuri:flexible-data-source

Conversation

@KKothuri

@KKothuri KKothuri commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Resolves #661.

Summary

Extends load_raw_dataset() in src/speculators/data_generation/preprocessing.py from two source types to the four-step resolution chain requested in RFC #661, and promotes Magpie/Nemotron to shared presets. Scope is intentionally limited to data source discovery — schema/role normalization is left to a separate effort.

Changes

  • load_raw_dataset now resolves a source in order:
    1. Local .json/.jsonl file (existing).
    2. Local directory — recursively glob *.json/*.jsonl (sorted for determinism) and load as one dataset; an empty directory raises an actionable error.
    3. Named preset from DATASET_CONFIGS (existing).
    4. hf:<id>[:<subset>:<split>] — load an arbitrary HuggingFace dataset.
  • New _load_hf_dataset helper parses the hf: spec (split defaults to train). It raises if the loaded dataset has no conversations column, since _preprocess_batch otherwise only warns and silently yields zero samples — satisfying the RFC's "no silent data corruption" requirement.
  • Adds magpie (already in ShareGPT conversations format, no normalize_fn) and nemotron (nvidia/Llama-Nemotron-Post-Training-Dataset, SFT/chat, with _normalize_nemotron building conversations from input + output) presets. Both schemas were verified against the live HF datasets-server before hardcoding.

All source types compose across mixed --data usage, e.g.:

--data sharegpt --data /my/shards/ --data hf:nvidia/HelpSteer2 --data extra.jsonl

Note for the maintainer

Beyond the RFC's literal hf:<id>:<subset>:<split>, I also accept the 2-part form hf:<id>:<split> (subset omitted), since single-config datasets with a non-train split (e.g. HuggingFaceH4/ultrachat_200k:train_sft) are common. Happy to make it strict if you'd prefer.

Why this is not a duplicate

#637 (RFC #583) touches the same function but bundles directory/HF discovery together with role/schema normalization. Per @shanjiaz's scoping on #583 and the assignment of #661, this PR delivers the discovery slice only, keeping it separate from normalization. The hf: handling here is also a different, more explicit design: an explicit hf: prefix (rather than treating any unmatched string as an HF path) plus a hard failure on non-conversations data.

Tests

New tests in tests/integration/datagen/test_preprocessing.py: local file, recursive directory bundle (incl. nested .json), empty-directory error, preset dispatch, unsupported source, hf: spec parsing (parametrized), the conversations-format guard, malformed hf: specs, and the new presets. The hf:/preset cases mock load_dataset so they are deterministic and network-free.

Commands run (in the project venv):

python -m pytest tests/integration/datagen/test_preprocessing.py -q
# 51 passed, 2 skipped

ruff check  src/speculators/data_generation/{preprocessing,configs}.py tests/integration/datagen/test_preprocessing.py   # All checks passed
ruff format --check <same files>                                                                                          # already formatted
python -m mypy src/speculators/data_generation/{preprocessing,configs}.py                                                 # Success: no issues

AI assistance disclosure

AI assistance (Claude Code) was used to help write this change. I (the submitter) have reviewed every changed line, understand the change end-to-end, and ran the tests above.

🤖 Generated with Claude Code

)

## Summary
Extends `load_raw_dataset()` from two source types to a four-step
resolution chain so a speculator can be trained on local file shards and
arbitrary HuggingFace datasets without a preset, composing freely across
mixed `--data` usage.

## Motivation
Previously `--data` accepted only a local `.json`/`.jsonl` file or a named
preset from `DATASET_CONFIGS`. Local data bundles had to be listed file by
file, and any HF dataset not already in the registry required adding a
preset. This implements the discovery piece of RFC vllm-project#661 (kept separate
from the schema-normalization work) and promotes Magpie/Nemotron to shared
presets.

## Changes
- `load_raw_dataset` resolves a source in order:
  1. Local `.json`/`.jsonl` file (existing).
  2. Local directory: recursively glob `*.json`/`*.jsonl` and load as one
     dataset; an empty directory raises an actionable error.
  3. Named preset from `DATASET_CONFIGS` (existing).
  4. `hf:<id>[:<subset>:<split>]` for an arbitrary HF dataset.
- New `_load_hf_dataset` helper parses the `hf:` spec (split defaults to
  `train`; `hf:<id>:<split>` selects a split without a subset). It raises
  if the loaded dataset has no `conversations` column, since
  `_preprocess_batch` otherwise only warns and silently yields zero
  samples — preventing silent data loss as required by the RFC.
- Add `magpie` (conversations already in ShareGPT format, no normalize_fn)
  and `nemotron` (`nvidia/Llama-Nemotron-Post-Training-Dataset` SFT/chat,
  with `_normalize_nemotron` building conversations from input + output)
  presets.
- Tests: local file, recursive directory bundle, empty-directory error,
  preset dispatch, unsupported source, `hf:` spec parsing, the
  conversations-format guard, malformed specs, and the new presets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hZBWmU32ZGrifozzK4DqQ
Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds magpie and nemotron entries to DATASET_CONFIGS with a _normalize_nemotron helper. Extends load_raw_dataset to support local directory inputs (recursive JSON/JSONL glob) and arbitrary HuggingFace datasets via an hf:<id>[:<subset>:<split>] spec, delegated to a new _load_hf_dataset helper. Integration tests cover all new resolution paths.

Changes

Flexible dataset source discovery

Layer / File(s) Summary
Nemotron normalizer and new dataset presets
src/speculators/data_generation/configs.py
Adds _normalize_nemotron to convert input/output fields into a conversations list, and registers magpie and nemotron entries in DATASET_CONFIGS.
HF spec helper and expanded load_raw_dataset resolution
src/speculators/data_generation/preprocessing.py
Adds _load_hf_dataset to parse and validate hf: specs (defaulting split to train, raising on malformed specs or missing conversations). Reworks load_raw_dataset to handle local file, local directory, named preset, and hf: spec inputs in that order.
Integration tests for all resolution paths
tests/integration/datagen/test_preprocessing.py
Adds tests for local file/directory loading, empty-directory error, named preset resolution with mocked load_dataset, hf: spec parsing, malformed-spec rejection, missing conversations column validation, DATASET_CONFIGS registration assertions, and _normalize_nemotron output.

Possibly related issues

  • #661 [RFC]: Flexible data source discovery — This PR directly implements the resolution chain proposed in the RFC: local file, local directory, named preset, and hf: spec paths, including the Magpie/Nemotron registry additions and integration test coverage described there.

Possibly related PRs

  • vllm-project/speculators#495: Introduced normalize_fn hooks and config-driven dataset handling in configs.py/preprocessing.py, which this PR extends with new presets and loading paths.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change to flexible data source discovery.
Linked Issues check ✅ Passed The changes implement the requested file, directory, preset, and hf: resolution chain, add the conversations guard, and cover the new paths with tests.
Out of Scope Changes check ✅ Passed The added preset and test work stays aligned with the discovery and preset objectives and does not introduce unrelated scope.
Description check ✅ Passed The description accurately matches the changes to dataset source resolution, new HF loading support, preset additions, and tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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 `@src/speculators/data_generation/preprocessing.py`:
- Around line 671-687: Reject empty hf: suffixes in the preprocessing parser
before load_dataset is called. In preprocessing.py, update the hf: parsing logic
around the spec.removeprefix("hf:").split(":") match in the helper that assigns
hf_id, subset, and split so that trailing-colon forms like hf:org/name: and
hf:org/name:subset: raise the local ValueError instead of falling through with
an empty split. Add validation for empty subset/split segments in this branch
and cover it with a regression test for the trailing-colon cases.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3da890cb-b47a-4f1f-9148-75e1484e4c32

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa4da9 and def1cfc.

📒 Files selected for processing (3)
  • src/speculators/data_generation/configs.py
  • src/speculators/data_generation/preprocessing.py
  • tests/integration/datagen/test_preprocessing.py

Comment thread src/speculators/data_generation/preprocessing.py
Trailing-colon forms (`hf:org/name:`, `hf:org/name:subset:`) and an empty
subset (`hf:org/name::split`) previously parsed with an empty segment and
fell through to load_dataset instead of raising locally. Validate empty
subset/split here and cover the cases with regression tests. Also add a
docstring to _normalize_nemotron and the test helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hZBWmU32ZGrifozzK4DqQ
Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>
The RFC asks for an integration test against a small public HF dataset.
The other hf: tests mock load_dataset; this one exercises the real Hub
download end to end using philschmid/guanaco-sharegpt-style (~9k rows,
conversations format), asserting the dataset loads with no normalize_fn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hZBWmU32ZGrifozzK4DqQ
Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>
@mergify

mergify Bot commented Jun 29, 2026

Copy link
Copy Markdown

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @KKothuri.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Jun 29, 2026
Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>

# Conflicts:
#	tests/integration/datagen/test_preprocessing.py
@mergify mergify Bot removed the needs-rebase label Jun 29, 2026
@imargulis

Copy link
Copy Markdown
Contributor

@KKothuri @shanjiaz
Following up on RFC#583 I mostly had the same approach implemented with a slight difference below:

The local-directory path loads every discovered file in a single pass:

load_dataset("json", data_files=[all .json/.jsonl], split="train")
which forces one Arrow schema across all files, so a bundle whose files/splits don't share an identical schema fails with DatasetGenerationError. This isn't limited to exotic data — even two files that share conversations but differ in one top-level column raise:

# a.jsonl: {"conversations": [...], "idx": 1}
# b.jsonl: {"conversations": [...], "lang": "en"}
load_dataset("json", data_files=["a.jsonl", "b.jsonl"], split="train")
#-> DatasetGenerationError: Couldn't cast ...

Loading each file/source separately and then concatenate_datasets on the common columns (dropping non-shared ones with a warning, and raising if no shared data column remains) handles heterogeneous bundles cleanly, and extends to parquet by grouping on schema. For homogeneous bundles it's identical to the current single-pass.

I have this implemented and tested (directory-bundle slice of RFC #583). Happy to (a) push it as a follow-up on top of this PR, or (b) hand you the diff to fold in — your and @shanjiaz's call. The hf: handling and presets here are great as-is.

@fynnsu fynnsu added the ready This PR is ready for review label Jul 2, 2026
@fynnsu fynnsu enabled auto-merge (squash) July 2, 2026 16:20
The CI runners use a pre-baked HF cache (HF_HOME=/model-cache) and are
network-restricted, so downloading philschmid/guanaco-sharegpt-style (not
in that cache) failed the buildkite build. Skip the test on any fetch
failure instead of failing; the hf: parsing and conversations guard remain
covered deterministically by the mocked tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hZBWmU32ZGrifozzK4DqQ
Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>
auto-merge was automatically disabled July 2, 2026 17:10

Head branch was pushed to by a user without write access

@fynnsu fynnsu enabled auto-merge (squash) July 2, 2026 18:04
@fynnsu fynnsu merged commit 77eda18 into vllm-project:main Jul 2, 2026
9 of 10 checks passed
@KKothuri KKothuri deleted the flexible-data-source branch July 2, 2026 18:45
Eros483 pushed a commit to Eros483/speculators that referenced this pull request Jul 4, 2026
) (vllm-project#675)

Resolves vllm-project#661.

## Summary

Extends `load_raw_dataset()` in
`src/speculators/data_generation/preprocessing.py` from two source types
to the four-step resolution chain requested in RFC vllm-project#661, and promotes
Magpie/Nemotron to shared presets. Scope is intentionally limited to
**data source discovery** — schema/role normalization is left to a
separate effort.

## Changes

- `load_raw_dataset` now resolves a source in order:
  1. Local `.json`/`.jsonl` file (existing).
2. **Local directory** — recursively glob `*.json`/`*.jsonl` (sorted for
determinism) and load as one dataset; an empty directory raises an
actionable error.
  3. Named preset from `DATASET_CONFIGS` (existing).
4. **`hf:<id>[:<subset>:<split>]`** — load an arbitrary HuggingFace
dataset.
- New `_load_hf_dataset` helper parses the `hf:` spec (split defaults to
`train`). It **raises if the loaded dataset has no `conversations`
column**, since `_preprocess_batch` otherwise only warns and silently
yields zero samples — satisfying the RFC's "no silent data corruption"
requirement.
- Adds `magpie` (already in ShareGPT `conversations` format, no
`normalize_fn`) and `nemotron`
(`nvidia/Llama-Nemotron-Post-Training-Dataset`, `SFT`/`chat`, with
`_normalize_nemotron` building conversations from `input` + `output`)
presets. Both schemas were verified against the live HF datasets-server
before hardcoding.

All source types compose across mixed `--data` usage, e.g.:
```
--data sharegpt --data /my/shards/ --data hf:nvidia/HelpSteer2 --data extra.jsonl
```

### Note for the maintainer

Beyond the RFC's literal `hf:<id>:<subset>:<split>`, I also accept the
2-part form `hf:<id>:<split>` (subset omitted), since single-config
datasets with a non-`train` split (e.g.
`HuggingFaceH4/ultrachat_200k:train_sft`) are common. Happy to make it
strict if you'd prefer.

## Why this is not a duplicate

vllm-project#637 (RFC vllm-project#583) touches the same function but bundles directory/HF
discovery **together with** role/schema normalization. Per @shanjiaz's
scoping on vllm-project#583 and the assignment of vllm-project#661, this PR delivers the
**discovery slice only**, keeping it separate from normalization. The
`hf:` handling here is also a different, more explicit design: an
explicit `hf:` prefix (rather than treating any unmatched string as an
HF path) plus a hard failure on non-conversations data.

## Tests

New tests in `tests/integration/datagen/test_preprocessing.py`: local
file, recursive directory bundle (incl. nested `.json`), empty-directory
error, preset dispatch, unsupported source, `hf:` spec parsing
(parametrized), the conversations-format guard, malformed `hf:` specs,
and the new presets. The `hf:`/preset cases mock `load_dataset` so they
are deterministic and network-free.

Commands run (in the project venv):

```
python -m pytest tests/integration/datagen/test_preprocessing.py -q
# 51 passed, 2 skipped

ruff check  src/speculators/data_generation/{preprocessing,configs}.py tests/integration/datagen/test_preprocessing.py   # All checks passed
ruff format --check <same files>                                                                                          # already formatted
python -m mypy src/speculators/data_generation/{preprocessing,configs}.py                                                 # Success: no issues
```

## AI assistance disclosure

AI assistance (Claude Code) was used to help write this change. I (the
submitter) have reviewed every changed line, understand the change
end-to-end, and ran the tests above.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Eros483 pushed a commit to Eros483/speculators that referenced this pull request Jul 4, 2026
) (vllm-project#675)

Resolves vllm-project#661.

## Summary

Extends `load_raw_dataset()` in
`src/speculators/data_generation/preprocessing.py` from two source types
to the four-step resolution chain requested in RFC vllm-project#661, and promotes
Magpie/Nemotron to shared presets. Scope is intentionally limited to
**data source discovery** — schema/role normalization is left to a
separate effort.

## Changes

- `load_raw_dataset` now resolves a source in order:
  1. Local `.json`/`.jsonl` file (existing).
2. **Local directory** — recursively glob `*.json`/`*.jsonl` (sorted for
determinism) and load as one dataset; an empty directory raises an
actionable error.
  3. Named preset from `DATASET_CONFIGS` (existing).
4. **`hf:<id>[:<subset>:<split>]`** — load an arbitrary HuggingFace
dataset.
- New `_load_hf_dataset` helper parses the `hf:` spec (split defaults to
`train`). It **raises if the loaded dataset has no `conversations`
column**, since `_preprocess_batch` otherwise only warns and silently
yields zero samples — satisfying the RFC's "no silent data corruption"
requirement.
- Adds `magpie` (already in ShareGPT `conversations` format, no
`normalize_fn`) and `nemotron`
(`nvidia/Llama-Nemotron-Post-Training-Dataset`, `SFT`/`chat`, with
`_normalize_nemotron` building conversations from `input` + `output`)
presets. Both schemas were verified against the live HF datasets-server
before hardcoding.

All source types compose across mixed `--data` usage, e.g.:
```
--data sharegpt --data /my/shards/ --data hf:nvidia/HelpSteer2 --data extra.jsonl
```

### Note for the maintainer

Beyond the RFC's literal `hf:<id>:<subset>:<split>`, I also accept the
2-part form `hf:<id>:<split>` (subset omitted), since single-config
datasets with a non-`train` split (e.g.
`HuggingFaceH4/ultrachat_200k:train_sft`) are common. Happy to make it
strict if you'd prefer.

## Why this is not a duplicate

vllm-project#637 (RFC vllm-project#583) touches the same function but bundles directory/HF
discovery **together with** role/schema normalization. Per @shanjiaz's
scoping on vllm-project#583 and the assignment of vllm-project#661, this PR delivers the
**discovery slice only**, keeping it separate from normalization. The
`hf:` handling here is also a different, more explicit design: an
explicit `hf:` prefix (rather than treating any unmatched string as an
HF path) plus a hard failure on non-conversations data.

## Tests

New tests in `tests/integration/datagen/test_preprocessing.py`: local
file, recursive directory bundle (incl. nested `.json`), empty-directory
error, preset dispatch, unsupported source, `hf:` spec parsing
(parametrized), the conversations-format guard, malformed `hf:` specs,
and the new presets. The `hf:`/preset cases mock `load_dataset` so they
are deterministic and network-free.

Commands run (in the project venv):

```
python -m pytest tests/integration/datagen/test_preprocessing.py -q
# 51 passed, 2 skipped

ruff check  src/speculators/data_generation/{preprocessing,configs}.py tests/integration/datagen/test_preprocessing.py   # All checks passed
ruff format --check <same files>                                                                                          # already formatted
python -m mypy src/speculators/data_generation/{preprocessing,configs}.py                                                 # Success: no issues
```

## AI assistance disclosure

AI assistance (Claude Code) was used to help write this change. I (the
submitter) have reviewed every changed line, understand the change
end-to-end, and ran the tests above.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Karthik Kothuri <karthikkothuri2009@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Eros483 <arnabmandal2912@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready This PR is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFC]: Flexible data source discovery

3 participants