"""Ordered, read-only int32 token shards. Prediction windows never wrap.""" import errno import hashlib import os import json from bisect import bisect_right from pathlib import Path import numpy as np import torch class TokenStream: def __init__(self, entries, root, sequence_length, vocab_size): self.length = sequence_length + 1 self.vocab_size = vocab_size self.arrays, self.files, self.offsets, self.ends, self.identity = [], [], [], [], [] total = 0 for entry in entries: entry = {"path": entry} if isinstance(entry, str) else entry path = Path(entry.get("path", entry.get("bin", ""))) if not path.is_absolute(): path = root / path path = path.resolve() stat = path.stat() offset = entry.get("offset_tokens", 0) tokens = entry.get("tokens", stat.st_size // 4 - offset) if (stat.st_size % 4 or type(tokens) is not int or tokens <= 0 or type(offset) is not int or offset < 0 or offset + tokens > stat.st_size // 4): raise ValueError(f"Invalid headerless int32 token range: {path}") if offset == 0 and "offset_tokens" not in entry and tokens * 4 != stat.st_size: raise ValueError(f"Declared token count differs from file size: {path}") try: array = np.memmap(path, dtype="= self.vocab_size: raise ValueError("Token IDs are outside the configured vocabulary") return torch.from_numpy(values) def batch(self, start, size): return torch.stack([self.window(i) for i in range(start, start + size)]) def verify_hashes(self): for entry in self.identity: if entry["sha256"] is None: raise ValueError("--verify-data requires SHA256 for every shard") digest = hashlib.sha256() with Path(entry["path"]).open("rb") as stream: for chunk in iter(lambda: stream.read(8 << 20), b""): digest.update(chunk) if digest.hexdigest() != entry["sha256"]: raise ValueError(f"Data SHA256 mismatch: {entry['path']}") def load_data(path, config): path = Path(path).resolve() data = json.loads(path.read_text()) if data.get("context_length", config.block_size) != config.block_size: raise ValueError("Prepared data context length differs from the model") if data.get("ready_marker"): ready = path.parent / data["ready_marker"] if not ready.is_file() or ready.read_text().strip() != hashlib.sha256(path.read_bytes()).hexdigest(): raise ValueError("Dataset preparation is incomplete or dataset.json changed") manifest = path.parent / data["manifest"] if hashlib.sha256(manifest.read_bytes()).hexdigest() != data["manifest_sha256"]: raise ValueError("Preparation manifest SHA256 mismatch") if data.get("train_order", "sequential") != "sequential": raise ValueError("Prepare a single fixed, ordered token stream before training") if data.get("vocab_size", config.vocab_size) != config.vocab_size: raise ValueError("Dataset vocabulary differs from the model") train = TokenStream(data["train"], path.parent, config.block_size, config.vocab_size) validation = (TokenStream(data["validation"], path.parent, config.block_size, config.vocab_size) if data.get("validation") else None) if validation: for a in train.identity: for b in validation.identity: if a['path'] == b['path'] and max(a['offset_tokens'], b['offset_tokens']) < min( a['offset_tokens'] + a['tokens'], b['offset_tokens'] + b['tokens']): raise ValueError("Training and validation token ranges overlap") return train, validation