#!/usr/bin/env python3 """Verdichtet die RawResult.json unterhalb eines Verzeichnisses zu einer Tabelle. Gedacht fuer die Sichtung einer Matrix: Welche Zelle hat gemessen, welche ist eine Fehlmessung, und woran lag es. Schreibt nichts - reine Auswertung. python lauf-uebersicht.py "" [--details] """ from __future__ import annotations import argparse import json from pathlib import Path def schreibziele(lauf: Path) -> list[str]: """Wohin der Lauf zu schreiben versuchte, samt Ergebnis. Der haeufigste Grund fuer ein leeres Ergebnisverzeichnis ist ein Schreibversuch an den falschen Ort - der ist ohne diese Aufstellung nicht zu sehen, weil ``written_files`` dann schlicht leer bleibt. """ session = lauf / "_meta" / "opencode-session.json" if not session.is_file(): return [] try: daten = json.loads(session.read_text(encoding="utf-8")) except (json.JSONDecodeError, OSError): return [] ziele = [] for nachricht in daten.get("messages", []): for teil in nachricht.get("parts", []): if teil.get("type") != "tool" or teil.get("tool") not in ("write", "edit"): continue zustand = teil.get("state", {}) ziele.append( f"{teil.get('tool')} {zustand.get('status')} -> " f"{zustand.get('input', {}).get('filePath')}" ) return ziele def main() -> int: parser = argparse.ArgumentParser(description="Uebersicht ueber Versuchslaeufe") parser.add_argument("verzeichnis") parser.add_argument("--details", action="store_true", help="Schreibziele zeigen") args = parser.parse_args() wurzel = Path(args.verzeichnis) zeilen = [] for datei in sorted(wurzel.rglob("RawResult.json")): try: d = json.loads(datei.read_text(encoding="utf-8")) except (json.JSONDecodeError, OSError): continue lauf = datei.parent zeilen.append( { "lauf": lauf.name, "modus": d.get("mode", "?"), "exit": d.get("exit_code"), "status": d.get("subtype", "?"), "min": round(d.get("duration_ms", 0) / 60000, 1), "turns": d.get("num_turns", 0), "tools": d.get("tool_call_count", 0), "sub": d.get("subagent_stats", {}).get("spawned", 0), "dateien": len(d.get("written_files") or []), # Eine 0 aus einem laufenden Subagenten ist kein Messwert. "tokens": ( d["usage"]["total_tokens"] if d.get("usage_captured", True) else None ), "pfad": lauf, } ) if not zeilen: print("Keine RawResult.json gefunden.") return 1 kopf = f"{'Lauf':44} {'Modus':8} {'St':3} {'Min':>6} {'Turn':>5} {'Tool':>5} {'Sub':>4} {'Dat':>4} {'Tokens':>10}" print(kopf) print("-" * len(kopf)) for z in zeilen: tok = "n. erf." if z["tokens"] is None else f"{z['tokens']:,}" print( f"{z['lauf'][:44]:44} {z['modus']:8} {str(z['exit']):3} {z['min']:6.1f} " f"{z['turns']:5} {z['tools']:5} {z['sub']:4} {z['dateien']:4} {tok:>10}" ) if args.details: for ziel in schreibziele(z["pfad"]): print(f" {ziel}") gueltig = [z for z in zeilen if z["dateien"] > 0] print( f"\n{len(zeilen)} Laeufe, davon {len(gueltig)} mit Ergebnisdateien, " f"{len(zeilen) - len(gueltig)} Fehlmessungen." ) return 0 if __name__ == "__main__": raise SystemExit(main())