Skip to content

PR #801 segmentation results DO NOT MERGE - #824

Closed
ttlequals0 wants to merge 23 commits into
mainfrom
feature/prompt-variant-benchmark
Closed

ttlequals0 wants to merge 23 commits into
mainfrom
feature/prompt-variant-benchmark

Conversation

@ttlequals0

@ttlequals0 ttlequals0 commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Why

PR #801 proposes a much shorter ad-detection system prompt: classify every transcript segment and keep the promotional ones. Its numbers compared the new prompt in segment-id mode against the old sweep in timestamp mode, and the harness could not hold two prompts side by side. This PR adds a prompt-variant dimension to the benchmark, imports the #801 rows, and runs 17 models over the full 15-episode corpus under both prompts so everything is in one place.

Reports in this PR:

results/report.md stays at the last full campaign. The detection runs from this comparison are saved as the prompt-testing report.

What changed

  • --prompt-variant {detection,segmentation} on run, report, show-prompt and dump-prompt. detection is the live prompt and stays byte-identical, including every existing prompt_hash. segmentation is the Experiment: Prompt optimization #801 prompt frozen at prompts/segmentation-v1.txt, with its own user-prompt builder and a parser that keeps sponsor, cross_promo, self_promo and interaction segments.
  • Per-cell report files, a benchmark compare command (paired F0.5 deltas and p-values), --model / --episode / --retry-errors run filters, and max_tokens recorded on every call.
  • Runs pause on account rate limits (HTTP 429 with a reset time) instead of writing error rows, capped by [run] max_rate_limit_pause_seconds. A paused call gives up its concurrency slot, so other providers keep running.
  • scripts/import_calls.py and the 1880 imported Experiment: Prompt optimization #801 rows in a co-authored commit. The episode_results.jsonl rewrite in that commit is our schema change, not the contributor's.
  • Call records now live in per-model shards under results/raw/calls/ (schema v3). The flat calls.jsonl crossed GitHub's 100 MB blob limit with these rows. benchmark migrate-calls converts an existing checkout.
  • Full-corpus runs: 15 episodes (13 with ads, 2 verified ad-free), 5 trials, both prompts, 17 models, no error rows left in either cell.

Findings

Paired on the 13 ad-bearing episodes. Delta is segmentation minus current.

Model Current F0.5 Segmentation F0.5 Delta p Cuts in the 2 ad-free episodes (current / segmentation)
claude-haiku-4-5-20251001 0.882 0.787 -0.096 0.008 0 / 1
google/gemini-3.5-flash-lite 0.868 0.789 -0.080 0.11 0 / 3
google/gemini-3.5-flash 0.843 0.835 -0.007 0.91 0 / 5
x-ai/grok-4.3 0.842 0.790 -0.052 0.46 0 / 0
claude-sonnet-5 0.824 0.754 -0.070 0.24 0 / 2
claude-opus-5-5 0.823 0.789 -0.033 0.66 0 / 1
claude-opus-4-8 0.816 0.788 -0.028 0.50 0 / 4
google/gemini-3.1-flash-lite 0.790 0.776 -0.014 0.77 7 / 5
deepseek/deepseek-v4-flash 0.779 0.732 -0.048 0.39 0 / 0
qwen/qwen3.7-flash 0.772 0.790 +0.018 0.74 0 / 4
claude-opus-5 0.771 0.822 +0.051 0.29 0 / 3
openai/gpt-oss-120b 0.685 0.691 +0.006 0.91 6 / 8
qwen/qwen3.8-flash 0.672 0.759 +0.086 0.24 2 / 5
qwen/qwen3.5-plus-02-15 0.389 0.827 +0.438 0.004 0 / 6
microsoft/phi-4 0.291 0.658 +0.367 0.000 0 / 0
bytedance-seed/seed-2-1-turbo 0.080 0.553 +0.473 0.000 2 / 0
mistralai/mistral-small-2603 0.053 0.741 +0.688 0.000 0 / 0

How to read p: the chance of a gap at least this large if the prompt made no difference and the spread were episode-to-episode noise. It comes from a paired t-test over the per-episode F0.5 differences, the same test the report uses for its tiers. Below 0.05 counts as real.

  • The current top tier does not improve. Haiku 4.5, the production default, loses 0.096 F0.5 and that loss is significant at 13 episodes (p 0.008). Every other top-tier model is flat or slightly down within noise.
  • Recall rises on most models (opus 5 goes from 0.87 to 0.94) but not on haiku or sonnet, and precision drops between 1 and 13 points across the top tier. F0.5 weights precision, so the trade is net negative for models that were already precise.
  • Ad-free episodes are the clearest regression. Under the current prompt, 13 of 17 models cut nothing in the two verified ad-free episodes. Under the segmentation prompt, 12 of 17 cut something, including every Claude model. The counts are merged cuts summed over 10 trials (2 episodes x 5), so opus-4-8's 4 means a cut in roughly 1 of every 3 ad-free runs.
  • Cost per episode falls about a third for the Claude models because the system prompt is a third the size.
  • Models at the bottom of the current ranking (mistral-small, seed, phi-4, qwen3.5-plus) go from unusable to mid-table. The gain is real (p below 0.005) but none of them reach the current prompt's top tier.
  • Asking for every segment needs a larger output budget. The segmentation cell ran at 16384 output tokens (phi-4 at 10000, its endpoint has a 16K context). At the production default of 4096, qwen3.5-plus, qwen3.8-flash and seed truncated on nearly every window and returned nothing parseable.

