"""Build the model's input state exactly as in training and evaluation. python make_state.py diff CHANGE.diff --task-file TASK.txt [--baseline-file BASELINE.txt] python make_state.py snippet CODE.py --lang py --task "Clean up the build cache" Prints the state. Use it as `state=` in `model.system_one(...)` (see ../examples/usage.py). """ import argparse import sys from collections import Counter, defaultdict from pathlib import Path from behaviour import analyse_diff, analyse_snippet BUDGET_CHARS = 8192 * 3 # ≈ 8k tokens; longer changes are cut with a marker, as in training def summary(rep): t = (rep or {}).get("text") or "" return t.strip() or "(no security-relevant behaviour found)" def baseline_text(counter_by_kind): """Repository baseline: the most common operands per behaviour kind across the repo's ordinary changes.""" if not counter_by_kind: return "(no repository history available)" lines = [] for kind in sorted(counter_by_kind): top = counter_by_kind[kind].most_common(8) lines.append(f"- {kind}: " + "; ".join(f"{op} ({n})" for op, n in top)) return "\n".join(lines) def add_to_baseline(acc, rep): """Accumulate one ordinary change's report into acc (a defaultdict(Counter)) for baseline_text.""" for b in (rep or {}).get("behaviours", []): acc[b["kind"]][str(b.get("operand") or b.get("path") or "?")[:80]] += 1 def new_baseline(): return defaultdict(Counter) def state(task, baseline, beh, change): parts = [f"Task:\n{(task or '(not provided)').strip()}", f"Repository baseline (normal behaviour of this repo/org):\n{baseline}", f"Behaviour summary (automated, not a verdict):\n{beh}"] head = "\n\n".join(parts) + "\n\nChange:\n" room = BUDGET_CHARS - len(head) if len(change) > room: change = change[:max(room - 80, 0)] + f"\n[... {len(change) - room + 80} more characters omitted]" return head + change def main(argv=None): ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) ap.add_argument("mode", choices=("diff", "snippet")) ap.add_argument("input", help="file, or - for stdin") ap.add_argument("--task"); ap.add_argument("--task-file") ap.add_argument("--baseline-file", help="precomputed baseline text (default: no repository history)") ap.add_argument("--lang", choices=("js", "ts", "py", "sh"), help="snippet language") a = ap.parse_args(argv) change = sys.stdin.read() if a.input == "-" else Path(a.input).read_text(encoding="utf-8", errors="replace") task = a.task or (Path(a.task_file).read_text(errors="replace") if a.task_file else None) rep = analyse_diff(change, task=task) if a.mode == "diff" else analyse_snippet(change, lang=a.lang, task=task) base = Path(a.baseline_file).read_text().strip() if a.baseline_file else baseline_text(None) print(state(task, base, summary(rep), change)) return 0 if __name__ == "__main__": sys.exit(main())