feat: auto-save browser challenge reports

This commit is contained in:
lda
2026-06-15 21:15:31 +07:00 Verified
parent 66eaa4a67d
commit 008103ac15
3 changed files with 220 additions and 5 deletions
@@ -8,7 +8,10 @@ from pathlib import Path
from examples.agent_challenges.browser_click_challenge.classification import (
extract_challenge_report,
)
from examples.agent_challenges.browser_click_challenge.opencode_io import _result_text
from examples.agent_challenges.browser_click_challenge.opencode_io import (
_result_text,
parse_opencode_output,
)
def save_report(
@@ -35,14 +38,44 @@ def report_from_result(result_path: Path) -> tuple[Path, str]:
result = json.loads(result_path.read_text(encoding="utf-8"))
if not isinstance(result, dict):
raise ValueError("result file must contain a JSON object")
parsed = result.get("parsed")
if not isinstance(parsed, dict):
raise ValueError("result file is missing parsed output")
report_text = _result_text(parsed)
report_text = _report_text_from_result(result)
workspace = _workspace_from_result(result, report_text)
return workspace, report_text
def save_report_from_result_payload(
result: dict[str, object],
*,
output_name: str = "final-report.md",
) -> Path:
"""Save final report text from an in-memory harness result payload."""
report_text = _report_text_from_result(result)
workspace = _workspace_from_result(result, report_text)
return save_report(
workspace=workspace,
report_text=report_text,
output_name=output_name,
)
def _report_text_from_result(result: dict[str, object]) -> str:
parsed = result.get("parsed")
if isinstance(parsed, dict):
return _result_text(parsed)
stdout = result.get("stdout")
if isinstance(stdout, str) and stdout.strip():
try:
recovered = parse_opencode_output(stdout)
except ValueError as exc:
raise ValueError(
"result file is missing parsed output and stdout has no report text"
) from exc
return _result_text(recovered)
raise ValueError("result file is missing parsed output")
def _workspace_from_result(result: dict[str, object], report_text: str) -> Path:
config = result.get("config")
if isinstance(config, dict):
@@ -61,6 +94,10 @@ def _workspace_from_result(result: dict[str, object], report_text: str) -> Path:
def _read_report_text(input_file: Path | None) -> str:
if input_file is None:
if sys.stdin.isatty():
raise ValueError(
"manual report mode needs piped stdin, --input-file, or --from-result"
)
return sys.stdin.read()
return input_file.read_text(encoding="utf-8")
@@ -50,6 +50,9 @@ from examples.agent_challenges.browser_click_challenge.opencode_io import ( # n
build_opencode_command,
parse_opencode_output,
)
from examples.agent_challenges.browser_click_challenge.reports import ( # noqa: E402
save_report_from_result_payload,
)
__all__ = [
"CHALLENGE_DIR",
@@ -85,6 +88,7 @@ __all__ = [
"render_prompt",
"rpc_url_for_port",
"run_trial",
"save_report_from_result_payload",
"server_command",
"start_server",
"starting_trial_index",
@@ -282,6 +286,7 @@ def run_trial(config: TrialConfig, *, index: int, results_dir: Path) -> dict[str
"stderr": exc.stderr or "",
"parsed": None,
}
_write_trial_report(payload)
_write_trial_result(results_dir, config=config, index=index, payload=payload)
return payload
@@ -305,6 +310,7 @@ def run_trial(config: TrialConfig, *, index: int, results_dir: Path) -> dict[str
"stderr": completed.stderr,
"parsed": parsed,
}
_write_trial_report(payload)
_write_trial_result(results_dir, config=config, index=index, payload=payload)
return payload
@@ -327,6 +333,15 @@ def _write_trial_result(
path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
def _write_trial_report(payload: dict[str, Any]) -> None:
try:
report_path = save_report_from_result_payload(payload)
except ValueError as exc:
payload["report_save_error"] = str(exc)
return
payload["report_path"] = report_path.as_posix()
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--model", default="opencode/mimo-v2.5-free")
@@ -422,6 +437,8 @@ def main(argv: list[str] | None = None) -> int:
"classification": result["classification"],
"returncode": result["returncode"],
"duration_seconds": round(float(result["duration_seconds"]), 3),
"report_path": result.get("report_path"),
"report_save_error": result.get("report_save_error"),
}
)
print(json.dumps(summaries[-1], sort_keys=True))