Skip to content

Commit b40bbd2

Browse files
varunursekarclaude
andcommitted
Correct the opencode recipe: anthropic/ now works, openai/ crashes on Claude
The recommended launch was backwards. It advised `openai/claude-sonnet-5`, which crashes: that form forces the Responses API, and litellm's Anthropic->Responses translation emits three id namespaces in one stream (a resp_ id, Anthropic msg_/toolu_ item ids, and a stray chatcmpl- id), so opencode dies resolving a text part under an id it never registered. All six main-model calls returned 200, so the gateway was never at fault. `anthropic/claude-sonnet-5` is now the right form. It previously escaped the gateway -- harbor's adapter injects a baseURL only for the openai provider and opencode ignores ANTHROPIC_BASE_URL -- but vero now supplies that baseURL itself for any non-openai provider. Measured on the conformance example: reward 1.0 over 39 steps, 39 metered `messages` calls, no translation layer involved. Also corrects a security claim that overstated the risk. Escaping the gateway fails closed rather than leaking: the optimizer only ever holds a scoped producer token, so a direct call to a provider's public endpoint returns `401 invalid x-api-key` and the run dies. The upstream credential never leaves the gateway container. The note previously said such a run would hold a credential the optimizer is never supposed to see, which would have discouraged the form that now works, for the wrong reason. Adds two things the same investigation turned up: opencode issues an auxiliary small-model call of the same provider family (claude-haiku-4-5 or gpt-5.4-nano) that 403s against a single-entry allow-list, silently and invisibly outside the gateway log; and it drives the stateful Responses API for openai/ providers, which changes how per_trial_tokens.py's content-matching fallback behaves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 47ac5e7 commit b40bbd2

1 file changed

Lines changed: 48 additions & 17 deletions

File tree

harness-engineering-bench/CONFIGURATION.md

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -97,27 +97,58 @@ benchmark can be checked against the others at a glance.
9797
`ANTHROPIC_BASE_URL`/`ANTHROPIC_API_KEY` at the producer scope, and the model
9898
string it requests matches `--model` as spelled.
9999

100-
**`--agent opencode` requires two flags, and getting it wrong silently defeats
101-
credential isolation.** Harbor's adapter injects a `baseURL` into
102-
`~/.config/opencode/opencode.json` **only when the provider half of
103-
`provider/model` is `openai`** (`agents/installed/opencode.py`); with
104-
`anthropic/claude-sonnet-5` it writes no baseURL, so opencode calls
105-
`api.anthropic.com` directly — unmetered, outside the allow-list, and holding a
106-
credential the optimizer is never supposed to see. It also *requires* the
107-
`provider/` form and raises `ValueError` without it. But it registers the model
108-
under the provider as the bare id, so that is what the gateway receives, while
109-
`${optimizer_model}` would put the prefixed form in the allow-list → 403
110-
`model_denied`. Launch it as:
100+
**`--agent opencode` requires two flags and the provider-native prefix.** It
101+
*requires* the `provider/model` form and raises `ValueError` without it, but
102+
registers the model under that provider as the **bare id**, so the bare id is
103+
what the gateway receives while `${optimizer_model}` would put the prefixed form
104+
in the allow-list → 403 `model_denied`. Launch a Claude model as:
111105

112-
--agent opencode --model openai/claude-sonnet-5 \
106+
--agent opencode --model anthropic/claude-sonnet-5 \
113107
--param optimizer_model=claude-sonnet-5
114108

115109
`--param` wins over the `--model`-derived default (`setdefault` in
116-
`harbor/cli.py`), so the prefixed form reaches opencode while the bare form
117-
reaches the allow-list. **Verify on every new harness** by checking the gateway
118-
request log for `403 model_denied` and confirming the producer scope's request
119-
count is non-zero — a zero producer count with a working optimizer means it
120-
found another way out.
110+
`harbor/cli.py`), so the prefixed form reaches opencode and the bare form
111+
reaches the allow-list. Measured on `vero/examples/harness-conformance`: reward
112+
1.0 over 39 steps, 39 metered `messages` calls.
113+
114+
**Use the provider-native prefix, not `openai/`, for non-OpenAI models.**
115+
Harbor's adapter injects a `baseURL` into `~/.config/opencode/opencode.json`
116+
only when the provider half is `openai` (`agents/installed/opencode.py`), and
117+
opencode ignores `ANTHROPIC_BASE_URL`, so `anthropic/…` used to escape the
118+
gateway entirely. vero now supplies the missing baseURL itself via
119+
`--ak opencode_config=…` for any non-`openai` provider (`harbor/cli.py`), which
120+
the adapter deep-merges last. Two consequences:
121+
122+
- `openai/claude-sonnet-5` **crashes** — do not reach for it as a workaround. It
123+
forces the Responses API, and litellm's Anthropic→Responses translation emits
124+
three id namespaces in one stream (a `resp_` id, Anthropic `msg_`/`toolu_`
125+
item ids, and a stray `chatcmpl-` id); opencode dies resolving a text part
126+
under an id it never registered. All six main-model calls returned `200`, so
127+
the gateway is not at fault.
128+
- Escaping the gateway **fails closed; it does not leak.** The optimizer only
129+
ever holds a scoped producer token, so a direct call to a provider's public
130+
endpoint returns `401 invalid x-api-key` and the run dies. The upstream
131+
credential never leaves the gateway container. An earlier revision of this
132+
note claimed such a run would hold a credential the optimizer should not see;
133+
that was wrong.
134+
135+
**Allow a second producer model.** opencode also issues an auxiliary
136+
summarisation/title call using a small model of the same provider family
137+
(`claude-haiku-4-5` on the anthropic path, `gpt-5.4-nano` on the openai one).
138+
With a single allow-list entry that call `403`s. It is non-fatal but invisible
139+
outside the gateway request log, and the model varies by family — so allow a
140+
second entry rather than adding one more fixed name.
141+
142+
**opencode drives the Responses API for `openai/` providers**, which is stateful
143+
(`previous_response_id`, no prompt resend). That matters for
144+
`scripts/per_trial_tokens.py`, whose content-matching fallback behaves
145+
differently there than on claude-code's `messages` traffic.
146+
147+
**Verify every new harness or model** with `vero/examples/harness-conformance`
148+
(see its `SKILL.md`) before spending a benchmark on it: check the gateway
149+
request log for `403 model_denied`, confirm the producer request count is
150+
non-zero — a zero count with a working optimizer means it found another way out
151+
— and confirm the `endpoint` is the one you intended.
121152
- **Telemetry**: W&B project `harness-engineering-bench` for the whole suite
122153
(group per benchmark, `--param wandb_run=` for the per-launch name) with trace
123154
uploads; inner

0 commit comments

Comments
 (0)