glm5.2: streamed prefill fails when the boot model map does not cover routed experts#553
Open
sk8erboi17 wants to merge 1 commit into
Open
glm5.2: streamed prefill fails when the boot model map does not cover routed experts#553sk8erboi17 wants to merge 1 commit into
sk8erboi17 wants to merge 1 commit into
Conversation
… the boot model map
In SSD streaming mode the boot Metal model map can be restricted to a
small span of the GGUF. On a 96 GB machine loading GLM-5.2-UD-Q2_K
(262 GB) the boot log shows:
ds4: SSD streaming initial metal model map restricted to token
embedding (1 spans, 0.94 GiB tensor span)
ds4_gpu_wrap_model_range() only looks up those boot views, so every
streamed-prefill path that addresses routed expert tensors directly in
the mmapped file gets nil back and the whole graph fails:
- the batch-selected-addr overflow expert views:
ds4: Metal streaming prefill batch selected addr failed to map
overflow expert views at layer 3
- the full-layer prefill path:
ds4: Metal model range 3.25..4.23 GiB is not covered by mapped
model views
and every multi-token GLM prompt dies with 'metal GLM prefill failed'
(single-token decode is unaffected).
Instead of failing, fall back to ds4_gpu_wrap_model_exact_range(): a
page-aligned no-copy view over the same mmap, cached in the exact-view
NSCache. Same bytes, same offsets; buffers in flight stay retained by
their command buffers, so cache eviction is safe. Configurations whose
boot views already cover the request never reach the fallback.
Tested on MacBook Pro M2 Max 96 GB (Metal), glm5.2 @ bd89932, with
GLM-5.2-UD-Q2_K_RoutedQ2K.gguf and --ssd-streaming: 614- and
2014-token prompts prefill and generate with
DS4_METAL_GLM_STREAMING_PREFILL_FULL_LAYER=1 (see PR for details and
an open question about the default addr path). ./ds4_test
--metal-kernels passes; the model-free tracks of make test pass.
sk8erboi17
added a commit
to sk8erboi17/DStudio
that referenced
this pull request
Jul 12, 2026
…oud backend One release-sized change spanning engine management, agent telemetry, skill routing and an optional cloud backend. Everything that touches upstream ds4 sources goes through the anchored patch sets as usual - nothing upstream is vendored or edited in place. GLM 5.2 (optional second engine) - POST /api/glm/setup installs antirez/ds4 branch glm5.2 (pinned bd89932) into ./ds4-glm52 - curl+tar, gitignored like ./ds4 - applies patch/ds4-glm52/metal-model-views.patch and builds. Doctor gains a non-fatal 'GLM engine (optional)' row with an Install action. - metal-model-views.patch: in SSD streaming the boot Metal map covers only the token embedding, and ds4_gpu_wrap_model_range() failed every streamed range outside it, killing any multi-token GLM prefill; it now falls back to on-demand cached exact views (same bytes, page aligned). Submitted upstream as antirez/ds4#553. - The model menu gains an Engine-branch section: GET /api/engine/checkouts lists the ds4* checkouts (with git branch), POST /api/engine/checkout swaps the active one at runtime; process-local, never persisted. GLM launches drop --power (rejected below 100), force the working full-layer streaming prefill and pass a 32GB expert-cache budget. Live agent telemetry and streaming tool blocks (jsonl patch v29) - status events stream prefill done/total/tps and decode tps every 300ms; the working label shows real numbers ('prefill 43% - 116 tok/s', 'decode 12.9 tok/s - 183 token') with Time elapsed below in the same font and the active-skill chip beside it. - tool_call_begin / tool_call_param / tool_body_delta mirror the DSML visualizer incrementally: a write/edit block is BORN when the model starts the stanza and its body streams inside as +/- diff lines; the final tool_call still finalizes the block. Verified with Selenium. - transcript folds get per-kind accents (reasoning violet, web search blue, page visits teal); status events are stripped before tokenizing so streamed prose stays continuous; the agent view no longer yanks the scroll back mid-gesture and no stale working footer survives a turn that finished off-conversation. Skill routing - picker option 'Auto - route by prompt': the MODEL decides. A new skills_search(query) agent tool searches the local packs (user, shipped, cybersecurity briefs); the per-turn instruction asks the model to judge, search once and load at most one best match with skill(id). The chip switches from 'skill - auto' to the id actually loaded, captured from the live stream. Ghost selections (packs removed from disk) now show as '<id> - missing'. - the GSA/RSA max-thinking lock no longer blocks the thinking picker outside agent mode. SSD streaming defaults - UI launches were forcing streaming OFF (stored default) while the boot auto-start used auto: same model, 20 tok/s in chat vs 2-3 in agent. Default is now auto everywhere plus a one-time migration of persisted 'off', so the engine's memory-pressure heuristic decides. DeepSeek cloud backend (Chat, Agent, Design, GSA, RSA) - Settings > Connection: 'Model backend' (Local ds4 / DeepSeek API) and an API-key field. Chat calls api.deepseek.com directly (CORS-enabled, format-compatible: ds4 mimics that API). Agent/Design keep every tool local and relay ONLY inference through the launcher, which streams https via curl - key and body in 0600 temp files, never on the argv, never visible to the child. GSA/RSA inherit via the agent; with a remote backend the local model is not even loaded. The model pill lists the API's models and one pick applies everywhere. Also: api_agent_interrupt no longer SIGINTs a loading/idle child ('engine stopped (signal 2)' race on mode switches) and ds4-design maps SIGINT to its graceful SIGTERM cleanup.
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.
Summary
On machines where the model is much larger than RAM, the
glm5.2branch fails every multi-token prefill withmetal GLM prefill failed(HTTP 500 fromds4-server,prompt processing failedfrom the CLI). Single-token decode is unaffected, which makes the failure look intermittent: a "hello" probe works, the first real prompt dies.Root cause: in SSD streaming mode the boot Metal model map is restricted to a small span of the GGUF, but both streamed prefill paths resolve routed-expert tensor ranges through
ds4_gpu_wrap_model_range(), which is a lookup into the boot views only and returnsnilfor anything outside them. This PR makes that lookup fall back to an on-demand cached exact view instead of failing the whole graph.Environment
glm5.2@bd89932GLM-5.2-UD-Q2_K_RoutedQ2K.gguf(262 GB, fromantirez/GLM-5.2-GGUF, fetched with./download_model.sh glm-antirez-q2)--ssd-streaming; reproduced with the auto cache budget and with explicit--ssd-streaming-cache-experts 32GB/44GBReproduction
./download_model.sh glm-antirez-q2 ./ds4 -m gguf/GLM-5.2-UD-Q2_K_RoutedQ2K.gguf --ssd-streaming -c 8192 --nothink \ --prompt-file six-hundred-token-prompt.txt -n 8Boot shows the restricted map and the auto-budget warning:
and the prefill dies on the default path:
With
DS4_METAL_GLM_STREAMING_PREFILL_FULL_LAYER=1the failure just moves to the full-layer path:The same happens through
ds4-server(/v1/chat/completionsreturns 500metal GLM prefill failedfor a 614-token prompt; a 25-token probe and single-token decode work fine).Root cause
ds4_gpu_set_model_map_range()maps only the token-embedding span, sog_model_viewscovers 0.94 GiB of a 244 GiB file.ds4_gpu_wrap_model_range()resolves a range strictly against those boot views and returnsnil(after printing "not covered by mapped model views") when the range lies outside them.d27a11d("streaming: keep prefill logits cache-size invariant via overflow expert views") wrap the whole routed gate/up/down tensors of a layer through exactly this lookup — on this machine class those ranges are never covered, so the overflow path cannot work at any cache budget, which defeats its purpose (the comment inds4_gpu_stream_prefill_batch_selected_addr_enabled()says the addr path "must stay reachable at ANY cache budget").On machines with enough RAM for the boot map to cover the file (or budgets large enough that prefill batches never overflow the expert cache), the lookup always hits and the bug stays latent — which is presumably why the branch works on the machines it was developed on.
The fix
ds4_gpu_wrap_model_range()now falls back tods4_gpu_wrap_model_exact_range()when the requested range is not covered by the boot views, with a one-time stderr note instead of a hard error:newBufferWithBytesNoCopyover the same mmap, so computed logits are unchanged;Validation
On the M2 Max / 96 GB machine, with the patch and
DS4_METAL_GLM_STREAMING_PREFILL_FULL_LAYER=1:614- and 2014-token prompts also complete end-to-end through
ds4-server(/v1/chat/completions). Sustained chat use works (slow, as expected for a 262 GB model streamed on 96 GB — this is inspection-grade, and that's fine).Regression tests on the patched checkout:
./ds4_test --metal-kernels→metal-kernels: OKmake testpass (Q4_K unit tests 4/4,ds4-eval --self-test-extractors,ds4_agent_test); the model-dependent tracks (--long-context, logprob vectors, the GLM 100-case fixture) were not run on this machine — nods4flash.ggufin the test checkout, and I did not have official-logit fixtures. Happy to run more if you tell me which track matters most for this change.Open question (separate bug?)
Even with this fix, the default batch-selected-addr path still fails on this machine: the fallback note prints, ~18 s of work happen, then the request returns
metal GLM prefill failedwith no further stderr — something after the overflow-view mapping returns 0 silently (set_addr_slot/addr_buffersprint nothing, so it is downstream of those). Forcing full-layer prefill (DS4_METAL_GLM_STREAMING_PREFILL_FULL_LAYER=1) is what actually makes GLM usable here, but the auto rule (glm_graph_stream_prefill_full_layer_min_tokens()) leaves common prompt sizes on the broken path.Two things you may want to consider (not included in this PR to keep it minimal):