Official Task Variations (1/2): Refactor Prompt Parsing into Reusable Components - #108
Official Task Variations (1/2): Refactor Prompt Parsing into Reusable Components#108kargibora wants to merge 25 commits into
Conversation
The official Arena-Hard prompt has the judge answer the question itself before emitting the verdict, so a small runtime budget truncates every judgment before the label (observed: 3/3 unparseable at 512 tokens). judge.default_max_out_tokens lets the task carry the official 4096-token budget; an explicit judge.max_out_tokens still wins.
A judging protocol's prompt and parser are one unit, so presets now carry the parser callable directly (parse=...) instead of a parser_mode string dispatched inside PairScore. PairScore shrinks to the score-format parser that owns the softmax temperature (ELO's calibration path unchanged), the verdict parser becomes a plain function beside the presets, and judge_and_parse_prefs takes one parse argument instead of parser_mode + score_parser.
judge.parser names a parser from JUDGE_PARSERS for runs using custom prompt files (default: score), replacing the silently hardcoded score parser. Setting it with a preset is rejected — presets bundle their own parser. Run metadata now records the parser under judge_parser.
Every parser is now a small class: __call__ returns the universal preference, parse_values optionally exposes structured values (flat dict, _a/_b suffixes in judged positions, key set owned by the parser), and name feeds the registry and run metadata. Parsed values are collected onto JudgeAnnotation.judge_values and persist in the annotations CSV.
Parsers that declare uses_top_logprobs receive the judge's first-token top logprobs in __call__; judging collects them when the backend is asked via judge.top_logprobs (with a task-level default).
…ndom The official annotator seeds its switch mask with the backward-compat column name 'is_switched_outputs' and presents the baseline first on unswitched rows; the previous seed prefix and model-first orientation produced deterministic but non-official presentation orders.
Both runners resolved the configured prompt but silently parsed with the default score parser; the ELO calibration pass now also judges with the same resolved prompt as the main run.
Clarify judge batching and parser requirements, and pass battle dataframes directly to scorers.
Replace separate summary and metadata callbacks with one score function that returns metrics, breakdowns, and methodology together.
Use a typed scorer registry and defer richer scorer objects until official benchmarks require them.
Remove unused parser side channels, dynamic registration, and task-local prompt plumbing while clarifying prompt preset and parser field names.
| def load_mt_bench_prompt_text(filename: str) -> str: | ||
| return ( | ||
| files(_PROMPT_PACKAGE) | ||
| .joinpath("mt_bench", filename) |
There was a problem hiding this comment.
This is a bit optional but I thought we can move them to a better place, as now judgearena/prompts includes code about the prompt and parsing, not just the template
| logger = get_logger(__name__) | ||
|
|
||
|
|
||
| def _build_judge_batches( |
There was a problem hiding this comment.
Note: This is required to allow different categories/languages to receive different prompt. Personally this will be useful if we want to, for example, use translated prompt for each different language. At the moment, it will be required by the arena-hard as creative_writing judge is different, as well as its user prompt.
| return groups | ||
|
|
||
|
|
||
| def _random_swap_mask(instructions: pd.Series) -> pd.Series: |
There was a problem hiding this comment.
This is deterministic and hence, seed=0. We can make it non-deterministic later
| seed=seed, | ||
| top_k_via_model_kwargs=True, | ||
| ) | ||
| if top_logprobs is not None: |
There was a problem hiding this comment.
This feels very specific implementation to me but I dont know how to achieve this without changing ChatVLLM. we should either let it stay general or impelemnt a custom workflow but it can stay (we may want to output a single score and take a logprob of it to report uncertainity)
This will be used by arena-hard
Problem
Prompt presets currently return a parser-mode string, while the parser code remains fixed inside
evaluate.py. Adding a new judge output format therefore requires changes to both the prompt registry and the shared evaluation path. Custom prompt files also always use the score parser and cannot state which output format they produce.The scorer receives only a preference series. This is enough for a basic win rate, but task scorers may also need fields such as category, model, baseline, or completion text.
The core idea behind this PR is to refactor parsing logic into much reusable component, so models only receive the resolved prompt and the parser required for it. This allow us to implement different strategies like
Before and after
evaluate.pyownsPairScoreand selects behavior through a parser-mode stringjudgearena/prompts/parsing.pyowns named parser implementationsjudge.promptgroups both files and names the parser explicitlydefault_with_explanationpresetNew flow
Implementation
JudgeParserand the parser registry underjudgearena/prompts/parsing.py. The existingPairScorecalculation moves there without changing its formula or default temperature.evaluate.pyfocused on building judge inputs, running inference, and applying the parser supplied by the resolved prompt.judgearena/prompts/templates/without changing their text.Existing tasks keep the same default prompt,
PairScorebehavior, and fixed or both-order judging behavior. The new options are used only when selected by a task or run config.Custom prompt example
Tests
The PR adds focused checks for config defaults and overrides, custom prompt validation, prompt/parser resolution, and parser handoff in ELO and MT-Bench. Existing tests continue to cover the normal pairwise flow. Benchmark-specific verdict and logprob tests stay in the follow-up official-task-variants PR instead of being duplicated here.
Result:
280 passed.Notes
This PR deliberatly edits a lot of file (39) however they are all minimal