Studio: add Mmap/Mlock, draft KV cache dtype, Checkpoints and Cache RAM to Run settings - #9410
Conversation
…AM to Run settings Promote four llama-server flags out of the Extra Arguments text box and into first-class controls in the GGUF Advanced settings section: - Mmap/Mlock (--load-mode), with every documented value - Spec Decoding KV Cache Dtype (--spec-draft-type-k / --spec-draft-type-v) - Checkpoints (--ctx-checkpoints) - Cache RAM (--cache-ram) Blank follows llama.cpp's own default in every case, so nothing is emitted unless a value is set. The Model Memory settings keep final say over the load mode and the row says so when they do.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 972ea32782
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| spec_draft_cache_type: Optional[str] = Field( | ||
| None, | ||
| description = ( | ||
| "Draft KV cache dtype intended for the follow-up load. The draft " | ||
| "context is priced separately from the target's, so omitting it " |
There was a problem hiding this comment.
Include the new knobs in coexistence sizing
ValidateModelRequest accepts spec_draft_cache_type and the adjacent ctx_checkpoints, but _guard_chat_load_against_training never forwards either value into _estimate_gguf_required_gb, whose sizing functions also lack these parameters. Consequently, an SWA load requesting checkpoints is still budgeted as zero checkpoints and may be admitted alongside training despite requiring substantially more VRAM; conversely, a quantized draft cache is still priced as f16 and can be incorrectly rejected. Thread both settings through the validation and load-time coexistence estimator.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 3b86c00. ctx_checkpoints is now threaded from the guard through _estimate_gguf_required_gb into _estimate_gguf_kv_gb and on to _estimate_kv_cache_bytes, so an SWA load asking for checkpoints is budgeted for them. Checkpoints are per-slot snapshots that scale with the slot context, and ggml-org/llama.cpp#21690 is an OOM caused by exactly that, so the under-count was real.
On the draft cache dtype: this estimator prices the drafter's weights but never its KV, so there is no f16 charge for a quantized draft cache to correct here. The field stays on the preflight because it decides which inherited draft-cache flags get stripped, which is what keeps the preflight approving the command /load actually runs. Its description now says that instead of claiming a sizing role it does not have.
| chatOnly: true, | ||
| // Only when the pick names one, like spec_draft_n_max: the dtype belongs to a | ||
| // draft context that a resident load without a drafter does not have, and | ||
| // comparing null against its absence would reload every time. | ||
| pinned: (c) => c.specDraftCacheDtype != null, |
There was a problem hiding this comment.
Reload when the draft cache dtype is cleared
When a DSpark/DFlash model is resident with an explicit draft dtype such as q8_0, selecting the default f16 changes specDraftCacheDtype to null; this predicate then marks the setting as unpinned, so residentRuntimeMatchesConfig adopts the existing server and skips /load. The backend comparator has the same non-null guard, meaning even a request with a blank dtype cannot trigger the reset. Keep this comparison active whenever the selected speculative mode has a separate drafter so clearing the control actually relaunches with the default cache type.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 3b86c00. Correct catch: the guard was copied from spec_draft_n_max, where an unset value means the platform default the loader recomputes, but that is the wrong model here. Both sides record what the load REQUESTED, so a server launched with no dtype holds null and a blank control still agrees with it, which means the guard bought nothing and cost the reset. Comparing unconditionally on both the frontend predicate and _runtime_matches_intent, and the test that pinned the old behaviour now asserts that clearing the dtype relaunches.
972ea32 to
f5f48d5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f5f48d5ff8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # request counts: the estimator has always charged 0 here, and | ||
| # adopting llama.cpp's default of 32 for every load would move the | ||
| # fit for models nobody asked to change. | ||
| _effective_ctx_checkpoints = int(ctx_checkpoints or 0) |
There was a problem hiding this comment.
Price the effective checkpoint flag before fitting
When a load sets the new ctx_checkpoints field and also supplies an explicit --ctx-checkpoints/--swa-checkpoints in llama_extra_args, the extra argument is appended later and therefore determines what llama-server allocates, but this estimate uses only the first-class field. For example, ctx_checkpoints=0 with llama_extra_args=["--ctx-checkpoints", "256"] budgets no SWA snapshots while launching 256 per slot, so automatic fitting can admit a context that exhausts VRAM. Parse the last-wins effective value from the emitted arguments before passing it to the KV estimator.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in fcab813. Confirmed: both strip sites in the route act on inherited or stored args only, so extras sent for this load survive and last-win at launch, while the fit read the field. Added parse_ctx_checkpoints_override / resolve_ctx_checkpoints in llama_server_args.py, alongside parse_ctx_override and _extra_args_n_ubatch, and both the loader's _effective_ctx_checkpoints and _estimate_gguf_kv_gb now resolve through it, so the coexistence guard sizes the same number the child allocates.
| strip_mlock = True, | ||
| strip_load_mode_aliases = True, | ||
| strip_load_mode = True, |
There was a problem hiding this comment.
Preserve explicit load-mode overrides
When both the Mmap/Mlock control and an explicit --load-mode (or deprecated alias) are supplied while Model Memory is off, this call removes the explicit argument even though llama_extra_args is documented and diagnosed in the UI as being appended last and winning. Thus a power user selecting mmap in the control and typing --load-mode dio silently runs mmap; distinguish inherited extras from arguments supplied for this load and strip only the inherited copy.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in fcab813. Right, and it contradicted the panel: CONTROL_OWNED_FLAGS maps --load-mode to Mmap/Mlock and the diagnostics say the typed flag is passed after the controls and wins. apply_load_mode_policy now strips nothing and only emits before the extras, and the inherited copy is dropped by the route when load_mode is in model_fields_set (aliases included, since a trailing one resets the whole mode), which is the same split the batch pair uses. apply_model_memory_policy keeps its own strip, because there the trailing alias would drop the keep-resident lock and the panel reports that flag as removed rather than winning.
for more information, see https://pre-commit.ci
…he draft cache dtype is cleared The coexistence estimator that admits a chat load beside training never saw ctx_checkpoints, so an SWA load asking for checkpoints was budgeted as zero and could be let in against training it does not fit beside. Checkpoints are per-slot snapshots that scale with the slot's context (ggml-org/llama.cpp#21690 is an OOM caused by exactly that), so thread the value through _estimate_gguf_required_gb into the KV sizing. The draft KV cache dtype was compared only when the request named one, on both sides of the reload check. Clearing the control back to the f16 default stores null, so the resident server kept running the quantized draft cache the panel no longer showed. Both sides record what the load REQUESTED, so comparing unconditionally still matches two loads that asked for nothing.
There was a problem hiding this comment.
💡 Codex Review
https://github.com/unslothai/unsloth/blob/3b86c004fa785939f2a292621cf38cc366635f9e/studio/backend/core/inference/llama_cpp.py#L6330
Probe the legacy checkpoint flag before skipping the control
Recognize the legacy --swa-checkpoints spelling and retain the selected supported flag. The compatibility table in llama_server_args.py explicitly identifies it as the older equivalent, but this probe only accepts --ctx-checkpoints; consequently, a custom older llama-server advertising only --swa-checkpoints is marked unsupported and the new Checkpoints control is silently skipped even though that runtime can honor it.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| spec_draft_cache_type: Optional[str] = Field( | ||
| None, |
There was a problem hiding this comment.
Validate the draft cache dtype before launch
Reject or normalize this field to the supported KV-cache dtype set. A direct /load caller can currently supply an arbitrary or differently-cased value such as fp16 or Q8_0; when a separate drafter is attached, the value is appended verbatim to both draft-cache flags, so llama-server exits during argument parsing after the resident model has already been torn down. The main cache dtype path avoids this by lowercasing and checking its allowlist before emission.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in fcab813. The main cache dtype normalizes and checks its allowlist before emitting, and this one did not, so a direct caller could take the server down after the resident model was already unloaded. It now lowercases, checks the shared _VALID_KV_CACHE_TYPES, and drops an unmapped value with a warning rather than passing it through, so the load still comes up. The MTP draft-KV reserve prices the normalized value too, and the override store already dropped non-allowlisted dtypes.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9583988685
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if ctx_checkpoints is not None: | ||
| if server_caps.get("supports_ctx_checkpoints"): | ||
| cmd.extend(["--ctx-checkpoints", str(int(ctx_checkpoints))]) |
There was a problem hiding this comment.
Fall back to the legacy checkpoint flag
On llama-server builds that expose only the older --swa-checkpoints spelling, the capability probe checks only --ctx-checkpoints, so a value selected in the new Checkpoints control is silently skipped. The same patch explicitly classifies --swa-checkpoints as upstream's older spelling in llama_server_args.py:591-594; probe and retain the supported alias, as is already done for the draft-cache flags, so the control works with those builds.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Correct, and #9416 fixes it. #9410 had already merged when this landed, so the fix is a follow-up rather than another commit here.
Confirmed the alias direction: upstream renamed --swa-checkpoints to --ctx-checkpoints and kept the old name, so both parse on current builds and only the old one exists on a build predating the rename. probe_server_capabilities now walks ("--ctx-checkpoints", "--swa-checkpoints") and records which name this build has, the same handling the draft cache flags already use, and both emission sites use the recorded name, including the Windows full-offload path whose --ctx-checkpoints 0 was skipped for the same reason.
…ode, and allow-list the draft cache dtype Three follow-ups on the new controls, all cases where the extras and the control disagreed: - The fit priced ctx_checkpoints from the field, but the control emits its flag before the extras, so a typed --ctx-checkpoints last-wins at launch. A ctx_checkpoints=0 load with --ctx-checkpoints 256 in the extras allocated 256 per-slot snapshots against a fit that budgeted none. Both the loader and the training-coexistence estimator now resolve the effective value. - apply_load_mode_policy stripped every load-mode token from the extras it was handed, which includes the ones typed for this load, so selecting mmap and typing --load-mode dio silently ran mmap. The panel diagnoses that flag as winning, and now it does: the policy strips nothing and the route drops only an inherited copy, the same rule the batch pair follows. - spec_draft_cache_type reached both draft-cache flags verbatim, so a direct API caller sending Q8_0 or fp16 made llama-server exit at argument parsing with the previous model already torn down. Normalized and allow-listed before emission, like the main cache dtype.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fcab813ca7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| strip_load_mode = "load_mode" in fields_set, | ||
| strip_load_mode_aliases = "load_mode" in fields_set, |
There was a problem hiding this comment.
Strip inherited --mlock with the selected load mode
When /load omits llama_extra_args, inherits a resident --mlock, and supplies load_mode, these toggles remove --load-mode and most deprecated aliases but not --mlock, which belongs to the separate _MLOCK_FLAGS group. Because inherited extras are appended after the managed mode, selecting mmap or dio can still launch with locked weights while status records the selected value; enable strip_mlock whenever a first-class load mode owns inherited flags.
Useful? React with 👍 / 👎.
| # llama-server flags too, so GGUF-only like the rest of this block | ||
| for key in ("load_mode", "spec_draft_cache_type", "ctx_checkpoints", "cache_ram"): | ||
| if override.get(key) is not None: | ||
| kwargs[key] = override[key] |
There was a problem hiding this comment.
Strip saved load-mode flags before auto-switching
When an OpenAI auto-switch override contains the new load_mode together with carried llama_extra_args, this loop forwards the field but the shadow-stripping call below never enables strip_load_mode, its aliases, or strip_mlock. The extras are appended last, so an entry such as load_mode="mmap" with stale --load-mode dio launches DirectIO while the saved setting and requested-status echo say mmap; include the load-mode groups in the same stripping pass as the other newly forwarded controls.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Let the draft-cache control override environment defaults
For a separate-drafter load where LLAMA_ARG_SPEC_DRAFT_CACHE_TYPE_K or _V is set, _extra_args_draft_cache_types returns that environment value and these or assignments preserve it even when spec_draft_cache_type is supplied. The generated command-line cache flags override the environment at launch, so the fitter prices a different dtype from the one that runs; for example, a q4 environment default with a q8/f16 request can underbudget draft KV and select a context that exhausts VRAM. Only explicit extra-argument values should outrank the first-class control.
Useful? React with 👍 / 👎.
* Repair the two studio test suites #9410 left behind #9410 added the llama-server tuning group. Two test-side consequences were not carried with it, and both are red on main. chat-adapter.ts gained imports of serverTuningLoadPayload, committedServerTuningState and clearedServerTuningState. test_chat_autoload_failure_gate.py slices a region of that file and runs it against a PREAMBLE of stubs, so the three names were undefined and 70 scenarios failed. The stubs mirror server-tuning-fields.ts rather than returning nothing, because serverTuningLoadPayload spreads into the /load payload these scenarios assert on. apply-inference-status-to-store.ts now seeds six values through resolveBatchSizeSeed rather than two, so a hardcoded count of two in test_model_picker_contracts.py failed. The count now tracks the number of call sites and additionally pins that every one of them is told modelChanged from the same local, which is the anti-drift property the assertion existed for and is stronger than the number it replaces. * Check the seed contract per call site, not against the whole file A whole-file scan for modelChanged had it both ways: an unrelated one elsewhere in the file would fail the contract with every seed wired correctly, and an unrelated one could equally stand in for a seed that omitted the property, so the assertion could pass while the invariant was broken. Each resolveBatchSizeSeed call's own argument block is now checked, with parens balanced so a nested call survives. Comments are stripped first because this file discusses resolveBatchSizeSeed (modelChanged) in prose, which a raw scan counts as a seventh call site. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

Four llama-server flags documented in tools/server/README.md get first-class controls in Chat -> Run settings -> Advanced settings, instead of only being reachable through the Extra Arguments box.
--load-modeauto--spec-draft-type-k+--spec-draft-type-vf16--ctx-checkpoints--cache-ramAll four sit above Extra Arguments, which stays last so a hand-typed flag still last-wins over a control.
Behaviour
--mlock,--no-mmap).--model-draft. K and V go together, because llama.cpp refusesK != Von an MLA draft context. The value also feeds the MTP draft-KV reserve, so the VRAM budget prices what will run.autoplaceholder, matching Batch Size and Micro-batch Size beside them.0disables either, and-1lifts the Cache RAM limit. An explicit value now also wins over the Windows full-offload tuning, which otherwise forces both to0.Model Memory keeps final say over the load mode
Settings -> Model Memory already owns host placement, so
apply_load_mode_policyruns afterapply_model_memory_policyand defers to it: "Keep model in GPU memory" owns the mode outright, and "Don't reserve system RAM" vetoesnone/mlock/mmap+mlockwhile leavingmmapanddioalone. When either one overrides the pick, the row prints an inline note naming the setting that wins, the same treatment Extra Arguments already gives a typed--load-mode. A test pins the frontend's list of vetoed modes against the backend's so the note cannot drift.Plumbing
Each field follows
n_batch/n_ubatchend to end: per-model storage (schema v5, stamped only when one of the four is set), the chat runtime store, the/loadpayload with the same omit-when-blank rule,LoadRequest,GgufLoadIntent, argv emission gated on the--helpcapability probe, therequested_*status echo, reload dedupe, presets, the stored-override store used by the OpenAI-compatible auto-switch, and the extra-args diagnostics that name the control a typed flag duplicates. Inherited copies of the new flags are stripped only when the matching field is supplied, which is the existing rule for the batch pair.Tests
studio/backend/tests/test_server_tuning_flags.py: 36 tests covering pydantic bounds, the Model Memory precedence matrix, capability fallbacks, shadow stripping, reload dedupe and the override store.studio/frontend/tests/server-tuning-settings.test.ts: normalization, storage version stamping, the load payload, and the extra-args diagnostics.resident-config-match-accelerator-matrix.test.ts, which asserts everyPerModelConfigfield is either compared or deliberately excluded.npm run typecheckandnpm run test(4208 tests) pass. The backend suite was run against this branch and against its merge base, and the two produce the same set of results.Rebased on top of #9383, so the stored config is at v5 with disableVision keeping v4.