You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/10/07 05:00:47 UTC

(camel-performance-tests) 01/04: A third score: the last version right, without the errors of attempts the model fixed

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-performance-tests.git

commit 06327ef8c7f5b1d3f14b146d04c425d323b99f59
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Oct 6 10:17:33 2026 +0200

    A third score: the last version right, without the errors of attempts the model fixed
    
    ok counts every error a step logged, also those of an attempt the model fixed later, or of test data it wrote itself
    an earlier step; ok_final ignores errors altogether. ok_lastwrite is in between: the final state is right and no
    errors were logged after the model's last accepted write. The harness snapshots the errors at each accepted write;
    the summary shows the new column, and counts runs from before it as ok.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <no...@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
---
 ai-benchmark/agent_mcp_stepwise.py | 20 ++++++++++++++++++++
 ai-benchmark/summarize_stepwise.py | 16 ++++++++++------
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/ai-benchmark/agent_mcp_stepwise.py b/ai-benchmark/agent_mcp_stepwise.py
index c185c0d..a821f48 100755
--- a/ai-benchmark/agent_mcp_stepwise.py
+++ b/ai-benchmark/agent_mcp_stepwise.py
@@ -340,6 +340,21 @@ def score(step, project, cfg, mcp, name, before):
     # ok_final: the end state is right (files and log), whatever happened on the way; ok also needs no errors
     result["ok_final"] = result["file_ok"] and result["props_ok"] and result["log_ok"]
     result["ok"] = result["ok_final"] and errors_fine
+    # ok_lastwrite: the end state is right and the model's last version logged no errors; the errors of attempts it
+    # fixed on the way do not count (they do in ok). Without a write in the step it is the same as ok
+    last = before.get("last_write")
+    if last is not None:
+        after = sum(1 for l in lines if isinstance(l, dict) and lvl(l) == "ERROR" and error_key(l) not in last[0]
+                    and error_key(l) not in seen)
+        if isinstance(data, dict):
+            after += max(0, len(data.get("errors", []) or []) - max(last[1], before.get("error_count", 0)))
+    else:
+        after = result["errors"]
+    result["errors_after_last_write"] = after
+    lastwrite_fine = after == 0 or bool(chk.get("errors_ok"))
+    if chk.get("min_errors") and result["errors"] < chk["min_errors"]:
+        lastwrite_fine = False
+    result["ok_lastwrite"] = result["ok_final"] and lastwrite_fine
     result["log_sample"] = msgs[:5]
     return result, route, props
 
@@ -425,6 +440,9 @@ def main():
             trace = open(os.path.join(OUT, f"step{sid}.trace.jsonl"), "w")
             messages.append({"role": "user", "content": step["request"]})
             calls = 0; tokens = 0; t0 = time.time(); writes = 0; refused = 0; answer = ""
+            # the errors as they stood after the model's last accepted write: what came after is what its final
+            # version did; what came before were attempts it fixed (ok_lastwrite)
+            before["last_write"] = None
             if REFERENCE:
                 # the reference files stand in for the model's edits; the checks then score the steps file itself
                 writes += apply_reference(project, cfg, step.get("reference") or {}, mcp, name, before)
@@ -466,6 +484,8 @@ def main():
                         if fn["name"] in ("camel_write_file", "camel_edit_file") and ('"invalid"' in out
                                 or '"not-found"' in out or '"ambiguous"' in out or out.startswith("ERROR")):
                             refused += 1
+                        elif fn["name"] in ("camel_write_file", "camel_edit_file"):
+                            before["last_write"] = error_snapshot(mcp, name)
                         out = out[:TOOL_RESULT_CAP]
                         trace.write(json.dumps({"tool": fn["name"], "args": {k: (v if k != "content" else v[:1500]) for k, v in args.items()}, "result": out[:800]}) + "\n"); trace.flush()
                         messages.append({"role": "tool", "content": out, "tool_name": fn["name"]})
diff --git a/ai-benchmark/summarize_stepwise.py b/ai-benchmark/summarize_stepwise.py
index 8173c1b..0ba1a72 100644
--- a/ai-benchmark/summarize_stepwise.py
+++ b/ai-benchmark/summarize_stepwise.py
@@ -3,14 +3,15 @@
 
 A step passes (ok) when its files, log and probes are right and it logged no new errors; final state right (ok_final)
 leaves the errors out: a model that tests its own app (the HTTP request tool) causes errors on the way to a right
-answer, which ok counts against it."""
+answer, which ok counts against it. Last version right (ok_lastwrite) is in between: the final state is right and no
+errors were logged after the model's last accepted write; runs before it was recorded count it as ok."""
 import json, os, sys
 tag = sys.argv[1]; k = int(sys.argv[2]) if len(sys.argv) > 2 else 1
 tags = [f"{tag}-{i}" for i in range(1, k + 1)] if k > 1 else [tag]
 names = sorted(os.path.splitext(f)[0] for f in os.listdir("steps-ladder") if f.endswith(".json"))
-print(f"| example | steps | passes per step ({' '.join(tags)}) | all steps passed | final state right |")
-print("|---|---|---|---|---|")
-total = 0; possible = 0; clean = 0; final = 0
+print(f"| example | steps | passes per step ({' '.join(tags)}) | all steps passed | last version right | final state right |")
+print("|---|---|---|---|---|---|")
+total = 0; possible = 0; clean = 0; final = 0; lastw = 0
 for n in names:
     per = []; runs = []
     for t in tags:
@@ -26,5 +27,8 @@ for n in names:
         ok = sum(1 for r in runs if s < len(r) and r[s]["ok"]); cols.append(f"{ok}/{len(runs)}"); total += ok; possible += len(runs)
     allok = sum(1 for r in runs if r and all(x["ok"] for x in r)); clean += allok
     fin = sum(1 for r in runs for x in r if x.get("ok_final", x["ok"])); final += fin
-    print(f"| {n} | {steps} | {' '.join(cols)} | {allok}/{len(runs)} | {fin}/{sum(len(r) for r in runs)} |")
-print(f"\nsteps passed: {total} of {possible}; final state right: {final} of {possible}; runs with every step passed: {clean}")
+    lw = sum(1 for r in runs for x in r if x.get("ok_lastwrite", x["ok"])); lastw += lw
+    n_steps = sum(len(r) for r in runs)
+    print(f"| {n} | {steps} | {' '.join(cols)} | {allok}/{len(runs)} | {lw}/{n_steps} | {fin}/{n_steps} |")
+print(f"\nsteps passed: {total} of {possible}; last version right: {lastw} of {possible}; final state right: {final} of"
+      f" {possible}; runs with every step passed: {clean}")