Both reward-0 runs died with 'Termination reason: max_steps' after 3-4
exchanges -- the runner passes its generic default (8) down, far too
few for greet->verify->find->policy->act->confirm. Official tau2 runs
use 40+; floor, don't cap.
Co-Authored-By: Claude <noreply@anthropic.com>
loguru restricted to WARNING+ (every orchestrator step dumped full
messages -- thousands of lines per bench). AssistantMessage.is_final_
chunk now False on tool-call turns (official adapter semantics: True
means the agent is FINISHED talking); always-True handed the turn
back to the user prematurely mid-action-sequence.
Co-Authored-By: Claude <noreply@anthropic.com>
The user simulator (GLM via our adapter) inlines its scenario reasoning
in content as '...instructions...</think>reply' -- passed through
unstripped, the AGENT receives the scenario's secret instructions
(task goal, disclosure strategy), inflating rewards. Both channels now
trimmed at the last </think>.
Co-Authored-By: Claude <noreply@anthropic.com>
Same UX as the docker image chain: try local sources first, then the
GitHub URL, and on total failure print the exact manual commands --
with a warning that PyPI's 'tau2' is an unrelated physics package.
Co-Authored-By: Claude <noreply@anthropic.com>
mrcr's summary row displayed 'extraction_failure_rate 0.0%' as its
score because dict insertion order put the diagnostic first. Both
primary-metric picks (row build and repeats mean) now skip it.
Co-Authored-By: Claude <noreply@anthropic.com>
- config env entries: bfcl_v3->bfcl_mock, tau2_bench->tau2_official;
the eval loop previously POPPED the env key and discarded it -- now
it feeds args.env (CLI --env still works as default/fallback)
- swe_bench_verified images: per-instance names are never published;
the official docker.io layout is repo-level BASE images
(swebench/sweb.eval.x86_64.{repo}) with per-instance images built
on top -- naming corrected (note: the swebench/* namespace is
currently blocked on every reachable CN mirror all the same)
Co-Authored-By: Claude <noreply@anthropic.com>
'/data/hf_models/GLM-5.3-NVFP4' contains slashes, so the bare-name
heuristic ('/' not in judge) misclassified it as a full spec and passed
it through un-prefixed -- resolve_adapter then blew up. With
--judge-api-url given, combination is now unconditional (mirrors the
main model flags); bare names without a url fail fast; full specs
without a url pass through.
Co-Authored-By: Claude <noreply@anthropic.com>
A bare --judge-model without --judge-api-url produced a malformed spec
that exploded deep inside run_eval -- and it did so for longbench_v2,
which does not even use a judge. Now the CLI rejects the combination
up front, and run_eval constructs the judge adapter only when the
recipe's scorers actually include llm_judge.
Co-Authored-By: Claude <noreply@anthropic.com>
The mirror's CDN assigns 6KB/s or 4.8MB/s to the SAME file depending
on which edge a connection lands on -- a single-connection download
is one dice roll that can stall for the whole file. aria2c (-x8 -s8)
splits the file so each segment rolls independently, and
--lowest-speed-limit=50K re-opens stalled segments. Falls back to the
urllib path when aria2c is absent. Live test: the mrcr file that sat
at 2.1MB/190MB for 6 minutes came down in 43s (4.4MB/s).
Co-Authored-By: Claude <noreply@anthropic.com>
Hybrid-thinking gateways inline the reasoning channel into content
wrapped in <think>..</think>; extractors then produced answers like
'3</think>Let me analyze...' (drop em 0.0 with f1 0.5 on samples the
model actually answered). Blocks and stray closers removed pre-extract.
Co-Authored-By: Claude <noreply@anthropic.com>
The entrypoint override added only DockerSandbox the parameter; the
execution scorer now always passes it, so every local-sandbox bench
(live_code_bench) died with 'got an unexpected keyword argument' and
scored 0. Accepted (and ignored) in local/base for parity.
Co-Authored-By: Claude <noreply@anthropic.com>
The tree API intermittently returns truncated listings (9 parquets
listed as [.gitattributes, README.md] at night); previously that failed
the bench even though 5 of the files were already in the shared blob
store. Empty selection now uses matching cached blobs with a notice;
only a truly cold cache still raises.
Co-Authored-By: Claude <noreply@anthropic.com>
Even a real 9GB/74s pull printed only 'Pulling from' + 'Status' -- the
CLI suppresses per-layer Downloading lines for non-TTY stdout since
docker 25, so the CR-splitting/summary pipeline had nothing to parse.
--progress=plain forces them out.
Co-Authored-By: Claude <noreply@anthropic.com>
- watchdog: 10s was the user's experiment; back to 5min default
(EVALHARNESS_PULL_IDLE_S overrides). Zero-byte detection itself is
sound: select(fd, timeout) + any-chunk reset, unit-verified.
- a FAILED bench left its 'scoring 0/2' bar on screen through the
NEXT bench's entire dataset download; begin_bench() relabels to
'<name> · loading' and zeroes counters at each bench start.
Co-Authored-By: Claude <noreply@anthropic.com>
The containment check ('{img}' in template) matched ALL entries, so
every announce said 'via docker.io(daemon mirrors)' while the chain
silently rotated real mirrors. The daemon-default entry is exactly
'{img}' -- compare equality.
Co-Authored-By: Claude <noreply@anthropic.com>
16 -> 6 (daemon default, daocloud, 1ms.run, 1panel, rat.dev, ustc):
the long tail never delivered anyway. All-6 failure now prints an
explicit self-load recipe (save/load via an egress machine, or pull
when the network recovers) instead of a terse RuntimeError.
Co-Authored-By: Claude <noreply@anthropic.com>
16 total. The intl entries need real egress (usually blocked on this
host) but cost only 10s each with the idle watchdog on networks that
have them. Also noted: a 10s watchdog can kill sources mid slow
negotiation (rat.dev emitted 'Pulling fs layer' then went quiet) --
EVALHARNESS_PULL_IDLE_S=30 softens that.
Co-Authored-By: Claude <noreply@anthropic.com>
Was 6 explicit sources (the daemon's first hop hides 12 more of its
own); added 1panel.live, dockerproxy, 163, baidubce, ustc, sjtug, nju,
tuna. With the 10s idle watchdog the full sweep costs ~2min worst
case. EVALHARNESS_DOCKER_MIRRORS replaces the whole list.
Co-Authored-By: Claude <noreply@anthropic.com>
Any docker output resets the timer, so this only fires on totally
silent (dead) mirrors; EVALHARNESS_PULL_IDLE_S overrides.
Co-Authored-By: Claude <noreply@anthropic.com>
A dead mirror emitted zero bytes and the fixed 3600s attempt timeout
let it stall the whole chain for an hour (third occurrence tonight:
15m49s frozen at 0 bytes). select() with a 300s idle timeout now kills
the attempt and moves to the next mirror; a trickling source (1 line/s
Downloading updates) resets the timer and stays alive.
Co-Authored-By: Claude <noreply@anthropic.com>
docker refreshes Downloading lines with \r, not \n -- readline()
parked them in the buffer, so the byte snapshots never surfaced and
the overall-progress dict stayed empty (the ⏳ summary never printed).
Chunked reads now split on both terminators; verified with a synthetic
\r-stream that the parser yields the refresh lines and computes the
52.2/65.1MB-style summary.
Co-Authored-By: Claude <noreply@anthropic.com>
Per-layer events gave no sense of how much is LEFT. Downloading lines
are parsed per-layer and summed; every 3s the snapshot becomes
'⏳ 3.14/9.27 GB (34%) · 最大层 3f4a2b1c9d0e: 0.51/1.2 GB' instead of
a bare layer line. Key events (Pulling from / Status / errors) still
print immediately.
Co-ached-By: Claude <noreply@anthropic.com>
With --entrypoint python3, our 'python /work/main.py' runner became
interpreter args: python3 tried to open a FILE named 'python'
("can't open file '/app/python'"). When an entrypoint is set it IS
the interpreter -- pass just /work/<entry>.
Co-Authored-By: Claude <noreply@anthropic.com>
Cold pulls now tick a 'Downloading xMB/yGB' snapshot at most every 3s
(was 10s) -- keyword hits (Pull complete / Status / errors) still
print immediately.
Co-Authored-By: Claude <noreply@anthropic.com>
The official image's ENTRYPOINT is 'python3 -m bigcodebench.evaluate'
-- our 'python /work/main.py' runner was swallowed as CLI args and the
official evaluator died on 'No samples provided'. execution scorer now
accepts entrypoint= and docker exec passes --entrypoint; the BCB recipe
sets entrypoint python3. Also swept 7 leaked eh-exec containers (the
--rm path never fires when our timeout kills the CLI first).
Co-Authored-By: Claude <noreply@anthropic.com>
The scoring phase only reset the bar on the first judged sample; until
then the stale GENERATION state (100%, +N new) stayed on screen --
through multi-minute image-pull preflights it read as 'already done'
while nothing was scored yet.
Co-Authored-By: Claude <noreply@anthropic.com>
Two pull UX/correctness fixes: (1) a multi-GB pull with captured
output is minutes of silence reading as a hang -- layer progress now
streams (throttled) to stderr; (2) the mirror-tag -> canonical retag
could fail silently and the following rmi then deleted the ONLY tag,
losing a 342s pull and forcing a full re-download -- retag is now
verified and the mirror tag kept on failure.
Co-Authored-By: Claude <noreply@anthropic.com>
A multi-GB docker pull ran with fully captured output -- minutes of
silence that read as a hang (user interrupted a healthy run over it).
Each mirror attempt now prints which source it is trying and how long
a hit took.
Co-Authored-By: Claude <noreply@anthropic.com>
One flag for the whole ladder: off == --disable-thinking,
low..max map to reasoning_effort, full = plain default. Takes
precedence over the two older flags. Run Plan shows the active mode
('disabled' / 'enabled · effort=low' / 'enabled').
Co-Authored-By: Claude <noreply@anthropic.com>
The adapter dropped reasoning_effort/thinking from the payload, so the
middle rung of the ladder (es reference: full 98.3 / effort_low 94 /
no-think 82.3 on humaneval) was unreachable. Both keys now pass
through; --reasoning-effort {minimal,low,medium,high,max} overrides
the YAML, and config/effort_low.yaml mirrors default.yaml with
reasoning_effort: low for one-command low-thinking runs.
Probe on the endpoint: same question, default = 319 chars reasoning /
262 tok, low = 47 chars / 117 tok -- the server honors it.
Co-Authored-By: Claude <noreply@anthropic.com>
The preflight did a bare 'docker pull' -- docker.io is slow/unreachable
from CN without luck; it now walks the same fallback chain as the SWE
prefetch (daemon mirrors -> daocloud -> 1ms.run -> baidubce -> sjtu ->
rat.dev), retagging the hit to the canonical name.
Co-Authored-By: Claude <noreply@anthropic.com>
1140 real predictions scored 0.0% because the recipe referenced
'bigcodebench-sandbox:latest' -- a name nothing builds and docker.io
does not have; every sample then burned 3 pull-retries (~200s each).
- image -> bigcodebench/bigcodebench-evaluate:latest (the official hub
image, same one evalscope uses)
- ensure_image() preflight in evaluate(): recipe-level AND sample-level
images are verified/pulled ONCE before any container runs; missing ->
seconds-fast bench failure with a fix hint instead of a silent 0.0%
- docker exec: 'Unable to find image'/'manifest unknown' class errors
are permanent -- no 3x retry amplification
Verified with a bogus image: preflight raises in one pull-attempt with
the fix hint.
Co-Authored-By: Claude <noreply@anthropic.com>
Five categories (Code & Engineering / Reasoning & Math / Knowledge &
Language / Long Context / Agents & Tools) now annotate the console
summary table (new column), summary.csv (new field), and each report's
run_info.category.
Co-Authored-By: Claude <noreply@anthropic.com>
Tokens are cumulative (they include restored predictions' usage) while
time was this-run wall -- fully replayed benches showed 0s in the same
column, reading as broken. run_info now carries gen_fresh; a bench with
zero fresh generations renders its time cell as 'cached' (tokens keep
showing the true cumulative investment).
Co-Authored-By: Claude <noreply@anthropic.com>
115m39s next to eta 2h51m01s read inconsistently; all durations now
render as 45s / 7m15s / 1h55m39s / 2h51m01s / 3d02h00m.
Co-Authored-By: Claude <noreply@anthropic.com>
One sample whose stream never got a first byte (prefill queue at high
gate levels) exhausted 6 adapter retries and killed the WHOLE gather --
500 samples died with it. Terminal failures are now contained: empty
prediction (scores wrong, es-parity for timeouts), NOT checkpointed so
a rerun retries them, prominently counted; only a 100% wipeout fails
the bench. Stream-aggregate read timeout 60s -> 240s: a 128k prompt
queued behind other prefills legitimately takes minutes to start
answering; real hangs are now the gate's job (x0.7) and contained
failures rather than bench death.
Verified: 1-in-3 terminal failures -> bench completes 6 ok / 3 empty,
checkpoint holds only the 6.
Co-Authored-By: Claude <noreply@anthropic.com>
User-directed changes:
- steady no longer pins the converged level: the endpoint is shared,
other tenants move its capacity mid-run, so steady keeps judging
forever (+1 when rate beats reference by 5%, -1 when 15% below,
reference drifts by EWMA). Large drops are still handled by the
failure channel's multiplicative x0.7; the +-1 path tracks drift.
- dwell fallback scales with the OBSERVED completion cadence:
max(120s, 3x inter-completion gap EMA). A 25s timer judged 60s-per-
request benches on one lone sample.
- MIN_OK floor raised to 5 (evidence = max(5, 2x level)).
Simulated capacity drift 8->3->8: gate follows down then recovers.
Co-Authored-By: Claude <noreply@anthropic.com>
Demanding a 10% gain to keep doubling settled [1,2]->1 on the first
noisy plateau (lbv2: 4k..2M-token docs, completion-rate noise dwarfs
10%). Now: keep climbing while not clearly worse (>=0.9x); bisect only
on clear degradation; samples per level doubled (max(3, 2*level)) to
shrink noise; steady re-probes +1 after ~60s so a noise-induced settle
cannot pin the gate forever. Overshoot past the true knee is trimmed
by the failure channel (timeouts -> x0.7), which is the real ceiling
finder on a prefill-bound endpoint.
Noise-swept at +-25%: capacities 4/8/16 settle at 11/31/16 without a
failure model; production failures pull the overshoot back down.
Co-Authored-By: Claude <noreply@anthropic.com>
95 waiters all tokenizing 2M-token docs through the 32-thread default
executor saturated the GIL: the rich render thread and the event loop
starved, so the bar froze and jumped (and the gate probe went blind).
Truncation now runs on a dedicated 8-thread pool; the remaining
workers queue and the loop/renderer stay responsive.
eta formats as 45s / 7m15s / 2h35m40s / 6d03h12m as it grows.
Co-Authored-By: Claude <noreply@anthropic.com>
The 5s probe fetched /metrics via asyncio.to_thread, which shares the
DEFAULT executor with second-long truncation tokenizations -- 96 of
those queue-jumped the probe and the state machine never ticked (gate
frozen at 1 while results flowed, ETA 6h). Probes now run on a
dedicated single-thread executor, and after 3 consecutive fetch
failures the gate stops asking for /metrics entirely (this endpoint
404s; pure demand mode from then on).
Verified under a choked default executor: gate ticks 1->2 on schedule
and metrics_dead engages after 3 failures.
Co-Authored-By: Claude <noreply@anthropic.com>
The slash form read as a fraction and kept inviting 'why is the
denominator growing' -- it is the pipeline depth (tokenizing +
gate-queued + generating), bounded by the global semaphore, while the
gate-admitted count is the real server load.
Co-Authored-By: Claude <noreply@anthropic.com>
96 worker threads racing transformers 5.x lazy imports on the FIRST
_get_tokenizer call raised ImportError and degraded that whole first
batch to the char approximation (the old single-threaded path never
raced). First load now holds a threading.Lock; the transformers
'>model_max_length' logging is silenced inside truncation (counting a
2M-token doc before trimming it is the point), and the per-sample
degradation print becomes a one-shot warning.
Co-Authored-By: Claude <noreply@anthropic.com>
The counter was taken just past the GLOBAL semaphore (lifted to 96 in
auto mode so the gate is the sole limiter) but BEFORE the pool gate --
so 94 gate-queued workers counted as in-flight. The gate now pushes
its actually-admitted count and the bar shows 'admitted/held'
(e.g. in-flight 2/96 = 2 really hitting the server, 94 queued on the
gate). Verified: admitted never exceeds the gate limit.
Co-Authored-By: Claude <noreply@anthropic.com>
Replaces the +1/5s linear ramp: start at 1, double while measured
completions/s keeps improving (>10% over the previous level); the
first plateau opens a bisect [last_good, bad] that narrows to the
knee, then holds steady. Failures still cut x0.7 instantly and
restart probing from the shrunken level; zero completions = hold.
Judging a level needs max(MIN_OK, level) completions -- a
2-completion rate estimate at level 8 is quantization noise (caught
by simulation converging to 1 on a capacity-8 endpoint).
Simulated against throughput curves min(level, capacity):
capacity 8 -> 1,2,4,8,16 | bisect 12,10,9 -> steady 8
capacity 16 -> 1,2,4,8,16,32 | bisect ... -> steady 16
capacity 4 -> 1,2,4,8 | bisect 6,5 -> steady 4
Co-Authored-By: Claude <noreply@anthropic.com>
Ramping on demand alone was dangerous with slow endpoints: if the 2
in-flight longbench_v2 requests hang, 'no failures + waiters queued'
kept adding +1 every 5s all the way to 96 -- piling prefills onto a
server that had not answered anything. Both ramp paths (demand-driven
and /metrics) now require at least one SUCCESSFUL completion in the
probe interval; hangs hold the gate until read-timeouts fire and the
x0.7 backoff takes over.
Unit-verified: hang 4 probe intervals with 20 waiters -> limit stays 2;
one success -> +1; one failure -> x0.7.
Co-Authored-By: Claude <noreply@anthropic.com>
assemble() runs the max_input_tokens truncation tokenizer inline in the
coroutine; longbench_v2's 2M-token docs take seconds of CPU per encode,
and each one BLOCKED the whole loop -- heartbeat frozen, gate probes
dead, zero HTTP while the process sat at 100% single-core. encode now
runs in a worker thread (loop stays live, encodes parallelize).
Co-Authored-By: Claude <noreply@anthropic.com>