fix: make h2 patch repair failures diagnosable

This commit is contained in:
thanhnv
2026-07-18 12:04:32 +07:00
parent 61a8253679
commit 659ed09839
7 changed files with 120 additions and 9 deletions
@@ -299,9 +299,9 @@ def validate_write_output(text: str) -> str:
def patch_repair_attempts() -> int:
"""Return the bounded number of chances to repair an invalid model diff."""
try:
configured = int(os.environ.get("CASAN_GOAL_PATCH_REPAIR_ATTEMPTS", "2"))
configured = int(os.environ.get("CASAN_GOAL_PATCH_REPAIR_ATTEMPTS", "3"))
except ValueError:
configured = 2
configured = 3
return min(max(configured, 0), 3)
@@ -356,20 +356,45 @@ def repair_write_output(model: str, original_prompt: str, invalid_output: str, c
)
candidates = patch_repair_models(model)[:patch_repair_attempts()]
last_metadata, last_reason = {}, "goal_patch_missing"
for candidate in candidates:
attempts = []
for attempt_number, candidate in enumerate(candidates, start=1):
ok, output, metadata, reason = call_model(candidate, repair_prompt, candidate.startswith(("openai:", "anthropic:", "openai-compatible:")), max_output_tokens=patch_output_tokens())
metadata = dict(metadata)
metadata["repair_model"] = candidate
metadata["repair_attempt"] = attempt_number
last_metadata = metadata
if not ok:
last_reason = f"goal_patch_repair_failed:{reason}"
attempts.append({
"attempt": attempt_number,
"provider": provider_for_model(candidate),
"model": candidate,
"status": "failed",
"reason": last_reason,
})
continue
try:
validate_write_output(output)
except ValueError as error:
last_reason = str(error)
attempts.append({
"attempt": attempt_number,
"provider": provider_for_model(candidate),
"model": candidate,
"status": "failed",
"reason": last_reason,
})
continue
attempts.append({
"attempt": attempt_number,
"provider": provider_for_model(candidate),
"model": candidate,
"status": "pass",
"reason": "ok",
})
metadata["repair_attempts"] = attempts
return True, output, metadata, "ok"
last_metadata["repair_attempts"] = attempts
return False, "", last_metadata, f"goal_patch_repair_invalid:{last_reason}"
@@ -785,8 +810,9 @@ def run(job_path: str) -> int:
reason = f"goal_patch_repair_invalid:{repair_error}"
if reason:
actual_repair_model = str(repaired_meta.get("repair_model") or repair_model)
update_job(job_path, local_usage=local_meta, patch_repair_attempts=repaired_meta.get("repair_attempts", []))
stage(job_path, "local-worker", "error", reason, provider_for_model(actual_repair_model), actual_repair_model)
emit(goal_id, "H2-tool", "error", "Worker violated patch output contract after repair", {"reason": reason, "repair_provider": provider_for_model(actual_repair_model), "repair_model": actual_repair_model})
emit(goal_id, "H2-tool", "error", "Worker violated patch output contract after repair", {"reason": reason, "repair_provider": provider_for_model(actual_repair_model), "repair_model": actual_repair_model, "repair_attempts": repaired_meta.get("repair_attempts", [])})
raise ValueError(reason)
stage(job_path, "local-worker", "pass", "Primary solution prepared", job.get("local_provider", ""), local_model)
update_job(job_path, local_draft=safe_local, local_usage=local_meta)