Biraboro's picture
Upload folder using huggingface_hub
9d614f1 verified
Raw History Blame Contribute Delete
8.75 kB
"""PUBLIC INTERFACE of Data_Module. Other modules may import ONLY this file.
run_clean(cfg) -> dict
1. surgically cleans chat.jsonl -> cleaned_data/sft_clean.jsonl (+ post-write verification)
2. streams every SOURCE_REPOS entry (dataset first, model fallback), blacklist -> garbage filter
-> context-aware dedup (seeded with chat.jsonl for cross-deduplication)
3. mixes PRETRAIN_CONV_RATIO of chat samples into cleaned_data/pretrain_clean.jsonl
"""
from __future__ import annotations
import gc
import json
from pathlib import Path
from typing import Dict, List, Optional
from .internal.blacklist_gatekeeper import BlacklistGatekeeper
from .internal.context_deduplicator import ContextDeduplicator
from .internal.hf_universal_streamer import HFUniversalStreamer, RepoTelemetry
from .internal.pretrain_garbage_filter import PretrainGarbageFilter
from .internal.sft_surgical_cleaner import (
build_pretrain_chat_samples, clean_chat_file, verify_pretrain_jsonl, verify_sft_jsonl, write_starter_chat,
)
def cleaned_paths(cfg: Dict) -> Dict[str, Path]:
out = Path(cfg["root"]) / "cleaned_data"
return {"dir": out, "sft": out / "sft_clean.jsonl", "pretrain": out / "pretrain_clean.jsonl",
"rejected": out / "sft_rejected.jsonl"}
def ensure_chat_file(cfg: Dict) -> Path:
"""Create the 40-line starter chat.jsonl (37 clean + 3 intentionally broken lines) if it does not exist."""
chat_file = Path(cfg["chat_file"])
if write_starter_chat(chat_file):
print(f"[Data] {chat_file.name} not found -> generated the 40-line starter dataset at {chat_file}")
print(" (3 lines are broken/duplicate on purpose: the cleaner below will excise them and re-verify)")
return chat_file
def cleaned_data_ready(cfg: Dict) -> bool:
p = cleaned_paths(cfg)
return p["sft"].exists() and p["pretrain"].exists() and p["sft"].stat().st_size > 0
def _parse_limit(value) -> Optional[int]:
if value is None:
return None
if str(value).strip().lower() in ("full", "all", "none", ""):
return None
try:
return max(1, int(value))
except ValueError:
return None
def _print_table(rows: List[RepoTelemetry]) -> None:
header = ["Repo", "Type", "[Total Discovered]", "[Skipped by Blacklist]",
"[Destroyed Garbage/Dup]", "[Kept Clean Samples]"]
body = [[r.repo, r.repo_type, str(r.total_discovered), str(r.skipped_blacklist),
str(r.destroyed), str(r.kept)] for r in rows]
widths = [max(len(header[i]), *(len(row[i]) for row in body)) if body else len(header[i])
for i in range(len(header))]
line = "+" + "+".join("-" * (w + 2) for w in widths) + "+"
print("\n TELEMETRY [Total Discovered] -> [Skipped by Blacklist] -> [Destroyed Garbage/Dup] -> [Kept]")
print(" " + line)
print(" | " + " | ".join(h.ljust(w) for h, w in zip(header, widths)) + " |")
print(" " + line)
for row in body:
print(" | " + " | ".join(c.ljust(w) for c, w in zip(row, widths)) + " |")
print(" " + line)
for r in rows:
print(f" {r.repo}: garbage-gate={r.garbage_gate} too-large={r.too_large} read-err={r.read_errors} "
f"filter-destroyed={r.filter_destroyed} dup-files={r.duplicate_files} "
f"partial-dedup(sub-sections excised)={r.partial_dedup}"
+ (f" ERROR: {r.error}" if r.error else ""))
def run_clean(cfg: Dict) -> Dict:
paths = cleaned_paths(cfg)
paths["dir"].mkdir(parents=True, exist_ok=True)
token = cfg.get("hf_token") or None
chat_file = Path(cfg["chat_file"])
print("\n[Data] === STEP 1/3: SFT surgical cleaning of chat.jsonl ===")
records, sft_report = clean_chat_file(chat_file, paths["sft"], paths["rejected"], verbose=True)
print(f" chat.jsonl: {sft_report['raw_lines']} lines -> {sft_report['kept']} clean "
f"({sft_report['removed']} removed, {sft_report['repaired_modes']} mode repairs)")
if not records:
raise RuntimeError("No usable SFT records after cleaning chat.jsonl")
print("\n[Data] === STEP 2/3: streaming + filtering source repos ===")
gate = BlacklistGatekeeper(cfg.get("extra_blocked_extensions", []))
garbage_filter = PretrainGarbageFilter(json_quota=cfg.get("json_quota", 0.15))
dedup = ContextDeduplicator()
for rec in records: # cross-dedup: repo vs chat.jsonl
dedup.seed_text("\n".join([rec["user"], rec["thinking"], rec["assistant"]]))
telemetry: List[RepoTelemetry] = []
repo_tmp = paths["dir"] / "_repo_text.tmp.jsonl"
repo_docs = 0
try:
streamer = HFUniversalStreamer(token, gate)
except Exception as exc:
streamer = None
print(f" !! could not start HF streamer: {exc}")
with open(repo_tmp, "w", encoding="utf-8") as sink:
for entry in cfg.get("source_repos", []):
repo = entry.get("repo") if isinstance(entry, dict) else str(entry)
override = cfg.get("limit_override")
limit = _parse_limit(override if override not in (None, "") else
(entry.get("limit", 150) if isinstance(entry, dict) else 150))
tel = RepoTelemetry(repo=repo)
telemetry.append(tel)
if streamer is None:
tel.error = "streamer unavailable"
continue
try:
for item in streamer.stream(repo, limit, tel):
cleaned = garbage_filter.process(item["path"], item["ext"], item["text"])
if cleaned is None:
tel.filter_destroyed += 1
continue
doc, info = dedup.process(item["path"], cleaned)
if doc is None:
tel.duplicate_files += 1
continue
if info["status"] == "partial":
tel.partial_dedup += 1
sink.write(json.dumps({"text": doc, "source": repo, "path": item["path"]},
ensure_ascii=False) + "\n")
tel.kept += 1
repo_docs += 1
except Exception as exc:
tel.error = f"{type(exc).__name__}: {exc}"
print(f" !! stream error on {repo}: {tel.error}")
_print_table(telemetry)
if garbage_filter.stats:
print(" filter stats: " + ", ".join(f"{k}={v}" for k, v in sorted(garbage_filter.stats.items())))
if repo_docs == 0:
print(" !! WARNING: no repo documents survived - pre-training will use chat.jsonl data only.")
print("\n[Data] === STEP 3/3: building pretrain_clean.jsonl (repo text + chat mix) ===")
ratio = float(cfg.get("conv_ratio", 0.25))
chat_samples = build_pretrain_chat_samples(records, repo_docs, ratio)
total = repo_docs + len(chat_samples)
gap = max(1, repo_docs // len(chat_samples)) if chat_samples else 0
written_chat = 0
with open(paths["pretrain"], "w", encoding="utf-8") as out, open(repo_tmp, "r", encoding="utf-8") as src:
for i, line in enumerate(src, 1):
out.write(line)
if chat_samples and written_chat < len(chat_samples) and i % gap == 0:
out.write(json.dumps({"text": chat_samples[written_chat], "source": "chat.jsonl"},
ensure_ascii=False) + "\n")
written_chat += 1
while written_chat < len(chat_samples):
out.write(json.dumps({"text": chat_samples[written_chat], "source": "chat.jsonl"},
ensure_ascii=False) + "\n")
written_chat += 1
try:
repo_tmp.unlink()
except OSError:
pass
check = verify_pretrain_jsonl(paths["pretrain"])
print(f" [PRETRAIN-VERIFY] {'PASSED' if check['ok'] else 'FAILED'}: {check['lines']} lines "
f"({repo_docs} repo docs + {written_chat} chat samples = "
f"{(100.0 * written_chat / max(total, 1)):.0f}% conversation share)")
if not check["ok"]:
raise RuntimeError("pretrain_clean.jsonl failed verification: " + "; ".join(check["errors"][:5]))
sft_check = verify_sft_jsonl(paths["sft"])
if not sft_check["ok"]:
raise RuntimeError("sft_clean.jsonl failed final verification")
summary = {"sft_records": len(records), "repo_docs": repo_docs, "chat_pretrain_samples": written_chat,
"sft_path": str(paths["sft"]), "pretrain_path": str(paths["pretrain"])}
del records, chat_samples, dedup, garbage_filter, telemetry
gc.collect()
print(f"[Data] done -> {paths['sft'].name}, {paths['pretrain'].name} (RAM released)")
return summary