Skip to content

Commit 0af0256

Browse files
committed
Match random fallback labeling protocol
1 parent 9396a45 commit 0af0256

6 files changed

Lines changed: 99 additions & 20 deletions

File tree

‎RUN_STEPS.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ deterministic responses in the output directory's `cache/` subdirectory.
124124
The runtime refactor preserves the standard single-item and batched prompt
125125
templates used by the previous release; snapshot tests cover all four paths.
126126
For stochastic runs, set `VERIBENCH_JUDGE_TEMPERATURE=1` and
127-
`VERIBENCH_DISABLE_JUDGE_CACHE=1`.
127+
`VERIBENCH_DISABLE_JUDGE_CACHE=1`. Judgments that remain unparsable after the
128+
domain parser and correction rules receive uniformly random positive or
129+
negative labels and remain in the denominator. Set `VERIBENCH_FALLBACK_SEED` to
130+
make fallback labels deterministic.
128131

129132
DeepResearch:
130133

‎code/run_judges/README.md‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ The two runner files are the execution entrypoints. The `agenticcoding/`
1616
directory contains only the result-comparison utility.
1717
The standard DeepResearch and AgenticCoding prompt templates are unchanged
1818
from the previous release and are protected by prompt snapshot tests.
19-
As in the released experimental runner, missing or unparsable judgments are
20-
retained in the output and assigned the negative label.
19+
As in the released experimental runner, judgments that remain missing or
20+
unparsable after parsing and correction are retained in the output and assigned
21+
uniformly random positive or negative labels. Set `VERIBENCH_FALLBACK_SEED` to
22+
make these fallback labels deterministic for a rerun.
2123

2224
Convenience wrappers:
2325

@@ -55,6 +57,7 @@ For stochastic self-voting runs, you may also set:
5557
```bash
5658
export VERIBENCH_JUDGE_TEMPERATURE=1
5759
export VERIBENCH_DISABLE_JUDGE_CACHE=1
60+
export VERIBENCH_FALLBACK_SEED=1234
5861
```
5962

6063
---

‎code/run_judges/agenticcoding_runner.py‎

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
sys.path.insert(0, str(ROOT))
1717

1818
from prompting import agenticcoding_prompt
19-
from runtime import APIModel
19+
from runtime import APIModel, fallback_binary_label
2020

2121

2222
def read_jsonl(path: str | Path) -> list[dict[str, Any]]:
@@ -111,12 +111,24 @@ def normalize_result(value: Any) -> str:
111111
raise ValueError(f"invalid result value: {value!r}")
112112

113113

114-
def failed_check(check_id: str, reason: str) -> dict[str, str]:
115-
return {"check_id": check_id, "result": "fail", "reasoning": reason}
114+
def fallback_check(check_id: str, reason: str, key: str = "") -> dict[str, str]:
115+
result = "success" if fallback_binary_label(key or check_id) else "fail"
116+
return {
117+
"check_id": check_id,
118+
"result": result,
119+
"reasoning": f"{reason}; assigned uniformly random {result} label",
120+
}
116121

117122

