qwen4_exp QSA assumes the visible set is contiguous — true today, false the moment it is batched #324
Closed
opened 2026-09-07 12:49:11 +00:00 by grenade
·
1 comment
No Branch/Tag Specified
main
feat/397-replicas
feat/385-acestep-memory
fix/376-models-unhealthy-node
feat/music-surprise-genres
feat/web-decisions-route
fix/cold-load-serialise
fix/music-hide-stale-on-compose
feat/audio-disk-guard
feat/music-studio-ux
fix/cortex-glibc-floor
feat/music-history-sidebar
feat/audio-flac-mp3
feat/393-heartmula-serving
feat/391-heartmula-port
feat/387-acestep-rollout
feat/383-acestep-engine
feat/390-heartmula-fixtures
feat/381-acestep-fixtures
feat/402-music-ui
feat/386-audio-rates
feat/382-audio-job-api
feat/356-systemone-playground
fix/bench-skip-decision-models
feat/341-laya-residency
feat/341-laya-rollout
feat/337-laya-perf
feat/338-neuron-systemone
feat/335-laya-prompt-builder
feat/336-laya-engine
feat/339-laya-lang-detect
feat/338-systemone-surface
feat/334-laya-reference-fixtures
feat/96-mtp-probe
fix/324-qsa-visible-set
feat/310-ple-random-advice
feat/neuron-phase-timing
fix/qwen4-ple-conv-launch-bound
feat/318-per-layer-activation-hop
fix/318-release-host-memory
feat/318-per-model-max-in-flight
fix/318-logits-last-position
fix/318-cached-experts-residency
feat/318-host-source-tensors
feat/318-host-resident-experts
fix/322-cache-telemetry
feat/322-isq-cache
test/323-parity-long-prompt
fix/323-layer-type-alias
fix/323-qsa-scores-in-f32
feat/310-mmap-ngram-table
fix/quant-fallback-match-llamacpp
fix/qwen4-exp-expert-row-width
fix/qwen4-exp-load-reporting-and-pool-trim
fix/320-isq-reachable-and-log-levels
feat/qwen4-exp-quantised-experts
feat/qwen4-exp-expert-bank
feat/qwen4-exp-mtp-head
docs/mtp-wiring-settled
feat/qwen4-exp-snapshot
feat/rustingface-always
feat/qwen4-exp-harness
feat/qwen4-exp-qsa-oracle
feat/qwen4-exp-model
feat/qwen4-exp-decoder
feat/qwen4-exp-linear-attn
feat/qwen4-exp-moe
feat/qwen4-exp-full-attn
feat/qwen4-exp-qsa-indexer
feat/qwen4-exp-config
feat/qwen4-exp-qsa-selection
feat/qwen4-exp-ple-consumption
fix/seed-cutlass-anonymously
feat/qwen4-exp-ple-hashing
fix/seed-cutlass-in-cuda-ci
feat/hf-endpoint-env
feat/qwen4-exp-hyper-connections
feat/qwen4-exp-norm-primitives
fix/alias-test-temp-catalogue-collision
chore/retire-vestigial-reasoning-ladder
fix/context-limit-honours-kv-budget
feat/server-advertised-reasoning-ladder
feat/web-chat-collapsible-working
fix/router-sync-restarts-only-on-settings-change
feat/ci-owns-the-router-config
fix/infra-setup-ships-ratelimit-to-gateway
fix/nginx-preflight-checks-every-grant
fix/restore-max-in-flight-8
fix/retire-qwen36-27b
fix/nginx-sync-must-not-block-cortex-deploy
feat/nginx-search-alias-and-sync
fix/wire-surface-in-neuron-spans
fix/295-ignore-tiny-prefill-samples
fix/300-anthropic-streaming-thinking
fix/300-return-the-models-reasoning
feat/operator-repetition-knobs
fix/292-prefix-cache-budget
feat/stamp-preserve-thinking-state
fix/296-one-step-one-assistant-turn
feat/preserve-thinking-ab-gate
fix/client-capability-misattributions
fix/291-max-in-flight-and-config-only-ci
feat/290-reasoning-rungs-from-the-model
fix/284-gdn-state-f32
feat/bench-measurement-regimes
feat/285-nonstreaming-through-engine
fix/288-bench-authenticates
feat/283-operator-sampling-override
fix/286-clippy-all-targets
fix/replay-only-the-latest-reasoning
fix/developer-role-maps-to-system
fix/223-reasoning-effort-off
fix/fetch-the-models-own-prompt-format
fix/278-advertise-the-real-limits
fix/277-responses-reasoning-roundtrip
fix/272-sampling-gaps
fix/clippy-1.98-result-large-err
feat/sampling-defaults
fix/streaming-completion-log
fix/prometheus-histogram-buckets
fix/responses-reasoning-events
fix/stream-reasoning-content
fix/cortex-stream-read-timeout
fix/sse-keepalive-during-prefill
fix/anonymous-yields-to-authenticated
fix/upstream-test-fixture-collisions
fix/docs-directory-index
feat/angels-internal-vhost
v0.1.16
v0.1.15
v0.1.14
v0.1.12
v0.1.11
v0.1.10
v0.1.9
v0.1.8
v0.1.7
v0.1.6
v0.1.5
v0.1.4
v0.1.3
v0.1.2
v0.1.1
v0.1.0
Labels
Clear labels
out-of-scope
p1-now
p2-next
p3-later
Outside helexa's constraining principles: near-frontier models on consumer hardware, served transparently and predictably
Active pain or unblocking work — pick up first
Engine levers — start once P1 baselines exist
Dependent on earlier work, or demand-driven
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: helexa/helexa#324
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Split out of #311 so closing that issue does not bury it. Not a defect
today; a documented assumption that stops holding under a change
someone will make.
The assumption
BlockSelectorderives block extents arithmetically from the blockindex: block
bcovers positionsb * block_size .. (b+1) * block_size,and the tail is
n_blocks * block_size .. n_visible. That is onlycorrect if the set of positions a query may attend is the contiguous
range
0..n_visible.The reference does not assume it. From
transformers/models/qwen4_exp/modeling_qwen4_exp.py:Blocks are formed from whichever positions the mask allows, in order.
For a plain causal mask that is
0..n_visibleand the two agreeexactly — which is why full-model parity passes (#323).
When it stops being true
Any mask with holes:
front;
engine multiplexes sequences of different lengths and joins new ones
mid-stream (#98);
qwen4_expis not batched yet —supports_kv_snapshotis true since25157ca4, but the arch has not been through the engine — so nothingis wrong right now. The failure mode when it is: blocks composed of the
wrong positions, scored against the wrong keys, and a mask that permits
attention to positions the model should not see. It produces plausible
output on the sequence that happens to be contiguous, which is most of
them, and the wrong answer on the ones that are not.
What to do
Either follow the reference and derive blocks from the actual visible
set, or assert contiguity at the boundary so a padded or ragged batch
fails loudly instead of quietly. The second is cheap and is probably the
right first move: it converts an invisible wrong answer into an error
that names its own cause, and it can stand until someone needs the
first.
Worth doing before qwen4_exp is enabled for batched decode, not
after — the whole point is that the symptom does not look like the
cause.
Related: #311 (where this was found), #323 (parity, which passes
precisely because the causal case is contiguous), #98 (the engine whose
ragged prompts would break it).
Fixed in
0086a543, merged as230d243Blocks are now cut from the visible set rather than the position axis, following the reference's
nonzero-then-chunk.Contiguousexists because the set reaches the context window in size and is rebuilt for every query — aVecper query would cost more than the selection it feeds. Both variants go through the same arithmetic, which is the point: blocks are cut at set offsets and mapped back to absolute positions, and those coincide only when the set starts at zero without holes.Two departures from the plan above
I took the first option, not the second. The issue argued the assertion was the right first move. It converts a silent wrong answer into a loud failure, which is real value — but it leaves the model unbatchable, so it defers the work rather than doing it. The selector generalisation is what makes the eventual plumbing a matter of passing
Sparseinstead ofContiguous.The assertion already existed. I added one to
positions_from, then wrote a test to prove it fires — and the test failed, becauseIndexer::advancewas already enforcingtotal == past_len + seq_lenone level up. Mine was unreachable. Deleted it and widened the existing message to name the contiguity assumption it protects, rather than only the cache divergence that is its symptom.Tests
blocks_are_cut_from_the_visible_set_not_the_position_axis— visible[3,5,7,11,13,17,19,23,29], budget 1 block: selection returns[13,17,19,23,29]. Under the old arithmetic it returned4..8plus8, and 4, 6, 8, 9, 10 are positions the mask forbids.contiguous_and_sparse_agree_on_the_same_positions— the two representations must not drift, since only the explicit one is checked against the reference.a_cache_inconsistent_with_past_len_is_refused/..._is_served— drives the guard both ways, because an assertion that cannot be made to fire is not evidence.Mutations verified to fail: index arithmetic in
select, index arithmetic inblock_starts, and removing the guard — which returns[[[0,1,2,3,4,5]]]for an inconsistent cache, the exact plausible-looking wrong answer this issue described.Both parity fixtures still match the reference, confirming the contiguous path is untouched.
Correction to this issue's framing
This was described — including by me, elsewhere — as the blocker for batching. It is not.
model.rsbuilds its own causal mask and no padding mask reaches this arch, so there is currently no source of a non-contiguous visible set at all.The actual obstacle is that
past_lenis a single scalar for the whole batch: ragged sequences need per-sequence offsets, through the indexer, the GDN state and PLE's rolling conv context alike. Filed as #326 rather than left implied inside a closed issue.Closing.