Stop discarding a framework's JSON args and its measured accuracy - #1332
Draft
fengshaoyi-amd wants to merge 3 commits into
Draft
Stop discarding a framework's JSON args and its measured accuracy#1332fengshaoyi-amd wants to merge 3 commits into
fengshaoyi-amd wants to merge 3 commits into
Conversation
Three hardcoded, vLLM-shaped assumptions each failed silently on an ATOM
GLM-5.2-MXFP4 run, together ending a 16h session at 6h09m with no accuracy
on record and ~9.8h of budget unspent.
remove_server_args tokenized with a POSIX shlex.split and space-joined
without re-quoting, so a JSON-valued flag lost its inner double quotes.
_repair_unquoted_json is meant to recover that, but its bareword pattern
excludes '*', so atom's --online_quant_config exclude_layer wildcards
(*.mlp.gate, *expert*) could not be repaired and reached the server as
{global_quant_config:ptpc_fp8,...}. Every conc_sweep launch then died on
json.loads. The hop is unconditional: compose_server_args and the
current_best lift both always call strip_benchmark_harness_flags, whose
removal list is non-empty by construction. Removal now tokenizes with the
quote-preserving tokenizer on both sides and only falls back to POSIX for
inputs it declines, which are the ones with no JSON to protect.
SPACE_VALUE_FLAGS is deleted rather than extended. It had no reader left
anywhere in the tree, yet its docstring still promised dedup-time
protection for enrolled flags, so atom's flag looked covered while nothing
guarded it. Value shape, decided by parsing, is the property that matters.
EXPLORE parsed a variant's eval result only when is_high_accuracy_risk
matched a list of vLLM/SGLang flag names and VLLM_*/SGLANG_* env keys. It
matched none of the 12 ATOM variants: atom spells the knob
--kv_cache_dtype, and --online_quant_config was never enrolled at all.
bench-eval-bench runs the eval on every warmup_round regardless, so all 12
scores were already on disk; the predicate only discarded them. Worst
observed drop was 0.0068 against a 0.05 threshold. The gate is now
unconditional and the predicate and both lists are gone.
conc_sweep_failed no longer short-circuits SWEEP to CLOSE ahead of
should_reloop_to_explore. conc_sweep is a closeout concurrency scan, so its
failure carries no evidence about whether the remaining budget could still
find gain. It stays the stop_reason when reloop is blocked, so the honest
outcome survives without the budget being forfeited too.
The quote-preserving tokenizer closed the JSON corruption only for strings
it accepts. `_tokenize_for_removal` still fell back to a POSIX
`shlex.split` on the ones it declines, and that fallback applies to the
WHOLE string: one whitespace-bearing operand anywhere -- say a
`--tool-call-parser 'my parser'` sharing the args with atom's
`--online_quant_config` -- stripped the inner quotes off the JSON blob
beside it and produced the same `{global_quant_config:ptpc_fp8,...}` the
server rejected. `_repair_unquoted_json` cannot re-quote the `*expert*`
wildcards, so it reached the server broken exactly as before.
It also made the two sides of the comparison disagree: args tokenized
POSIX while the removal spec tokenized quote-preserving, so the flag was
not matched and not removed either.
Drop the fallback. A string the tokenizer declines carries a token the
unquoted `EXTRA_*_ARGS` transport cannot represent at all, so it is
already unlaunchable, and splitting it anyway corrupts every JSON blob
sharing it to strip a flag off a string that was going to fail regardless.
Returning it untouched is what every other caller of
`tokenize_server_args_preserving_json` already does -- dedup and the
coordinator helpers return the input, prelude and warm replay raise -- and
it matches this function's own documented "left untouched rather than
guessed" contract. The only cost is an unstripped
`--no-enable-prefix-caching`, which is visible, against a corrupted JSON
value that is not.
Three cases added; all three fail on the parent commit.
The accuracy gate now grades every variant that has a reference, but whether an eval actually ran is resolved from the materialized RUN_EVAL contract -- base config, reference envs, the variant's own extra_envs, the process env -- none of which the explore executor controls. A stale config or a proposal carrying RUN_EVAL=false therefore leaves no score on disk, and the gate fails the variant closed as accuracy_unavailable: a broken eval path reads as "no gains found" rather than as a broken eval path. Force RUN_EVAL=true under exactly the gate's own entry condition, so being graded and being measured cannot come apart. It lands on the round the gate reads -- the warmup under warm-decision, where the decision round stays throughput-only and parse_eval_results falls back to the warmup's score, and the decision round itself otherwise -- and is applied after the variant's envs so a proposal cannot switch off the eval its own KEEP depends on. --no-eval still wins, since it leaves baseline_accuracy at 0 and skips the gate outright. The comment claiming the lifecycle already evaluates every warmup_round unconditionally was wrong and is corrected: sweep, rebench, the baseline measure round and the explore decision rounds all set RUN_EVAL false.
CI E2E report — ❌ Timeout
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What happened
An ATOM GLM-5.2-MXFP4 run (16h budget, TP8/EP1, ISL 8192 / OSL 1024, conc 64, MXFP4 on 8x MI355X) stopped at 6h09m with
stop_reason=conc_sweep_failed, no accuracy anywhere infinal.json, and ~9.8h of budget unspent. Three separate hardcoded, vLLM-shaped assumptions each failed silently on a framework whose flags do not match the vLLM spelling.Every
conc_sweeplaunch died like this:Root cause, narrowed to one hop
remove_server_argstokenized with a POSIXshlex.splitand space-joined without re-quoting, so a JSON-valued flag lost its inner double quotes._reserialize_json_blobson the return path is supposed to recover that via_repair_unquoted_json, but that heuristic's bareword pattern (_JSON_BAREWORD) excludes*, so atom'sexclude_layerwildcards (*.mlp.gate,*expert*) could not be re-quoted,json.loadskept failing, and the damaged blob was retained verbatim.Reproduced as a pure-function chain against unmodified
main:The other three join points the incident report flagged are safe:
merge_server_argsnever splits, anddedup_vllm_server_args/_shell_safe_dedupeboth go throughtokenize_server_args_preserving_json(posix=False, quotes preserved).The hop is unconditional.
compose_server_argsalways ends instrip_benchmark_harness_flags, whose removal list is non-empty by construction, and_lift_to_current_bestre-strips both sides before merging. So no operatorremove_argsis needed to trigger it.Where it actually bit, per the session artifacts. The
EXPLOREvariant YAMLs carry the value intact —{"global_quant_config":"ptpc_fp8","exclude_layer":[...]}, whichjson.loadsaccepts — and those variants benchmarked fine. The damage appears onceSWEEPliftscurrent_bestthrough the strip, and everyconc_sweepYAML from that point on carries the unquoted form. An earlier revision of this description said all 12 variants were affected; the artifacts do not support that, and the corrected scope is theconc_sweeplaunches.A/B against the two real trees — the incident's own checkout and this branch — confirms the hop and reproduces the rejected string byte for byte:
7b50bee1e)f3884adb9)compact_json_server_argsstrip_benchmark_harness_flags{global_quant_config:ptpc_fp8,exclude_layer:[lm_head,model.embed_tokens,*.mlp.gate,*expert*]}— identical to the string inserver.logcompose_server_argsChanges
1.
remove_server_argspreserves JSON quotes. Both the args and the removal specs now tokenize through_tokenize_for_removal, which uses the quote-preserving tokenizer and declines — returning the input untouched — anything it cannot tokenize without dropping quote bytes.The first revision of this commit kept a POSIX
shlex.splitfallback for declined inputs, justified here as safe because a declined input "cannot hold a JSON value the unquoted transport could deliver anyway". That reasoning was wrong: it is about the declined token, but the fallback applies to the whole string. One whitespace-bearing operand anywhere —--tool-call-parser 'my parser'sharing the args with--online_quant_config— sent the entire string through POSIX and reproduced the incident value byte for byte.4e45fad3fdrops the fallback; the cost is an unstripped flag, which is visible in the args, rather than a corrupted JSON value, which is not.2.
SPACE_VALUE_FLAGSdeleted rather than extended. It had no reader left anywhere in the tree (only a definition, an alias, a_grid_runnerre-export and an__all__entry), yet its docstring still promised dedup-time protection for enrolled flags. That stale promise is why atom's--online_quant_configlooked covered while nothing guarded it. Value shape, decided by parsing, is the property that matters. Note this removes a name whose comment claimed out-of-tree test use; flagging explicitly in case that matters to anyone.3. The accuracy gate is unconditional.
EXPLOREparsed a variant's eval result only whenis_high_accuracy_riskmatched a list of vLLM/SGLang flag names andVLLM_*/SGLANG_*env keys by substring. It matched none of the 12 ATOM variants: atom spells the knob--kv_cache_dtype(underscores, never matching--kv-cache-dtype), usesAITER_*env vars, and--online_quant_config— which changes numeric precision directly, the highest-risk class there is — was never enrolled at all.bench-eval-benchruns the eval on everywarmup_roundregardless, so all 12 scores were already on disk. Confirmed by callingparse_eval_resultson the winning variant's slot after the fact:accuracy=0.9689158453373768,task=gsm8k,metric=exact_match,strict-match. The predicate's only effect was discarding a number already paid for.baseline_accuracy=0.9704321455648218was instate.jsonthe whole time, so keeping that precondition is sufficient.Recovered from disk, GSM8K
exact_match,strict-matchvs baseline 0.9704:Worst drop 0.0068 against
ACCURACY_THRESHOLD = 0.05, so unconditional gating would not have rejected a single variant here. The predicate and both lists are removed.4.
conc_sweep_failedis no longer terminal. The short-circuit sat ahead ofshould_reloop_to_explore(), so one failure ended the whole optimization. conc_sweep is a closeout concurrency scan, not the optimization itself, so its failure carries no evidence about whether the remaining budget could still find gain. It now flows into the normal reloop decision and stays thestop_reasonwhen reloop is blocked, so the honest outcome survives without the budget being forfeited too. Reloop evidence gainssweep_exit_reasonso a downgraded failure is still distinguishable after the fact.Test plan
test_json_server_args_roundtrip.py(19 cases) locks the compose -> dedup -> lift chain, asserting onjson.loadsrather than exact strings so any join point that stops re-quoting fails regardless of normalization shape. The three cases added by4e45fad3fcover the shared-string case above; all three fail onf3884adb9and pass on4e45fad3f.main, the suite is 7 failed / 10 passed. The failures are all the ATOM wildcard cases; the vLLM--compilation-configcases pass on both because their values have no*and the repair heuristic recovers them. That split is exactly why vLLM never hit this and ATOM did.4e45fad3fchecked the same way against its own parent: the server-args / grid-runner / accuracy suites fail on an identical set of 41 pre-existing cases on both commits, zero new and zero incidentally fixed.mainworktree: server-args + accuracy suites 39 vs 39 identical; phase/termination suites 185 vs 185 with zero new; explore suites 45 vs 45. Pre-existing failures are a missingpytest-asyncioin the local venv, unrelated to this change.ruff checkclean,ruff formatapplied.test_sweep_closes_on_failed_conc_sweep_even_when_reloop_available, which locked the old terminal behaviour, and added a case assertingconc_sweep_failedstill surfaces as thestop_reasonwhen reloop is blocked, so the downgrade cannot launder the outcome.python -m atom.entrypoints.openai_server --online_quant_config <composed value>and takes under a minute rather than a 12h run. Script:verify_atom_argparse.sh.EXPLOREwith a baseline accuracy, to show a variant score now lands incurrent_bestinstead of being discarded. This is the only one of the four fixes that still needs GPU wall-clock.conc_sweep_failedreloop) is covered by unit tests only. Now that the JSON corruption is fixed,conc_sweepis not expected to fail on this workload, so there is no natural end-to-end trigger to observe. Called out rather than papered over.Not changed, and why
The incident report also suggested making
SPACE_VALUE_FLAGSvalue-shape-driven (moot, see change 2) and supportingRUN_EVALfrom.env. The latter is by design:RUN_EVALis listed inBLOCKED_EXTERNAL_ENV_NAMESbecause workload/benchmark keys are owned by the CLI flags (--eval/--no-eval), so honouring it from.envwould contradict that ownership. The warning is correct; only its wording could be clearer. The warm-replayREPRODUCEDlabel inconsistency is a separate subsystem and is left for its own PR.