Studio: add Mmap/Mlock, draft KV cache dtype, Checkpoints and Cache RAM to Run settings by danielhanchen · Pull Request #9410 · unslothai/unsloth · GitHub
Skip to content

Studio: add Mmap/Mlock, draft KV cache dtype, Checkpoints and Cache RAM to Run settings - #9410

Merged
danielhanchen merged 6 commits into
mainfrom
studio-advanced-llama-server-flags
Aug 20, 2026
Merged

Studio: add Mmap/Mlock, draft KV cache dtype, Checkpoints and Cache RAM to Run settings#9410
danielhanchen merged 6 commits into
mainfrom
studio-advanced-llama-server-flags

Conversation

@danielhanchen

@danielhanchen danielhanchen commented Aug 20, 2026

Copy link
Copy Markdown
Member

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.

Control Flag llama.cpp default
Mmap/Mlock --load-mode auto
Spec Decoding KV Cache Dtype --spec-draft-type-k + --spec-draft-type-v f16
Checkpoints --ctx-checkpoints 32
Cache RAM --cache-ram 8192 MiB

All four sit above Extra Arguments, which stays last so a hand-typed flag still last-wins over a control.

Behaviour

  • Mmap/Mlock offers every documented value: Auto, None, mmap, mlock, mmap+mlock, DirectIO. Auto is llama.cpp's own default, so picking it stores nothing and emits nothing, which means a build that redefines auto is followed rather than pinned. A build predating the enum falls back to the deprecated spellings for the values that had one (--mlock, --no-mmap).
  • Spec Decoding KV Cache Dtype only appears for DSpark and DFlash, the modes that always attach a separate draft model, and the flags are only emitted where the launch actually carries --model-draft. K and V go together, because llama.cpp refuses K != V on an MLA draft context. The value also feeds the MTP draft-KV reserve, so the VRAM budget prices what will run.
  • Checkpoints and Cache RAM are blank by default with an auto placeholder, matching Batch Size and Micro-batch Size beside them. 0 disables either, and -1 lifts the Cache RAM limit. An explicit value now also wins over the Windows full-offload tuning, which otherwise forces both to 0.

Model Memory keeps final say over the load mode

Settings -> Model Memory already owns host placement, so apply_load_mode_policy runs after apply_model_memory_policy and defers to it: "Keep model in GPU memory" owns the mode outright, and "Don't reserve system RAM" vetoes none / mlock / mmap+mlock while leaving mmap and dio alone. 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_ubatch end to end: per-model storage (schema v5, stamped only when one of the four is set), the chat runtime store, the /load payload with the same omit-when-blank rule, LoadRequest, GgufLoadIntent, argv emission gated on the --help capability probe, the requested_* 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.
  • The four join the sweep in resident-config-match-accelerator-matrix.test.ts, which asserts every PerModelConfig field is either compared or deliberately excluded.

npm run typecheck and npm 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.

…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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread studio/backend/models/inference.py Outdated
Comment on lines +477 to +481
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 "

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +350 to +354
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,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@danielhanchen
danielhanchen force-pushed the studio-advanced-llama-server-flags branch from 972ea32 to f5f48d5 Compare August 20, 2026 16:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +1255 to +1257
strip_mlock = True,
strip_load_mode_aliases = True,
strip_load_mode = True,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

pre-commit-ci Bot and others added 2 commits August 20, 2026 16:35
…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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

https://github.com/unslothai/unsloth/blob/3b86c004fa785939f2a292621cf38cc366635f9e/studio/backend/core/inference/llama_cpp.py#L6330
P2 Badge 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".

Comment on lines +203 to +204
spec_draft_cache_type: Optional[str] = Field(
None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

danielhanchen added a commit to shimmyshimmer/unsloth-staging-4 that referenced this pull request Aug 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +17790 to +17792
if ctx_checkpoints is not None:
if server_caps.get("supports_ctx_checkpoints"):
cmd.extend(["--ctx-checkpoints", str(int(ctx_checkpoints))])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@danielhanchen
danielhanchen merged commit 5786a0e into main Aug 20, 2026
4 of 45 checks passed
@danielhanchen
danielhanchen deleted the studio-advanced-llama-server-flags branch August 20, 2026 16:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +8437 to +8438
strip_load_mode = "load_mode" in fields_set,
strip_load_mode_aliases = "load_mode" in fields_set,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +700 to +703
# 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]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +16208 to +16210

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

danielhanchen added a commit that referenced this pull request Aug 21, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant