Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| """aggregate.py — VADUGWI word-rating review report (offline, human-reviewed). | |
| Read the private dataset `deucebucket/vadugwi-data` (anonymous word/scenario | |
| ratings collected by the Space) and produce a human-review report of CANDIDATE | |
| corrections. NOTHING here is auto-applied to clanker — the engine's weights are | |
| genetically-optimized champions and FP-zero is non-negotiable. A human adopts any | |
| deltas deliberately. | |
| Two candidate sets (spec §11.4 / §11.7): | |
| KNOWN words -> weight-correction candidates: per word, mean human user_v vs | |
| engine compute_vadug(word).v, ranked by |systematic disagreement|. | |
| UNKNOWN words -> new-vocabulary candidates: per word, mean human rating mapped | |
| via SCALE_MAP to a candidate valence (0-255), expressed as a | |
| candidate dv vs the 128 neutral center, with sample size and | |
| agreement (stddev). | |
| Writes tools/aggregate_report.md. If the dataset is empty, says so and exits | |
| cleanly (expected until the Space starts collecting submissions). | |
| Spec: docs/.../2026-06-17-vadugwi-read-the-room-design.md §11.4, §11.7. | |
| """ | |
| import json | |
| import math | |
| import os | |
| import sys | |
| from collections import defaultdict | |
| CLANKER = "/home/deucebucket/ai-drive/clanker" | |
| sys.path.insert(0, CLANKER) | |
| HERE = os.path.dirname(os.path.abspath(__file__)) | |
| REPO = os.path.dirname(HERE) | |
| REPORT = os.path.join(HERE, "aggregate_report.md") | |
| DATA_REPO = "deucebucket/vadugwi-data" | |
| # Mirror of compute.js SCALE_MAP (-6..+6 -> 0..255, 128 = neutral center). | |
| SCALE_MAP = { | |
| -6: 0, -5: 0, -4: 51, -3: 102, -2: 128, -1: 140, | |
| 0: 153, 1: 166, 2: 179, 3: 204, 4: 230, 5: 255, 6: 255, | |
| } | |
| NEUTRAL = 128 | |
| def scale(raw): | |
| try: | |
| return SCALE_MAP[int(round(float(raw)))] | |
| except (ValueError, TypeError, KeyError): | |
| return None | |
| def load_rows(): | |
| """Return list of word-rating rows, or None if the dataset is unreachable/empty. | |
| Each row of interest: {word, user_rating, source?, lexicon_v?}. | |
| """ | |
| try: | |
| from huggingface_hub import HfApi, hf_hub_download | |
| except Exception as e: | |
| print(f"huggingface_hub unavailable: {e}") | |
| return None | |
| try: | |
| api = HfApi() | |
| info = api.dataset_info(DATA_REPO, files_metadata=True) | |
| except Exception as e: | |
| print(f"{DATA_REPO} unreachable: {type(e).__name__}: {e}") | |
| return None | |
| data_files = [s.rfilename for s in info.siblings | |
| if s.rfilename.endswith((".json", ".jsonl")) | |
| and not s.rfilename.startswith(".")] | |
| if not data_files: | |
| print(f"{DATA_REPO}: no data files yet (empty dataset).") | |
| return [] | |
| rows = [] | |
| for fn in data_files: | |
| try: | |
| path = hf_hub_download(DATA_REPO, fn, repo_type="dataset") | |
| except Exception as e: | |
| print(f"download {fn} failed: {e}") | |
| continue | |
| rows.extend(_iter_rows(path)) | |
| return rows | |
| def _iter_rows(path): | |
| out = [] | |
| try: | |
| if path.endswith(".jsonl"): | |
| with open(path) as f: | |
| for line in f: | |
| line = line.strip() | |
| if line: | |
| out.append(json.loads(line)) | |
| else: | |
| data = json.load(open(path)) | |
| out = data if isinstance(data, list) else data.get("rows", []) | |
| except Exception as e: | |
| print(f"parse {os.path.basename(path)} failed: {e}") | |
| # keep only word rows (carry a `word` + a rating) | |
| keep = [] | |
| for r in out: | |
| if not isinstance(r, dict): | |
| continue | |
| if "word" not in r: | |
| continue | |
| if r.get("user_rating") is None and r.get("rating") is None: | |
| continue | |
| keep.append(r) | |
| return keep | |
| def engine_valence(word): | |
| from engine.pendulum import compute_vadug | |
| result, _ = compute_vadug(word) | |
| return result.v | |
| def mean(xs): | |
| return sum(xs) / len(xs) if xs else float("nan") | |
| def stdev(xs): | |
| if len(xs) < 2: | |
| return 0.0 | |
| m = mean(xs) | |
| return math.sqrt(sum((x - m) ** 2 for x in xs) / (len(xs) - 1)) | |
| def write_empty_report(reason): | |
| lines = [ | |
| "# VADUGWI aggregate report", | |
| "", | |
| f"_Dataset_: `{DATA_REPO}`", | |
| "", | |
| f"**No ratings to aggregate yet.** {reason}", | |
| "", | |
| "This is expected before the Space begins collecting anonymous " | |
| "submissions. Re-run `python3 tools/aggregate.py` once rows exist.", | |
| "", | |
| "All outputs of this tool are CANDIDATES for human review and are never " | |
| "auto-applied to the clanker engine.", | |
| ] | |
| with open(REPORT, "w") as f: | |
| f.write("\n".join(lines) + "\n") | |
| print(f"Wrote empty report -> {REPORT}") | |
| def main(): | |
| rows = load_rows() | |
| if rows is None: | |
| write_empty_report( | |
| "The dataset could not be reached (auth/network) — nothing aggregated." | |
| ) | |
| return | |
| if not rows: | |
| write_empty_report("The dataset is empty (no submissions logged).") | |
| return | |
| # group ratings by (word, source) | |
| known = defaultdict(list) # word -> [user_v, ...] | |
| unknown = defaultdict(list) # word -> [user_v, ...] | |
| raw_by_word = defaultdict(list) # word -> [raw rating, ...] (unknown candidates) | |
| for r in rows: | |
| word = str(r.get("word", "")).strip().lower() | |
| if not word: | |
| continue | |
| raw = r.get("user_rating", r.get("rating")) | |
| uv = scale(raw) | |
| if uv is None: | |
| continue | |
| source = (r.get("source") or "").lower() | |
| if source == "unknown": | |
| unknown[word].append(uv) | |
| raw_by_word[word].append(float(raw)) | |
| else: | |
| known[word].append(uv) | |
| # KNOWN: human mean vs engine valence | |
| known_rows = [] | |
| for word, uvs in known.items(): | |
| try: | |
| ev = engine_valence(word) | |
| except Exception: | |
| continue | |
| hm = mean(uvs) | |
| known_rows.append({ | |
| "word": word, "n": len(uvs), "human_v": hm, | |
| "engine_v": ev, "disagree": hm - ev, "sd": stdev(uvs), | |
| }) | |
| known_rows.sort(key=lambda d: -abs(d["disagree"])) | |
| # UNKNOWN: human mean -> candidate dv vs 128 | |
| unknown_rows = [] | |
| for word, uvs in unknown.items(): | |
| hm = mean(uvs) | |
| unknown_rows.append({ | |
| "word": word, "n": len(uvs), "human_v": hm, | |
| "cand_dv": hm - NEUTRAL, "sd": stdev(uvs), | |
| }) | |
| unknown_rows.sort(key=lambda d: (-d["n"], -abs(d["cand_dv"]))) | |
| _write_report(rows, known_rows, unknown_rows) | |
| def _write_report(rows, known_rows, unknown_rows): | |
| L = [] | |
| L.append("# VADUGWI aggregate report") | |
| L.append("") | |
| L.append(f"_Dataset_: `{DATA_REPO}` · word rows aggregated: {len(rows)}") | |
| L.append("") | |
| L.append("> All entries below are **CANDIDATES for human review**. Nothing is " | |
| "auto-applied to the clanker engine — weights are genetically-optimized " | |
| "champions and FP-zero is non-negotiable. A human adopts deltas " | |
| "deliberately into `engine/forces_curated.py`.") | |
| L.append("") | |
| L.append("## Known words — weight-correction candidates") | |
| L.append("") | |
| L.append("Mean human valence vs engine `compute_vadug(word).v`, ranked by " | |
| "|systematic disagreement|. Large positive disagree = humans read the " | |
| "word warmer than the lexicon; negative = colder.") | |
| L.append("") | |
| if known_rows: | |
| L.append("| word | n | human_v | engine_v | disagree | agree(sd) |") | |
| L.append("|------|---|---------|----------|----------|-----------|") | |
| for d in known_rows[:100]: | |
| L.append(f"| {d['word']} | {d['n']} | {d['human_v']:.0f} | " | |
| f"{d['engine_v']:.0f} | {d['disagree']:+.0f} | {d['sd']:.0f} |") | |
| else: | |
| L.append("_No known-word ratings yet._") | |
| L.append("") | |
| L.append("## Unknown words — new-vocabulary candidates") | |
| L.append("") | |
| L.append("Words the engine had no entry for. Mean human rating mapped via " | |
| "SCALE_MAP to a candidate valence (0-255), expressed as a candidate " | |
| "**dv vs 128** (the neutral center). Larger sample + smaller sd = " | |
| "more trustworthy.") | |
| L.append("") | |
| if unknown_rows: | |
| L.append("| word | n | human_v | candidate dv | agree(sd) |") | |
| L.append("|------|---|---------|--------------|-----------|") | |
| for d in unknown_rows[:100]: | |
| L.append(f"| {d['word']} | {d['n']} | {d['human_v']:.0f} | " | |
| f"{d['cand_dv']:+.0f} | {d['sd']:.0f} |") | |
| else: | |
| L.append("_No unknown-word ratings yet._") | |
| L.append("") | |
| L.append("---") | |
| L.append("Candidate dv is a valence-only proposal; the other axes " | |
| "(da,dd,du,dg) still require human judgment before any vocabulary edit.") | |
| with open(REPORT, "w") as f: | |
| f.write("\n".join(L) + "\n") | |
| print(f"Wrote report ({len(known_rows)} known, {len(unknown_rows)} unknown " | |
| f"candidates) -> {REPORT}") | |
| if __name__ == "__main__": | |
| main() | |