"""Generate testresult.html: every unique activity prompt -> expected intent, model output, executor command (Windows + Pi),
device, model status, executor status (real execution for safe read-only actions on this machine, dry-run otherwise).
python eval/make_report.py (uses model/ numpy runtime for the curated rows)"""
import json, os, sys, html, time, collections, platform as _pf
HERE = os.path.dirname(os.path.abspath(__file__)); ROOT = os.path.dirname(HERE)
sys.path.insert(0, os.path.join(ROOT, "model")); sys.path.insert(0, os.path.join(ROOT, "executor"))
from runtime import CommandModel # noqa
from catalog import ACTIONS # noqa
import executor as EX # noqa
run_dir = sys.argv[1] if len(sys.argv) > 1 else None
_sp = os.path.join(run_dir, "eval", "summary.json") if run_dir else os.path.join(HERE, "model_eval_summary.json")
summary = json.load(open(_sp)) if os.path.exists(_sp) else {}
curated = [json.loads(l) for l in open(os.path.join(HERE, "curated_prompts.jsonl"), encoding="utf-8")]
model = CommandModel(os.path.join(ROOT, "model"))
esc = lambda s: html.escape(str(s if s is not None else ""))
EXEC_HERE = {"sh", "py"} # kinds that can really run on this Linux box
rows = []; t_all = time.time(); exec_counts = collections.Counter(); model_ok = 0
for r in curated:
out = model.predict(r["text"]); cmd = {k: v for k, v in out.items() if k != "_meta"}; canon = out["_meta"]["canonical"]
ok = canon == r["target"]; model_ok += ok
plans = {pl: EX.plan(cmd, pl, r["text"]) for pl in ("pi", "win")}
ppi = plans["pi"]; st, outp = "", ""
if not ppi.supported: st, outp = "n/a on Pi/Linux", ppi.note
elif ppi.kind not in EXEC_HERE: st, outp = "dry-run", f"{ppi.kind} step (needs desktop/keyboard); command rendered only"
elif ppi.risk != "safe": st, outp = f"dry-run ({ppi.risk}, gated)", "rendered only; UI asks for confirmation before running"
elif not ok: st, outp = "skipped (model mismatch)", "not executed because the parse differed from the expected intent"
else:
res = EX.run(ppi, cmd, dry_run=False); outp = res["output"][:300]
st = "executed: tool not on this machine" if ("command not found" in outp or ("No such file" in outp and "/sys/" in outp)) else f"executed: {res['status']}"
exec_counts[st.split(":")[0].split(" (")[0]] += 1
rows.append({"text": r["text"], "action": r["action"], "device": r["device"], "risk": r["risk"], "expected": r["target"], "pred": canon, "json": cmd, "ok": ok, "conf": out["_meta"]["confidence"],
"cmd_win": plans["win"].command if plans["win"].supported else "— " + plans["win"].note, "cmd_pi": ppi.command if ppi.supported else "— " + ppi.note, "kind_pi": ppi.kind, "exec_status": st, "exec_out": outp})
acc = model_ok / len(rows)
def cell_json(j): return " ".join(f"{esc(k)}={esc('••••' if k == 'password' else v)}" for k, v in j.items() if k != "action")
summ_rows = "".join(f"
{esc(n)}
{s['n']}
=0.99 else 'y' if s['exact']>=0.95 else 'r'}'>{s['exact']*100:.2f}%
{s['action']*100:.2f}%
{esc(', '.join(f'{a}→{b} ({n})' for (a,b),n in s['top_confusions'][:5]))}
" for n, s in summary.items())
by_risk = collections.Counter(r["risk"] for r in rows); by_dev = collections.Counter(r["device"] for r in rows)
table = "".join(f"""
{i+1}
{esc(r['text'])}
{esc(r['action'])}
{esc(ACTIONS[r['action']]['desc'])}
{esc(r['pred'])}
{cell_json(r['json'])}
conf {r['conf']*100:.1f}%
{'PASS' if r['ok'] else 'FAIL'}{'' if r['ok'] else '
expected: '+esc(r['expected'])+'
'}
{esc(r['risk'])}
{esc(r['device'])}
Windows
{esc(r['cmd_win'][:400])}
Raspberry Pi [{esc(r['kind_pi'])}]
{esc(r['cmd_pi'][:400])}
{esc(r['exec_status'])}
{esc(r['exec_out'][:300])}
""" for i, r in enumerate(rows))
doc = f"""Union Command Agent — test results
Union Command Agent — test results
generated {time.strftime('%Y-%m-%d %H:%M')} on {esc(_pf.node())} ({esc(_pf.system())}) · model: char-level transformer seq2seq, {sum(v.size for v in model.W.values())/1e6:.2f}M params, grammar-constrained decoding · {len(ACTIONS)} actions
{acc*100:.2f}%unique-activity prompts exact match ({model_ok}/{len(rows)}) — one held-out-template prompt per action
{''.join(f"
=0.99 else 'y' if s['exact']>=0.95 else 'r'}'>{s['exact']*100:.2f}%{esc(n)} exact (n={s['n']})
" for n, s in summary.items() if n in ('val','test_strict','curated_strict','B_golden_final'))}
exact = full canonical target (action + every slot value) matches. action = action label only. val shares templates with training. test / curated are held-out templates; since v6 the dataset also contains machine paraphrases of templates, so a held-out paraphrase can have a sibling paraphrase in train — test_strict / curated_strict keep only base-template rows (no sibling anywhere in train) and are the honest generalisation numbers. A_* / B_* are the two source projects' own evaluation sets relabelled to the union schema (their label conventions differ from the union in places, so they under-count).
set
n
exact
action
top confusions
{summ_rows}
Executor status on this machine
Safe, read-only actions whose Pi/Linux command is runnable here were actually executed and their output captured. Caution/critical actions are rendered but gated (the UI asks for confirmation; critical ones require typing the action name). Key/desktop steps need a display. {esc(', '.join(f'{k}: {v}' for k, v in exec_counts.most_common()))}.