Caveats

  • Detection scores every ad the model returned; segmentation keeps only the four promotional categories, matching the contributor's runner.
  • The contributor's google/gemini-3.1-flash-lite rows ran on an earlier revision of his prompt and were re-run on the frozen one.
  • claude-fable-5-1 and qwen/qwen3-8b are not in the comparison. Fable hit the provider's session rate limit at 8 of 15 episodes. qwen3-8b was deprecated by OpenRouter mid-run; its older detection rows stay in the detection cell report.
  • Stored hashes verify for five of the seven imported models (README has the detail).
  • Harness-only change. No production code or prompt changes.

Verification

uv run pytest -q in benchmarks/llm: 392 passed. Ruff reports no new findings on the touched files. Regenerating the segmentation cell report and the comparison from the committed rows reproduces the committed files apart from the generation timestamp.

@ttlequals0 ttlequals0 changed the title Add prompt-variant benchmark and PR #801 segmentation results (2.98.3) Add prompt-variant benchmark and PR #801 segmentation results Oct 8, 2026
@ttlequals0 ttlequals0 changed the title Add prompt-variant benchmark and PR #801 segmentation results PR #801 segmentation results DO NOT MERGE Oct 8, 2026
ttlequals0 and others added 19 commits October 13, 2026 07:55
cli.run/report/show-prompt/dump-prompt gain --prompt-variant (default
detection), validated the same way as --addressing-mode. Segmentation uses
its frozen prompt and rejects --snapshot. runner._build_user_prompt,
precompute_prompt_hashes, reconstruct_user_prompt, and run() thread
prompt_variant through to variants.format_segmentation_prompt /
parse_segmentation_response; the call record and derive_episode_results
rows carry it alongside addressing_mode, with legacy defaults for old
records. cli.run also gains repeatable --model/--episode filters, applied
before the dry-run preview.
report.render() now filters by prompt_variant (default 'detection',
legacy records untagged) alongside addressing_mode, with matching
title suffix and metadata line. Non-default (variant, mode) cells
write to suffixed report/assets paths via a new report_paths()
helper, wired into cli.run/cli.report; charts in those reports link
to their own assets dir. Adds `benchmark compare`, writing
results/comparison.md with one row per model present in >=2 of the
four cells, reusing aggregate.py's per-model scoring, plus a paired
delta/p-value between segmentation/segment_ids and
detection/timestamps (reusing the TL;DR tier t-test's diff/stat
computation).
Stdlib script that appends another checkout's call records and response shards under a prompt variant, skipping call ids already present.
Add a README subsection for the detection/segmentation prompt variants
and commit the regenerated segmentation+segment_ids report cell.
Critical/important fixes from the prompt-variant-benchmark review:
- Unparseable segmentation responses now report method=None (counted as a
  parse failure) instead of the string "none" (scored 0.5); an all-main-content
  response now scores full compliance for its addressing mode instead of the
  raw extraction method.
- The id-mode int conversion no longer raises on "1.0" or inf/nan values.
- benchmark compare divides cost by the cell's own episode count per model,
  so cells with different episode counts are comparable; it also excludes
  deprecated models, matching the per-cell report.
- Added known-value tests for the paired t-test p-value math.

