Skip to content

Add llamacpp launch settings (MTP, MoE offload) and Qwen3.5+ thinking control - #228

Merged
t-timms merged 1 commit into
mainfrom
feat/qwen38-mtp-cpumoe-thinking
Sep 24, 2026
Merged

t-timms merged 1 commit into
mainfrom
feat/qwen38-mtp-cpumoe-thinking

Conversation

@t-timms

@t-timms t-timms commented Sep 24, 2026

Copy link
Copy Markdown
Owner

What

Makes Godspeed able to launch and drive Qwen3.8-27B-class models on a 16 GB card, based on a desk review of what the harness could not do.

llama.cpp launch (llamacpp_manager, config, cli, tui/textual_app)

  • New optional llamacpp: settings section: server_bin, model_path, context, no_kv_offload, kv_cache_type, spec_type, spec_draft_n_max, n_cpu_moe, reasoning_budget, extra_args.
  • spec_type: draft-mtp uses the multi-token-prediction head embedded in Qwen3.5+ MTP GGUFs (no separate draft model; also --parallel 1).
  • $GODSPEED_LLAMA_SERVER overrides binary detection.
  • start_server() checks the binary's --help and refuses flags it does not know. The pinned b9066 builds predate MTP (upstream PR #22673, 2026-05-16), so this now fails early with a clear message.

Thinking control (llm/client.py, /effort)

  • _THINKING_CAPABLE_PREFIXES matched only qwen3.6 / qwen3-, so qwen3.8-* got no thinking control. Its chat template thinks by default at xhigh.
  • qwen3.5..qwen3.9 now send chat_template_kwargs (enable_thinking, reasoning_effort), the per-request mechanism llama-server documents. Raw reasoning_effort is no longer forwarded to these models because the Qwen3.8 template raises on anything outside xhigh|medium|low.
  • /effort none disables thinking.

Behavior changes / compatibility

  • No default behavior change for existing setups. With no llamacpp: section the launch command line is identical (covered by a test), and with no reasoning_effort / thinking_budget set nothing new is sent.
  • Qwen3.6 with thinking_budget > 0 now also sends chat_template_kwargs in addition to the previous extra_body keys (kept for compatibility).
  • /effort accepts none for every model; for non-Qwen3.5+ models it is forwarded like the other values.

Verification

  • ruff check ., ruff format --check ., mypy src/ (CI flags), bandit (CI flags): clean.
  • New tests: manager flags, help probe, override, refusal path, settings mapping, config validation, thinking payloads (both call paths share one helper), example profile parses. Targeted modules (test_llamacpp_manager, test_thinking, test_config_llamacpp, test_llm_client, test_cli_more): 338 passed, 1 skipped (the skip is the pre-existing model-file skipif).
  • Mutation check: reverting the qwen3.8 regex, dropping --parallel 1, and treating reasoning_budget=0 as falsy each fail a specific new test.
  • Full suite: see the CI run on this PR.

Not verified (needs a GPU / live server)

  • No live llama-server run. Nothing here was exercised against a real model. --n-cpu-moe, --reasoning-budget and --spec-type draft-mtp were confirmed present in a local WSL build's --help, not run.
  • The example profile scripts/settings_local_llm_qwen38_27b.yaml is labelled UNVALIDATED; its speed figures are sourced or estimated and say so. no_kv_offload: false for the hybrid model is an unmeasured choice.
  • No driver_catalog.yaml entries added: the catalog requires a passing scripts/validate_driver.py run, which needs the model served. Only added a caveat to the existing unverified qwen3.6-27b speed claim.
  • The MTP-capable build on the author's machine lives in WSL. Godspeed on Windows cannot launch a Linux binary, so start it there and Godspeed attaches to the running server. Launching via wsl.exe is a possible follow-up.

Why these numbers/choices

  • Template kwargs and the effort set come from the Qwen3.8 chat_template.jinja.
  • llama-server's documented per-request fields are chat_template_kwargs and reasoning_effort; a per-request thinking-token budget is not documented, hence the server-wide reasoning_budget.

… control

llamacpp_manager could only launch the 14B + draft-model setup it was tuned
for, and the client did not recognise Qwen3.8 as a thinking model.

llama.cpp launch
- New `llamacpp:` settings section (LlamaCppSettings) wired through
  _ensure_llamacpp from both the CLI and the Textual app: server_bin,
  model_path, context, no_kv_offload, kv_cache_type, spec_type,
  spec_draft_n_max, n_cpu_moe, reasoning_budget, extra_args.
- spec_type=draft-mtp uses the MTP head embedded in Qwen3.5+ GGUFs instead of
  a separate draft model (skips draft auto-detect, greedy sampling and the
  24576 context cap, and passes --parallel 1).
- $GODSPEED_LLAMA_SERVER overrides binary detection.
- start_server probes `llama-server --help` and refuses to launch with a flag
  the binary does not know. The pinned b9066 builds predate MTP (upstream
  PR #22673), so this fails early with a clear message instead of after launch.
- Defaults reproduce the previous command line exactly.

Thinking control
- The model match covered only qwen3.6 and qwen3-, so qwen3.8-* got no
  thinking control while its chat template thinks by default at effort xhigh.
- qwen3.5..qwen3.9 now get chat_template_kwargs (enable_thinking,
  reasoning_effort) from reasoning_effort or thinking_budget. The Qwen3.8
  template raises on any effort outside xhigh|medium|low, so raw values are
  normalised and never forwarded to these models.
- /effort accepts `none` to turn thinking off.
- The duplicated thinking blocks in _call and the streaming path are now one
  helper.

Also: example profile scripts/settings_local_llm_qwen38_27b.yaml, docs and
CHANGELOG entries, and a caveat on the unverified qwen3.6-27b speed claim in
driver_catalog.yaml.
@github-actions

Copy link
Copy Markdown

🤖 Godspeed Review

LLM review skipped (no API key configured).

Security

No secrets detected in changed files.

@t-timms
t-timms merged commit a2c3005 into main Sep 24, 2026
9 checks passed
@t-timms
t-timms deleted the feat/qwen38-mtp-cpumoe-thinking branch September 24, 2026 03:18
t-timms added a commit that referenced this pull request Sep 24, 2026
…easoning tier (#229)

Ternary-Bonsai-2-27B's KNOWN_ISSUES page documents chat-template failures that
also apply to any Qwen3.8 build (the template is shared). Checking Godspeed
against them found three exposures.

- Resumed sessions appended a mid-conversation `system` message
  (Conversation.add_system_message, called from the TUI resume bootstrap).
  Qwen3.5+ templates raise "System message must be at the beginning" -> HTTP 500.
  It now merges into the single leading system prompt; once messages exist it
  adds a labelled `user` note instead.
- Empty / None tool-call arguments were stored as-is in history, and the
  templates reject them (HTTP 500/400). add_assistant_message now stores "{}".
- _parse_tool_call called json.loads("") on an empty argument string and dropped
  a valid zero-argument call as malformed. Empty/None now parse as no arguments.
  Malformed JSON is still rejected.
- The derived reasoning-effort tiers picked "low" for thinking_budget <= 2048 and
  for the "minimal" alias, but Prism ML measured "low" using about as many
  reasoning tokens as the default xhigh on Qwen3.8. Derived tiers now pick
  "medium" (budget <= 8192) or "xhigh"; an explicit `reasoning_effort: low` is
  still honoured. Follows up #228.

Tests: tests/test_qwen38_template_safety.py (new), and the two resume tests that
asserted the old two-system-message behavior now pin the fixed behavior.
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