Skip to content

Commit ced19f9

Browse files
varunursekarclaude
andcommitted
Assert benchmark-config invariants from the configs themselves
These tests moved here from pr2-add-vero, where they could only ever skip. They had been failing for a while against a pinned literal -- evaluation allowed_models == ["gpt-5.4-mini-2026-03-17"] -- that stopped matching when the benchmarks switched to fireworks-served targets. Rewrite the assertions so they cannot drift that way again: the evaluation scope must allow exactly [config.model], derived rather than pinned. That is the real invariant, and it now matches the build-time validator that rejects a model outside its scope's allow-list. Also drop the skip guard, so a missing harness-engineering-bench fails rather than passing vacuously, and extend coverage from three benchmarks to all five. 415 passed, 5 skipped -- the three long-standing failures are gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 7bd0e03 commit ced19f9

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Invariants the checked-in benchmark build YAMLs must satisfy.
2+
3+
These read harness-engineering-bench, so they live on the branch that has it and
4+
carry no skip guard: if the directory is missing the tests fail loudly rather
5+
than passing vacuously, which is how their predecessors drifted unnoticed.
6+
7+
Assertions are derived from each config wherever possible. Pinning literal model
8+
names is what broke the earlier version -- a benchmark switched target model and
9+
the expectation was never updated -- so the rules below say what must hold
10+
between fields, not what today's values happen to be.
11+
"""
12+
13+
from __future__ import annotations
14+
15+
from pathlib import Path
16+
17+
import pytest
18+
19+
from vero.harbor import load_harbor_build_config
20+
21+
BENCHMARK_ROOT = Path(__file__).resolve().parents[2] / "harness-engineering-bench"
22+
23+
BENCHMARKS = ["gaia", "officeqa", "swe-atlas-qna", "tau3", "browsecomp-plus"]
24+
25+
# Names that would let a task reach the upstream provider directly, bypassing
26+
# the gateway's allow-list and budget.
27+
UPSTREAM_CREDENTIALS = {"OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_API_BASE"}
28+
29+
30+
def _config(benchmark: str):
31+
return load_harbor_build_config(
32+
BENCHMARK_ROOT / benchmark / "baseline" / "build.yaml"
33+
)
34+
35+
36+
@pytest.mark.parametrize("benchmark", BENCHMARKS)
37+
def test_benchmarks_route_all_inference_through_the_gateway(benchmark):
38+
"""No benchmark may hand a task the raw upstream credential."""
39+
config = _config(benchmark)
40+
41+
assert config.inference_gateway is not None, "benchmarks must meter inference"
42+
# The upstream credential is the gateway's alone. Declaring it as a task
43+
# secret would deliver it straight to the containers it is meant to bypass.
44+
assert not UPSTREAM_CREDENTIALS.intersection(config.secrets)
45+
assert config.inference_gateway.upstream_api_key_env == "OPENAI_API_KEY"
46+
assert config.inference_gateway.upstream_base_url_env == "OPENAI_BASE_URL"
47+
48+
49+
@pytest.mark.parametrize("benchmark", BENCHMARKS)
50+
def test_target_model_is_the_only_model_the_evaluation_scope_allows(benchmark):
51+
"""The measurement substrate is fixed: one target model, allow-listed.
52+
53+
Derived from config.model rather than pinned, so switching a benchmark's
54+
target model cannot leave this test asserting the old one.
55+
"""
56+
config = _config(benchmark)
57+
evaluation = config.inference_gateway.evaluation
58+
59+
assert config.model is not None
60+
assert evaluation.allowed_models == [config.model]
61+
# Search is budgeted; an unbounded evaluation scope would make the token
62+
# cost of a run unbounded too.
63+
assert evaluation.max_requests is not None
64+
assert evaluation.max_tokens is not None
65+
66+
67+
@pytest.mark.parametrize("benchmark", BENCHMARKS)
68+
def test_optimizer_and_target_models_are_separately_scoped(benchmark):
69+
"""Producer and evaluation are distinct scopes, so neither can spend the other's budget."""
70+
gateway = _config(benchmark).inference_gateway
71+
72+
assert gateway.producer.allowed_models
73+
# Whatever the two are set to, they are independent policies rather than one
74+
# shared pool; the compiler mints a separate token per scope.
75+
assert gateway.producer is not gateway.evaluation
76+
77+
78+
def test_build_params_override_run_time_knobs_without_rebuild():
79+
path = BENCHMARK_ROOT / "gaia" / "baseline" / "build.yaml"
80+
81+
default = load_harbor_build_config(path)
82+
assert default.environment_name == "modal"
83+
default_producer = default.inference_gateway.producer.allowed_models
84+
85+
overridden = load_harbor_build_config(
86+
path, params={"optimizer_model": "gpt-5.5", "inner_env": "docker"}
87+
)
88+
assert overridden.environment_name == "docker"
89+
assert overridden.inference_gateway.producer.allowed_models == ["gpt-5.5"]
90+
assert overridden.inference_gateway.producer.allowed_models != default_producer
91+
# The rest of the measurement substrate is untemplated and stays fixed.
92+
assert overridden.model == default.model
93+
assert overridden.task_source == default.task_source

0 commit comments

Comments
 (0)