Minor cleanup: mixed int/float id-mode entries log a warning instead of
dropping silently; ADDRESSING_MODES defined once in variants.py; compressed
over-long docstrings; moved inline test imports to the top of the file;
replaced a try/assert False test pattern with pytest.raises; import_calls.py
JSON decode errors now name the file and line; added CLI tests for
show-prompt/dump-prompt with the segmentation variant; corrected the README's
imported-rows hash-verification caveat with exact counts; added a CHANGELOG
entry for the prompt-variant benchmark feature.
results/raw/calls.jsonl was approaching GitHub's 100 MB blob limit. Call
records now live in results/raw/calls/<model>.jsonl, mirroring the existing
per-model response shards; schema_version bumps to 3 (readers still accept
v2). Adds `benchmark migrate-calls` to fold an existing flat calls.jsonl into
shards, idempotently.
Three rendered strings and several cli.py/runner.py help texts and docstrings
still named the deleted flat calls.jsonl; reworded to the per-model shard
layout, leaving the legacy-file mentions inside migrate-calls/migrate-raw and
the read_calls fallback warning alone since those genuinely describe that
file. Also fixes a latent tie-break: the detection-by-length/position charts
sorted models by detection rate with ties broken by input order, which
silently depended on calls.jsonl's row order; now explicitly tie-broken
alphabetically so it's stable regardless of read order. CHANGELOG notes the
schema v3 shard move. Regenerated report.md, the segmentation/segment_ids
report, and comparison.md so committed output matches the new wording and
chart ordering.
Move every existing row of calls.jsonl into results/raw/calls/<model>.jsonl
so no single data file grows past GitHub's 100 MB blob limit.
Append the 1880 call records and response-shard lines PR #801 ran
under the segmentation prompt variant.

Co-authored-by: John Belcher <[email protected]>
Current-prompt rows for three new models and segmentation-prompt rows for
fourteen models on the eight-episode subset, with the regenerated default
and segmentation cell reports, the comparison, and a fresh pricing snapshot.
The Claude wrapper's five-hour session cap was returning HTTP 429 on
every call until reset, each recorded as an error needing a
--retry-errors pass. Detect that wrapper-specific 429 shape (error.code
== assistant_rate_limit or a resets_at field), pause the whole provider
until resets_at + 15s (or Retry-After, or 60s) via a shared per-provider
resume timestamp, and resend without consuming retries or writing an
error row. A wait beyond the new [run] max_rate_limit_pause_seconds cap
(default 6h) still records an error. OpenRouter-style 429s (no code, no
resets_at) keep the prior backoff/error path. The OpenAI SDK client is
now built with max_retries=0 so our own retry logic sees the raw 429
instead of the SDK silently retrying into the pause window.
_wait_for_pause slept once for a fixed duration, so a second 429 that
extended the pause while a caller slept was ignored and it resent too
early. Loop instead: re-read resume_at after every wake and only return
once it is no longer in the future.

Building the OpenAI client with max_retries=0 also dropped its inner
retries for connection errors, timeouts, and 5xx, not just the 429 retry
it was meant to disable. Restore those inside _call_openai_compatible
via a small helper that retries them up to twice with the SDK's own
backoff shape (0.5s doubling, capped at 8s, jittered) through the
injectable sleep; 429s still go straight to the account-limit/transient
handling without inner retries.
claude-fable-5-1 reached 8 of 15 episodes before the provider's session
rate limit stopped it, and qwen/qwen3-8b was deprecated upstream with
7 of 15 segmentation episodes done. Both incomplete cells are removed;
qwen3-8b's older detection rows stay. The two rate-limited
mistral-small segmentation windows were re-run successfully. All three
reports are regenerated from the remaining rows.
The System One and jev changes landed on main against the flat
results/raw/calls.jsonl store. After rebasing onto them, the combined
report command and the jev cache path still passed calls_path, and the
two native accounting tests wrote and read the flat file. Point all four
at the per-model shards under results/raw/calls/.
@ttlequals0
ttlequals0 force-pushed the feature/prompt-variant-benchmark branch from 83b7e57 to e85677d Compare October 13, 2026 12:01
…eparately

The detection runs added for the prompt-variant comparison had regenerated
results/report.md and its charts. That file is the last full campaign and
the next one is weeks out, so it returns to the main version. The
regenerated detection-cell report is kept as results/report-prompt-testing.md
with its own chart directory, labeled as the PR #801 / #824 prompt test.
- Release a paused provider's concurrency slots: call_with_retry records
  the pause and raises ProviderPausedError; the runner waits outside the
  semaphores and resends.
- Dispatch prompt formatting and response parsing per variant through one
  table instead of string checks at each call site.
- Apply --model/--episode as a work-unit selection inside the runner so
  cfg and episodes keep one scope.
- Move call-store layout knowledge (reset, has-calls, batched dedup
  append) into storage; migrate-calls and import_calls share the append
  and write each shard once.
- Derive report title, run hint and no-data note from one non-default
  cell list; share the paired-diff helper between compare and aggregate.
- Warn about a legacy calls.jsonl once per path, not once per read.
Every budget gets its percentage and the 'of calls' qualifier closes the
list, instead of a first-element special case in the loop.
- import_calls writes response lines before call rows, so a missing
  source shard fails before anything is committed, and validates the
  --variant value against the known prompt variants.
- --model only accepts active model ids; a deprecated id used to pass
  validation and silently produce zero work units.
@ttlequals0 ttlequals0 closed this Oct 25, 2026
@ttlequals0
ttlequals0 deleted the feature/prompt-variant-benchmark branch November 3, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant