Skip to content

QVAC-24114 fix: fail the load when the model cannot decode - #4039

Draft
simon-iribarren wants to merge 1 commit into
mainfrom
fix/qvac-24114-probe-decode
Draft

QVAC-24114 fix: fail the load when the model cannot decode#4039
simon-iribarren wants to merge 1 commit into
mainfrom
fix/qvac-24114-probe-decode

Conversation

@simon-iribarren

Copy link
Copy Markdown
Contributor

Draft — the change compiles clean locally (bare-make, macOS arm64), but this machine cannot run locally built llm-llamacpp binaries (a clean unmodified build fails identically before initFromConfig, so it is a local-toolchain issue, not this change). Needs run-cpp-addon-tests / run-desktop-addon-tests to exercise it on runners, and ideally one manual run of the Gemma fixture below on a 24 GiB Apple silicon machine.

🎯 What problem does this PR solve?

loadModel returning a model id does not mean the model can run. Measured on a 24 GiB M4 Pro with Gemma 4 31B Q4_K_M (18.25 GiB):

  • ctx_size 1024, gpu_layers 99: loads in 25 s (Metal over-commits past recommendedMaxWorkingSetSize), then the first completion fails with [TextLlm] failed to decode next token, and every later request fails with evalMessageWithTools: failed to decode input tokens.
  • ctx_size 1024, gpu_layers 48: same — loads, cannot decode.
  • device 'cpu': decodes (0.1 tok/s), so the GGUF is fine; this is a memory-placement failure surfacing at the worst possible time, on the first user request, with no way for the app to tell a usable load from a dead one.

Two existing layers fail to catch it: fabric's common_init_from_params warmup discards the warmup decode status, and LoadFitNormalization disables warmup outright (params.warmup = false).

This is also the necessary complement to projection-based fitting (QVAC-24112 / tetherto/qvac-fabric-llm.cpp#214): macOS memory pressure is bistable, so a configuration can pass every static budget and still die at decode. Only an actual decode proves usability.

📝 How does it solve it?

After context creation in LlamaModel, run one strict BOS/EOS probe decode (mirroring fabric's warmup shape, encoder models included) and check its status:

  • failure → clear the probe's KV trace, throw a structured UnableToLoadModel StatusError; no model id is handed out.
  • success → the probe doubles as a warmup: weights faulted, Metal pipelines compiled, so first-token latency improves as a side effect.
  • skipped for finetuning, which never serves inference from this context.

No API change; the SDK surfaces the load error through the existing path.

🧪 How was it tested?

  • Compiles clean via bare-make on macOS arm64.
  • Local runtime verification blocked by a pre-existing local-toolchain issue (see note at top): a clean, unmodified build of current main fails identically before initFromConfig, so the published-prebuild control confirms the harness and isolates the local build as the variable.
  • Regression fixture for a runner with ≥ Gemma-class models: load Gemma 4 31B Q4_K_M at ctx_size 1024, gpu_layers 99 on a 24 GiB Apple silicon machine → load() must reject; Qwen3.5 0.8B/9B and gpt-oss-20B fixtures must keep loading (plus one warmup decode).

@github-actions

Copy link
Copy Markdown
Contributor

License compliance — clean

No new dependency license findings in this PR.

Warn-only (shadow) mode — this check does not block merges yet.

Updated automatically by the canonical license compliance workflow.

NOTICE presence (advisory)

Missing NOTICE (advisory, does not block):

  • ./.github/actions/release-merge-guard
  • ./docs/website
  • ./packages/ggml-coload-smoke
  • ./packages/fabric/test/integration
  • ./packages/inference-addon-cpp/mobile
  • ./packages/sdk/e2e
  • ./packages/llm-llamacpp/benchmarks/performance
  • ./packages/llm-llamacpp/benchmarks/server
  • ./packages/vla-ggml/sim/server
  • ./packages/embed-llamacpp/benchmarks/performance
  • ./packages/embed-llamacpp/benchmarks/server
  • ./packages/asr-ggml/benchmarks/server

A model can survive init and still be unable to decode: on a 24 GiB M4 Pro,
Gemma 4 31B Q4_K_M at ctx 1024 loads (Metal over-commits past
recommendedMaxWorkingSetSize) and then every decode fails with 'failed to
decode next token'. Fabric's warmup would not catch it — common_init_from_params
discards the warmup decode status — and LoadFitNormalization disables warmup
anyway. Run one strict BOS/EOS probe decode after context creation and throw
UnableToLoadModel on failure, leaving no KV trace. A successful probe doubles
as a warmup. Skipped for finetuning, which never serves inference from this
context.
@simon-iribarren
simon-iribarren force-pushed the fix/qvac-24114-probe-decode branch from bbd0380 to 7fe18b3 Compare August 26, 2026 09:55
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