Spaces:
Paused
Paused
File size: 8,187 Bytes
f850954 5dbd0bf f850954 5dbd0bf 1a36c6b 5dbd0bf 1a36c6b 5dbd0bf 1a36c6b 5dbd0bf 1a36c6b 5dbd0bf 1a36c6b 5dbd0bf 1a36c6b 5dbd0bf 1a36c6b 5dbd0bf f850954 5dbd0bf f850954 5dbd0bf f850954 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | """Inspect and grade what testers generated.
python -m api.review summary counts per kind: auto-valid, tester-rated, reviewed
python -m api.review list [--kind simulate|code] [--tester NAME] [--unreviewed] [-n 50]
python -m api.review show ID [--attempts] request, retrieved sources, output, validation
python -m api.review files ID DIR write the output to DIR (scene.xml or the package files)
python -m api.review mark ID correct|partial|incorrect [--note TEXT]
python -m api.review export out.jsonl everything, one JSON object per line
python -m api.review runs [--tester NAME] [-n 50] every /simulate/run call: who, when, status, GPU time
python -m api.review usage runs per tester: total, last 24 h, busiest 10 minutes
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from .config import get_settings
from .logstore import LogStore
def _short(text: str, n: int = 60) -> str:
text = " ".join(str(text).split())
return text if len(text) <= n else text[: n - 1] + "…"
def _request_text(row: dict) -> str:
req = row["request"] or {}
return req.get("description") or req.get("request") or ""
def _auto(row: dict) -> str:
if row.get("error"):
return "ERROR"
return {True: "valid", False: "invalid", None: "-"}[row.get("valid")]
def cmd_list(logs: LogStore, args) -> int:
rows = logs.list(args.kind, args.tester, args.unreviewed, args.n)
header = ("id", 12), ("created (UTC)", 19), ("tester", 10), ("kind", 8), ("auto", 7), ("tester", 7), ("review", 9)
print(" ".join(f"{h:<{w}}" for h, w in header) + " request")
for r in rows:
print(
f"{r['id']:<12} {r['created_at'][:19]:<19} {_short(r['tester'], 10):<10} {r['kind']:<8} "
f"{_auto(r):<7} {r['tester_rating'] or '-':<7} {r['review_verdict'] or '-':<9} {_short(_request_text(r))}"
)
if not rows:
print("(no generations)")
return 0
def cmd_show(logs: LogStore, args) -> int:
r = logs.get(args.id)
if not r:
print(f"no generation {args.id}", file=sys.stderr)
return 1
print(f"id: {r['id']} kind: {r['kind']} tester: {r['tester']} at: {r['created_at']}")
print(f"model: {r['model']} duration: {r['duration_ms']} ms attempts: {len(r['attempts'] or [])}")
print(f"request: {json.dumps(r['request'])}")
print(f"auto: {_auto(r)} {json.dumps(r['validation'])}")
if r.get("error"):
print(f"error: {r['error']}")
print(f"tester: {r['tester_rating'] or '-'} {r['tester_note'] or ''}")
print(f"review: {r['review_verdict'] or '-'} {r['review_note'] or ''}")
print("sources:")
for s in r["sources"] or []:
print(f" - {s}")
if args.attempts:
for i, a in enumerate(r["attempts"] or [], 1):
print(f"\n----- attempt {i}: {json.dumps(a.get('validation'))}")
print(a.get("raw_output", ""))
print("\n----- output")
if r["kind"] == "code" and r["output"]:
for path, content in json.loads(r["output"]).items():
print(f"\n=== {path}\n{content}")
else:
print(r["output"] or "(none)")
return 0
def cmd_files(logs: LogStore, args) -> int:
r = logs.get(args.id)
if not r or not r["output"]:
print(f"no output stored for {args.id}", file=sys.stderr)
return 1
out = Path(args.dir)
out.mkdir(parents=True, exist_ok=True)
files = json.loads(r["output"]) if r["kind"] == "code" else {"scene_rosdiff.xml": r["output"]}
for path, content in files.items():
dest = (out / path).resolve()
if out.resolve() not in dest.parents:
print(f"skipping unsafe path {path}", file=sys.stderr)
continue
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(content)
print(f"wrote {dest}")
return 0
def cmd_mark(logs: LogStore, args) -> int:
if not logs.set_review(args.id, args.verdict, args.note):
print(f"no generation {args.id}", file=sys.stderr)
return 1
print(f"{args.id}: {args.verdict}")
return 0
def cmd_summary(logs: LogStore, args) -> int:
rows = logs.summary()
if not rows:
print("(no generations)")
for r in rows:
print(
f"{r['kind']:<9} total {r['total']:>4} | auto-valid {r['auto_valid'] or 0:>4} | "
f"tester says works {r['tester_works'] or 0}/{r['tester_rated'] or 0} | "
f"reviewed correct {r['reviewed_correct'] or 0}/{r['reviewed'] or 0}"
)
return 0
def cmd_export(logs: LogStore, args) -> int:
with open(args.path, "w") as f:
for r in logs.all():
f.write(json.dumps(r) + "\n")
print(f"wrote {args.path}")
return 0
def cmd_runs(logs: LogStore, args) -> int:
rows = logs.list_runs(args.tester, args.n)
header = ("job", 16), ("kind", 9), ("created (UTC)", 19), ("tester", 12), ("status", 11), ("sim s", 5)
header += (("run s", 6), ("cost $", 6))
print(" ".join(f"{h:<{w}}" for h, w in header) + " source")
for r in rows:
gpu = f"{r['execution_ms'] / 1000:.1f}" if r.get("execution_ms") else "-"
cost = f"{r['cost_usd']:.3f}" if r.get("cost_usd") is not None else "-"
if r.get("parent_id"):
source = f"run {r['parent_id']}"
else:
source = f"generation {r['generation_id']}" if r.get("generation_id") else "raw input"
print(
f"{r['id']:<16} {(r.get('kind') or 'sim'):<9} {r['created_at'][:19]:<19} {_short(r['tester'], 12):<12} "
f"{r['status']:<11} {r['duration_s'] or 0:<5g} {gpu:<6} {cost:<6} {source}"
)
if not rows:
print("(no runs)")
return 0
def cmd_usage(logs: LogStore, args) -> int:
from .jobs import month_spend
settings = get_settings()
rows = logs.usage()
spent, limit = month_spend(settings, logs), settings.monthly_credit_usd
print(f"this month (estimated): ${spent:.2f}" + (f" of ${limit:.2f} limit" if limit else " (no limit)"))
if not rows:
print("(no jobs)")
for u in rows:
cost = f" ~${u['cost_usd']:.2f}"
flag = " <-- burst" if u["max_in_10min"] >= settings.run_burst_warn else ""
print(
f"{u['tester']:<16} runs {u['runs']:>4} last 24h {u['last_24h']:>4} busiest 10 min "
f"{u['max_in_10min']:>3} GPU {u['gpu_seconds']:>7.1f} s{cost} last {u['last'][:19]}{flag}"
)
return 0
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--db", type=Path, help="default: $ROSDIFF_LOG_DB or data/generations.sqlite3")
sub = parser.add_subparsers(dest="cmd", required=True)
sub.add_parser("summary")
p = sub.add_parser("list")
p.add_argument("--kind", choices=["simulate", "code"])
p.add_argument("--tester")
p.add_argument("--unreviewed", action="store_true")
p.add_argument("-n", type=int, default=50)
p = sub.add_parser("show")
p.add_argument("id")
p.add_argument("--attempts", action="store_true", help="also print every raw model attempt")
p = sub.add_parser("files")
p.add_argument("id")
p.add_argument("dir")
p = sub.add_parser("mark")
p.add_argument("id")
p.add_argument("verdict", choices=["correct", "partial", "incorrect"])
p.add_argument("--note", default="")
p = sub.add_parser("export")
p.add_argument("path")
p = sub.add_parser("runs")
p.add_argument("--tester")
p.add_argument("-n", type=int, default=50)
sub.add_parser("usage")
args = parser.parse_args(argv)
logs = LogStore(args.db or get_settings().log_db)
handlers = {
"list": cmd_list,
"show": cmd_show,
"files": cmd_files,
"mark": cmd_mark,
"summary": cmd_summary,
"export": cmd_export,
"runs": cmd_runs,
"usage": cmd_usage,
}
return handlers[args.cmd](logs, args)
if __name__ == "__main__":
sys.exit(main())
|