118-
def parse_checks(raw: str | None, expected: list[dict[str, Any]]) -> list[dict[str, str]]:
123+
def parse_checks(
124+
raw: str | None,
125+
expected: list[dict[str, Any]],
126+
fallback_prefix: str = "",
127+
) -> list[dict[str, str]]:
119128
expected_ids = [str(item["check_id"]) for item in expected]
129+
def fallback(check_id: str, reason: str) -> dict[str, str]:
130+
key = f"{fallback_prefix}::{check_id}" if fallback_prefix else check_id
131+
return fallback_check(check_id, reason, key)
120132
try:
121133
parsed = extract_json(raw or "")
122134
values = parsed if isinstance(parsed, list) else [parsed]
@@ -133,16 +145,16 @@ def parse_checks(raw: str | None, expected: list[dict[str, Any]]) -> list[dict[s
133145
output = []
134146
for check_id in expected_ids:
135147
if check_id in duplicate:
136-
output.append(failed_check(check_id, "duplicate check_id in judge output"))
148+
output.append(fallback(check_id, "duplicate check_id in judge output"))
137149
continue
138150
value = by_id.get(check_id)
139151
if value is None:
140-
output.append(failed_check(check_id, "judge output omitted this check_id"))
152+
output.append(fallback(check_id, "judge output omitted this check_id"))
141153
continue
142154
try:
143155
result = normalize_result(value.get("result"))
144156
except ValueError as exc:
145-
output.append(failed_check(check_id, str(exc)))
157+
output.append(fallback(check_id, str(exc)))
146158
continue
147159
output.append({
148160
"check_id": check_id,
@@ -151,7 +163,7 @@ def parse_checks(raw: str | None, expected: list[dict[str, Any]]) -> list[dict[s
151163
})
152164
return output
153165
except Exception as exc:
154-
return [failed_check(check_id, f"judge output could not be parsed: {exc}") for check_id in expected_ids]
166+
return [fallback(check_id, f"judge output could not be parsed: {exc}") for check_id in expected_ids]
155167

156168

157169
def evaluate_case(case: dict[str, Any], trajectory: dict[str, Any], api: APIModel, batch_size: int, style: str, max_chars: int) -> dict[str, Any]:
@@ -165,9 +177,16 @@ def evaluate_case(case: dict[str, Any], trajectory: dict[str, Any], api: APIMode
165177
raw = api.generate_chat([{"role": "user", "content": prompt}], max_tokens=1024, temperature=0.0)
166178
except Exception as exc:
167179
raw = None
168-
parsed = [failed_check(str(item["check_id"]), f"judge request failed: {exc}") for item in batch]
180+
parsed = [
181+
fallback_check(
182+
str(item["check_id"]),
183+
f"judge request failed: {exc}",
184+
f"{case['instance_id']}::{item['check_id']}",
185+
)
186+
for item in batch
187+
]
169188
else:
170-
parsed = parse_checks(raw, batch)
189+
parsed = parse_checks(raw, batch, fallback_prefix=str(case["instance_id"]))
171190
evaluated.extend(parsed)
172191

173192
by_category: dict[str, dict[str, Any]] = {}

‎code/run_judges/deepresearch_runner.py‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
sys.path.insert(0, str(ROOT))
1616

1717
from prompting import deepresearch_batch_prompt, deepresearch_single_prompt
18-
from runtime import APIModel
18+
from runtime import APIModel, fallback_binary_label
1919

2020

2121
def read_json(path: str | Path) -> Any:
@@ -115,10 +115,18 @@ def evaluate_item(
115115
index = start + offset
116116
candidate = by_index.get(index)
117117
try:
118-
covered = normalize_answer(candidate.get("covered")) if candidate else False
119-
except (AttributeError, ValueError):
120-
covered = False
121-
justification = str(candidate.get("justification", "")) if candidate else parse_error or "missing point_index"
118+
covered = normalize_answer(candidate.get("covered")) if candidate else None
119+
except (AttributeError, ValueError) as exc:
120+
covered = None
121+
parse_error = parse_error or f"invalid coverage value: {exc}"
122+
if covered is None:
123+
covered = fallback_binary_label(f"{item['id']}::{index}")
124+
justification = (
125+
f"{parse_error or 'missing point_index'}; assigned uniformly random "
126+
f"{'positive' if covered else 'negative'} label"
127+
)
128+
else:
129+
justification = str(candidate.get("justification", ""))
122130
weight = point.get("weight", 1)
123131
outputs.append({"point": point["point"], "weight": weight, "covered": covered, "justification": justification})
124132
total_weight = sum(point.get("weight", 1) for point in points)

‎code/run_judges/runtime.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import hashlib
1111
import json
1212
import os
13+
import secrets
1314
import threading
1415
import time
1516
from pathlib import Path
@@ -48,6 +49,15 @@ def _env_float(name: str, default: float) -> float:
4849
return float(raw)
4950

5051

52+
def fallback_binary_label(key: str) -> bool:
53+
"""Return the protocol's random binary label for an unparsable judgment."""
54+
seed = os.getenv("VERIBENCH_FALLBACK_SEED", "").strip()
55+
if seed:
56+
digest = hashlib.sha256(f"{seed}\0{key}".encode("utf-8")).digest()
57+
return bool(digest[0] & 1)
58+
return bool(secrets.randbits(1))
59+
60+
5161
class JsonCache:
5262
"""Process-safe-enough JSON cache for deterministic judge calls."""
5363

‎tests/test_judge_runners.py‎

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
import json
33
import sys
44
import unittest
5+
from unittest.mock import patch
56
from pathlib import Path
67

78

89
ROOT = Path(__file__).resolve().parents[1]
910
sys.path.insert(0, str(ROOT / "code" / "run_judges"))
1011
sys.path.insert(0, str(ROOT / "code" / "main_leaderboard"))
1112

13+
import agenticcoding_runner
14+
import deepresearch_runner
1215
from agenticcoding_runner import evaluate_case, parse_checks
1316
from compute_main_leaderboard import iter_ac_checks
1417
from deepresearch_runner import evaluate_item
@@ -50,6 +53,16 @@ def save_cache(self):
5053
pass
5154

5255

56+
class UnparseableJudge:
57+
model_name = "unparseable-judge"
58+
59+
def generate_chat(self, messages, **kwargs):
60+
return "not a valid judgment"
61+
62+
def save_cache(self):
63+
pass
64+
65+
5366
class RunnerTests(unittest.TestCase):
5467
def test_run_steps_packaged_result_paths_exist(self):
5568
run_steps = (ROOT / "RUN_STEPS.md").read_text(encoding="utf-8")
@@ -126,9 +139,32 @@ def test_agenticcoding_output_keeps_category_schema(self):
126139
self.assertEqual(result["Rules"]["checks"][0]["check_id"], "r1")
127140
self.assertEqual(len(iter_ac_checks({"results": [result]})), 1)
128141

129-
def test_agenticcoding_missing_check_is_fail(self):
142+
def test_unparsable_labels_use_random_fallback(self):
143+
item = {"id": "DRB2_1", "question": "Q", "response": "A"}
144+
rubrics = {"rubric": [{"point": "first"}, {"point": "second"}]}
145+
with patch.object(deepresearch_runner, "fallback_binary_label", return_value=True):
146+
result = evaluate_item(
147+
item,
148+
rubrics,
149+
UnparseableJudge(),
150+
batch_size=2,
151+
style="standard",
152+
model_name="fixture",
153+
)
154+
coverage = result["result"]["coverage_results"]
155+
self.assertEqual([row["covered"] for row in coverage], [True, True])
156+
self.assertTrue(all("uniformly random positive label" in row["justification"] for row in coverage))
157+
158+
expected = [{"check_id": "r1"}, {"check_id": "r2"}]
159+
with patch.object(agenticcoding_runner, "fallback_binary_label", return_value=True):
160+
parsed = parse_checks("not valid JSON", expected, fallback_prefix="case-1")
161+
self.assertEqual([item["result"] for item in parsed], ["success", "success"])
162+
self.assertTrue(all("uniformly random success label" in item["reasoning"] for item in parsed))
163+
164+
def test_agenticcoding_missing_check_can_be_negative_fallback(self):
130165
expected = [{"check_id": "r1"}, {"check_id": "r2"}]
131-
parsed = parse_checks('[{"check_id": "r1", "result": "success"}]', expected)
166+
with patch.object(agenticcoding_runner, "fallback_binary_label", return_value=False):
167+
parsed = parse_checks('[{"check_id": "r1", "result": "success"}]', expected)
132168
self.assertEqual([item["result"] for item in parsed], ["success", "fail"])
133169
self.assertEqual(parsed[1]["check_id"], "r2")
134170

0 commit comments

Comments
 (0)