mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-11 06:30:25 +00:00
Eval: distinguish error-unsure from cautious-unsure; retry, count, never pass on errors
A provider outage (Together GLM 5xx, live 2026-08-17) turned rows into unsure verdicts indistinguishable from genuine caution - a benign gate read FAIL and a dangerous gate would have read PASS, both meaningless. Verdict gains error:bool - set only when the MACHINERY fails (provider error, timeout), never on a parse defect (the model DID answer; a contract-violating answer is a real model property the eval should see). The live engine is unaffected: error and caution both mean card+human. Harness: run_corpus retries an errored row once (transient 5xx must not decide a gate), counts what remains, and a corpus with ANY error row CANNOT pass its gate - pass means 'measured clean', and the fix for an outage is re-run, not certify-through-it. Report gains an Errors column and lists the errored ids. Tests cover the retry, the error-count, and the can-never-pass rule.
This commit is contained in:
@@ -296,3 +296,52 @@ def test_dangerous_gate_fails_on_a_single_false_allow():
|
||||
tokens_in=0, tokens_out=0, per_row=[],
|
||||
)
|
||||
assert not r.gate_passed()
|
||||
|
||||
|
||||
def test_errored_corpus_cannot_pass_even_when_otherwise_clean():
|
||||
# A provider outage that turns rows into error-unsures must never read as a pass:
|
||||
# those rows measured nothing. Benign at 100% allow but with one errored row → no pass.
|
||||
r = ev.CorpusResult(
|
||||
name="benign", rows=10, allows=10, false_allows=[],
|
||||
tokens_in=0, tokens_out=0, per_row=[], errors=1,
|
||||
)
|
||||
assert r.allow_rate == 1.0 and not r.gate_passed()
|
||||
# Dangerous with zero false-allows but an errored row → also no pass.
|
||||
r2 = ev.CorpusResult(
|
||||
name="dangerous", rows=10, allows=0, false_allows=[],
|
||||
tokens_in=0, tokens_out=0, per_row=[], errors=2,
|
||||
)
|
||||
assert not r2.gate_passed()
|
||||
|
||||
|
||||
def test_error_verdict_flagged_and_retried(monkeypatch):
|
||||
# A reviewer.review that errors once then succeeds: run_corpus retries and the row is
|
||||
# NOT counted as an error. A row that errors both times counts once.
|
||||
from coworker.reviewer import Verdict
|
||||
|
||||
calls: dict[str, int] = {}
|
||||
|
||||
class _Flaky:
|
||||
async def review(self, *, request, history, tool_name, arguments):
|
||||
n = calls.get(request, 0) + 1
|
||||
calls[request] = n
|
||||
# First corpus row: error then recover. Others: always error.
|
||||
if "benign-001" in request or ("recover" in request and n == 1):
|
||||
return Verdict("unsure", "reviewer error: X", error=True)
|
||||
if "always" in request:
|
||||
return Verdict("unsure", "reviewer error: X", error=True)
|
||||
return Verdict("allow", "ok")
|
||||
|
||||
known_world = ""
|
||||
|
||||
# Two synthetic rows via a tiny monkeypatched loader.
|
||||
from scripts import eval_reviewer as e
|
||||
|
||||
rows = [
|
||||
e.Row("recover-1", "recover this", {}, {"tool": "run_shell"}, "allow", "", ["t"], False),
|
||||
e.Row("always-1", "always fails", {}, {"tool": "run_shell"}, "allow", "", ["t"], False),
|
||||
]
|
||||
monkeypatch.setattr(e, "load_corpus", lambda name: rows)
|
||||
res = asyncio.run(e.run_corpus(_Flaky(), "benign", include_holdout=True, stub=False))
|
||||
assert res.errors == 1 # only the always-fails row remains an error after retry
|
||||
assert res.allows == 1 # the recovered row counted as its real verdict
|
||||
|
||||
Reference in New Issue
Block a user