SPB Git

spb/wp26_uqo

Public
6.1 KB · 150 lines python
1# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca2"""`av` command-line interface. Every command is a thin wrapper around a numbered script in scripts/3(loaded lazily by file path so that missing scripts fail with a clear message) or around the4experiments package."""5from __future__ import annotations67import importlib.util8import sys9from pathlib import Path10from typing import Optional1112import typer1314from .config import ROOT1516app = typer.Typer(name="av", help="Agentic valuation (UQO WP 26): data, baselines, agents, evaluation, paper.", no_args_is_help=True, add_completion=False)17SCRIPTS = ROOT / "scripts"1819# command -> script name pattern (scripts/NN_<name>.py). Numbers are not fixed here on purpose.20SCRIPT_FOR = {21    "synth": "synthetic",22    "build": "build_analytic",23    "splits": "splits",24    "features": "features",25    "geo": "geo",26    "train": "train_baselines",27    "run-arm": "run_arm",28    "evaluate": "evaluate",29    "tables": "tables",30    "paper": "paper",31}323334def _find_script(name: str) -> Path | None:35    hits = sorted(SCRIPTS.glob(f"[0-9][0-9]_{name}*.py"))36    return hits[0] if hits else None373839def _call_script(cmd: str, argv: list[str]) -> None:40    name = SCRIPT_FOR[cmd]41    path = _find_script(name)42    if path is None:43        typer.echo(f"[av {cmd}] scripts/NN_{name}.py does not exist yet (expected a numbered script in {SCRIPTS}).", err=True)44        raise typer.Exit(code=2)45    spec = importlib.util.spec_from_file_location(f"av_script_{name}", path)46    assert spec and spec.loader47    mod = importlib.util.module_from_spec(spec)48    sys.modules[spec.name] = mod49    spec.loader.exec_module(mod)50    if not hasattr(mod, "main"):51        typer.echo(f"[av {cmd}] {path.name} has no main(argv) function", err=True)52        raise typer.Exit(code=2)53    rc = mod.main(argv)54    if isinstance(rc, int) and rc != 0:55        raise typer.Exit(code=rc)565758def _passthrough(cmd: str):59    def _cmd(ctx: typer.Context) -> None:60        _call_script(cmd, list(ctx.args))6162    _cmd.__name__ = cmd.replace("-", "_")63    return _cmd646566for _c, _help in (67    ("build", "Build data/processed/analytic.parquet from the raw snapshot (scripts/01_build_analytic.py)."),68    ("splits", "Chronological / spatial splits (scripts/02_splits.py)."),69    ("features", "As-of feature matrix and comparable retrieval (scripts/NN_features.py)."),70    ("geo", "Geospatial layers (scripts/NN_geo.py)."),71    ("train", "Fit baselines A–D on train, tune on validation (scripts/NN_train_baselines.py)."),72    ("evaluate", "Metrics, calibration, stratification, clustered inference (scripts/NN_evaluate.py)."),73    ("tables", "LaTeX tables and figures from results/ (scripts/NN_tables.py)."),74    ("paper", "Build the PDF (scripts/NN_paper.py)."),75):76    app.command(name=_c, help=_help + " Extra arguments are passed through.", context_settings={"allow_extra_args": True, "ignore_unknown_options": True})(_passthrough(_c))777879@app.command(help="Generate the clearly labelled synthetic dataset (data/synthetic/).")80def synth(n: int = typer.Option(20000, help="number of synthetic transactions")) -> None:81    if _find_script(SCRIPT_FOR["synth"]) is not None:82        _call_script("synth", ["--n", str(n)])83        return84    from .data.synthetic import write_default8586    df = write_default(n)87    typer.echo(f"synthetic: {df.shape} rows -> data/synthetic/synthetic_transactions.parquet")888990@app.command(name="run-arm", help="Run one LLM arm (E–J) on a subsample; mock unless --live and keys are present.")91def run_arm(92    arm: str = typer.Option("F_independent", help="treatments.yaml arm id: E_single_llm, F_independent, G_debate, H_debate_skeptic, I_placebo, J_cost_matched"),93    rounds: Optional[int] = typer.Option(None, help="debate rounds (G/H/I); G defaults to 2 if omitted"),94    committee: str = typer.Option("all_anthropic", help="all_anthropic | all_openai | mixed_random"),95    split: str = typer.Option("validation"),96    n: int = typer.Option(24, help="number of properties"),97    live: bool = typer.Option(False, "--live", help="call the vendors (requires keys); otherwise the mock provider"),98    tier: Optional[str] = typer.Option(None, help="mid | small (configs/models.yaml cost_matched_pairs)"),99    run_id: Optional[str] = typer.Option(None, "--run-id"),100    synthetic: bool = typer.Option(False, "--synthetic", help="use data/synthetic/ instead of data/processed/"),101    effort: Optional[str] = typer.Option(None, help="reasoning effort passed to the providers"),102    concurrency: int = typer.Option(4, help="properties valued concurrently"),103    seed: Optional[int] = typer.Option(None),104) -> None:105    argv = ["--arm", arm, "--committee", committee, "--split", split, "--n", str(n), "--concurrency", str(concurrency)]106    if rounds is not None:107        argv += ["--rounds", str(rounds)]108    if live:109        argv.append("--live")110    if tier:111        argv += ["--tier", tier]112    if run_id:113        argv += ["--run-id", run_id]114    if synthetic:115        argv.append("--synthetic")116    if effort:117        argv += ["--effort", effort]118    if seed is not None:119        argv += ["--seed", str(seed)]120    _call_script("run-arm", argv)121122123@app.command(name="providers-check", help="List vendor models through the official SDKs and check configs/models.yaml ids.")124def providers_check() -> None:125    from .experiments.providers_check import check, print_report126127    rep = check()128    print_report(rep)129    if not rep["all_configured_found"]:130        raise typer.Exit(code=1)131132133@app.command(help="Write results/freeze.json (prompt/config/code/model hashes) or --check it against the current code.")134def freeze(check: bool = typer.Option(False, "--check", help="compare the current state with results/freeze.json")) -> None:135    from .experiments.freeze import check_freeze, write_freeze136137    if check:138        diffs = check_freeze()139        if diffs:140            typer.echo("PROTOCOL DEVIATION — items changed since the freeze:\n  " + "\n  ".join(diffs))141            raise typer.Exit(code=1)142        typer.echo("freeze OK: prompts, configs, code and model ids unchanged")143        return144    p = write_freeze()145    typer.echo(f"freeze written: {p}")146147148if __name__ == "__main__":149    app()150
Files · main
Menu

Theme