mindXtrain / mindxtrain /data /curate.py
Gregory-L's picture
fork mindXtrain from GitHub (Professor-Codephreak/mindXtrain@661bd41) as the mindX-specific line
dfb775d verified
Raw History Blame Contribute Delete
4.27 kB
"""Dataset curation — dispatch on `DataCfg.source` to the right adapter.
Three sources today:
- `hf` (default) — `datasets.load_dataset(streaming=True)`, requires `--extra ml`.
- `local` — JSONL files under `cfg.path`, pure stdlib.
- `mindx_dreams` — mindX dream-cycle JSONL corpus under `cfg.path`, pure stdlib.
See `mindxtrain.data.sources.mindx_dreams`.
The HF path stays lazy-import so consumers without `--extra ml` can still
read configs that target other sources.
"""
from __future__ import annotations
import json
from collections.abc import Iterator
from pathlib import Path
from typing import Any
from mindxtrain.config.schema import DataCfg
def load_streaming_dataset(cfg: DataCfg) -> Iterator[dict[str, Any]]:
"""Yield rows for the configured `DataCfg.source` one at a time."""
if cfg.source == "mindx_dreams":
from mindxtrain.data.sources.mindx_dreams import (
iter_mindx_dreams,
iter_mindx_evolutions,
)
assert cfg.path is not None # validated by DataCfg
# Two-stream yield with a SHARED budget so max_samples caps the
# combined corpus, not each stream individually. Consolidation rows
# come first (richer base signal), evolutions follow when opted in.
cap = cfg.max_samples
emitted = 0
remaining = (cap - emitted) if cap is not None else None
for row in iter_mindx_dreams(cfg.path, max_samples=remaining):
yield row
emitted += 1
if cap is not None and emitted >= cap:
return
if cfg.include_evolutions:
remaining = (cap - emitted) if cap is not None else None
try:
for row in iter_mindx_evolutions(cfg.path, max_samples=remaining):
yield row
emitted += 1
if cap is not None and emitted >= cap:
return
except FileNotFoundError:
# mindx_dreams already succeeded above, so the root exists —
# this would only fire on a path race. Swallow silently.
pass
return
if cfg.source == "local":
assert cfg.path is not None # validated by DataCfg
yield from _iter_local_jsonl(cfg.path, max_samples=cfg.max_samples)
return
if cfg.source == "lighthouse":
msg = (
"DataCfg.source='lighthouse' is reserved for shard-tar inputs; "
"use `mindxtrain.storage.lighthouse.fetch` to materialize them locally first, "
"then point a `local` source at the resulting directory."
)
raise NotImplementedError(msg)
# source == "hf"
try:
from datasets import load_dataset
except ImportError as exc:
msg = "datasets not installed; run `uv sync --extra ml`."
raise RuntimeError(msg) from exc
kwargs: dict[str, Any] = {"streaming": cfg.streaming}
revision = getattr(cfg, "revision", None)
if revision:
kwargs["revision"] = revision
ds = load_dataset(cfg.hf_id, split=cfg.split or "train", **kwargs)
emitted = 0
for row in ds:
yield row
emitted += 1
if cfg.max_samples is not None and emitted >= cfg.max_samples:
return
def _iter_local_jsonl(
path: Path,
*,
max_samples: int | None = None,
) -> Iterator[dict[str, Any]]:
"""Walk *.jsonl under `path` and yield parsed rows.
Skips lines that fail to parse — local datasets are often hand-assembled
and a single bad line shouldn't fail the run.
"""
path = Path(path).expanduser()
if path.is_file():
files = [path]
else:
files = sorted(path.rglob("*.jsonl"))
emitted = 0
for f in files:
with f.open("r", encoding="utf-8") as fh:
for line in fh:
line = line.strip()
if not line:
continue
try:
row = json.loads(line)
except json.JSONDecodeError:
continue
yield row
emitted += 1
if max_samples is not None and emitted >= max_samples:
return
__all__ = ["load_streaming_dataset"]