vadugwi / tools /aggregate.py
deucebucket's picture
Data flywheel: 50q + word ratings + FastAPI backend (anonymous collection)
6da6c49 verified
Raw
History Blame Contribute Delete
9.1 kB
#!/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()