Skip to content

record_baseline.py: no retry/incremental save (one ollama restart cost 10 cells), and context_tokens/keep_alive are pinned in reports but never sent #2644

Description

@Xore

Two defects in analysis/ghidra/benchmarks/corpus/record_baseline.py, both
found while harvesting the #1805-c ghidra-slot round (PR #2641). Filed together
because both are in the same file and both are about the request the harness
actually makes versus the one it claims to make.

1. No retry and no incremental save — one container restart cost 10 cells

ghidra-ollama-1 was recreated at 2026-08-27T20:18:17Z, mid-round. All
three benchmark lanes died within one second of each other:

20:18:16 done A qwen3:14b run2 exit:1                    <- 53 min of work, discarded
20:18:17 done A qwen3:14b run3 exit:1                    <- instant, container still restarting
20:18:17 done B qwen3:14b run1 exit:1                    <- instant
20:18:17 done B qwen3:14b run2 exit:1                    <- instant

identically for qwen2.5-coder:14b-instruct-q4_K_M (lane 2) and
Seneca-Cybersecurity-LLM-x-QwQ-32B (lane 3). The failure is
http.client.RemoteDisconnected out of urlopen(req, timeout=600) at
record_baseline.py:251, with no handler above it.

Two separate costs:

  • No retry. The container was back within seconds — the very next run
    (B run3, started 20:18:17) completed normally an hour later. Three of the
    four failures per lane were runs that fired into a two-second restart window
    and died instantly. A bounded retry with backoff on connection errors would
    have lost nothing at all.
  • No incremental save. The A run2 failure had already scored 11 of 14
    cases
    across 53 minutes. Every one of those answers was thrown away because
    the report is only written at the end. Cases are independent; there is no
    reason a run cannot checkpoint per case and resume.

Net effect: 10 of 72 cells lost, leaving three models at N=1 in a matrix
whose whole purpose was N>=3 with error bars, and directly blocking the
gemma-4-26B-A4B promotion call in #1805-c because the incumbent's Tier B
number is one of them.

Fix: catch connection-class exceptions in ask_model, retry with backoff (the
model is resident and keep_alive is generous, so a retry is cheap), and write
per-case results incrementally so an aborted run resumes instead of restarting.
The lane script's resume guard already keys on the output filename, so a
partial-report format is the missing half of a mechanism that already exists.

Related but distinct: #2032 records the same class of defect (no incremental
save, no HTTP retry) in claims.py. This is the same lesson in a second file —
worth checking whether any other benchmark entry point has it too.

2. context_tokens and keep_alive are recorded but never sent

Every report stamps a qualification_request block that is quoted verbatim as
the round's pins:

{"concurrency": 1, "context_tokens": 8192, "keep_alive": "10m",
 "output_tokens": 512, "seed": 144, "temperature": 0, "thinking": false}

The payload ask_model actually builds carries model, messages,
temperature, max_tokens, seed, stream and (since #1953)
reasoning_effort. There is no num_ctx and no keep_alive anywhere in the
file
grep -n "num_ctx\|keep_alive\|context_tokens" returns nothing but
unrelated analysis_options hits.

On the analysis host the effect is benign and was checked rather than assumed:
the container sets OLLAMA_CONTEXT_LENGTH=32768 and the largest evidence in
the round is 4002 characters (~1000 tokens), so nothing was truncated — the
served context was four times the pin that was published.

It is still a false pin, and it is a hole in #1947 rule 5 (tag + digest +
request pinning), which exists precisely so a round can be reproduced. A report
that states a context length it did not set will silently produce different
numbers on a host with a different OLLAMA_CONTEXT_LENGTH, and nothing in the
artifact would show it.

Fix: send options.num_ctx and keep_alive for real, or drop them from
qualification_request and record the server-side context instead. Sending
them is better — the pin is the point. Either way the recorded block and the
transmitted request must agree, and a test should assert that they do.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingllmLLM analysis workermlML worker and GPU scoring

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions