Skip to content

server : fix 501 on multimodal models blocking text-only slot save/restore (#21133) - #25076

Merged
ggerganov merged 1 commit into
ggml-org:masterfrom
CHIPMUNK-T0T:feat/mtmd-slot-save-restore
Jul 16, 2026
Merged

server : fix 501 on multimodal models blocking text-only slot save/restore (#21133)#25076
ggerganov merged 1 commit into
ggml-org:masterfrom
CHIPMUNK-T0T:feat/mtmd-slot-save-restore

Conversation

@CHIPMUNK-T0T

@CHIPMUNK-T0T CHIPMUNK-T0T commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Overview

When I used a multimodal model (e.g. Qwen3.5) and load and use it with an image modality with mmproj, this llama.cpp server returned HTTP 501 unconditionally even for text-only conversations, because the check looked at model capability (mctx) rather than the slot's actual content.
As a result, the prefill cache could not be stored or reused even for text-only conversations, which leads to slow TTFT for long prompts.
The affected operations were /slots save / restore / erase on a server with --mmproj loaded. These now gate on the slot's content (has_media()): a text-only slot is allowed, and only a slot that holds media is rejected.

Additional information

This PR helps downstream consumers (e.g. Ollama) that reuse a prefilled cache across requests via slot save/restore on a multimodal model, which was previously blocked whenever an mmproj was loaded.
Only the text-only case is newly allowed; all other behavior is unchanged — slots that actually hold media are still rejected, and text-only (non-multimodal) servers are unaffected.

Notes:

  • The restore path keeps using the existing llama_state_seq_* format. Since that format cannot store media chunks, a restored slot is always text-only, so no media gate is needed there.
  • On save, get_text_tokens() is used instead of get_tokens() (which asserts !has_mtmd); the slot is confirmed media-free first.
  • Serializing image/audio chunks is out of scope here and could be a follow-up.

Testing:

  • New tests on a multimodal model (tinygemma3): text-only save / restore / erase succeed, while saving a slot that holds an image is rejected with HTTP 501. The existing text-only tests are unchanged.
  • Local run: clean build (-j8), test_slot_save.py passing 5/5.

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: Yes, but I used AI translater using Ai agent, because I'm Japanese, not a native English user, so it is difficult to tell you my soul!
    • The original motivation came from me. I used ollama to more fast decode/ttft, and I had AI aided (Codex and Claude Code) investigate and assess its impact/scope, but I made the final decision on every implementation change myself.
    • I have reviewed all changes, understand them fully, and can explain any line without AI assistance.

ISSUE #21133

@CHIPMUNK-T0T
CHIPMUNK-T0T requested a review from a team as a code owner June 27, 2026 11:04
@ggml-gh-bot

ggml-gh-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown

Hi @CHIPMUNK-T0T, thanks for your contribution!

Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:

  • AI-generated content: This project does not accept PRs, descriptions or commit messages that are fully or predominantly AI-generated. If you have used AI to assist you in writing code, please make sure to disclose that explicitly.

Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below.

@CHIPMUNK-T0T

CHIPMUNK-T0T commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the notice.

To clarify my AI usage: I am a native Japanese speaker, so it is difficult to tell you my native opinion, so I thought the original explanation and reasoning about this topic in Japanese, and I used AI assistance to translate and polish the English text.
The PR design, implementation decisions, final commit contents, and tests are mine.
I reviewed the final English description and code myself, and I can explain and maintain the changes.

If this still does not satisfy the project policy, I am happy to shorten or rewrite the PR description in simpler wording.

@CHIPMUNK-T0T

CHIPMUNK-T0T commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Hi @CISC

this PR is still ready for review and currently mergeable.

The main use case is reusing text-only prefill cache when a multimodal model is loaded, while continuing to reject slots that actually contain media.

Please let me know if the scope or implementation should be adjusted.

@ggerganov ggerganov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, this is a good improvement.

We should also support save/restore with the media. This simply requires to serialize the server_tokens together with the llama state. In case you are interested for a follow-up PR.

@ggerganov ggerganov added the merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. label Jul 16, 2026
@ggerganov

Copy link
Copy Markdown
Member

Please rebase this branch to the latest master.

@ggerganov
ggerganov merged commit a8dc0e3 into ggml-org:master Jul 16, 2026
11 of 30 checks passed
@CHIPMUNK-T0T
CHIPMUNK-T0T deleted the feat/mtmd-slot-save-restore branch July 17, 2026 12:19
fewtarius added a commit to fewtarius/CachyLLama that referenced this pull request Jul 19, 2026
…, OpenCL Q6_K/Adreno, CORS, checkpoint min-step, prompt cache refactor, MoE expert API stays)

Upstream highlights since 6be7459:
- model: DFlash speculative with KV rotation (ggml-org#25823)
- model: Hy3 (hy_v3) with MTP speculative decoding (ggml-org#25395)
- model: DeepseekV4 with fused hyper-connection ops (ggml-org#25585)
- ggml: 0.17.0, LIGHTNING_INDEXER, out_prod, f16 set_rows
- vulkan: Q2_0 support, native e2m1/e4m3 conversions, transfer-queue race fix
- CUDA: MMQ kernel config refactor (ggml-org#24127), tighter MMQ src1 buffer for fp4 (ggml-org#25613), CUDA graphs on Volta/Turing, MoE gate/up dedup, CUDA Virtual Devices
- ROCm: hexagon L2 cache rework, native fp4, FP16/INT8 coopmat on AMD
- SYCL: Battlemage flash attention via oneDNN XMX, XIELU op, fp16 conv2d_dw
- OpenCL: Q6_K GEMM/GEMV fix, ragged-tile MoE prefill FP16, Adreno vectorized LD/ST, A7x optimizations, ABS op
- kleidiai: SME2 f32 kernel, SME vs SME2 dispatch
- server: refactor prompt cache state ownership (ggml-org#25649) - new server_prompt_cache_state separates prompt metadata from KV data
- server: evict checkpoints within min-step (ggml-org#25472)
- server: text-only slot save/restore with mtmd (ggml-org#25076)
- server: --cors-* options (ggml-org#25655)
- server: refactored server_stream (ggml-org#25541)
- server: respect min-step when splitting prompt batches (ggml-org#25420)
- server: move chat-template thinking probe inside init try/catch (ggml-org#24093)
- common: auto-download dflash/eagle3 HF sidecars (ggml-org#25811), drop --stdin mutual-exclusion, align tokenize usage
- conversion: BitNetForCausalLM, dflash tokenizer fix, split MTP export for HY V3
- llama-quant: exclude i32 ffn_gate_tid2eid routing table, allow manual tensor types with --pure
- llama-batch: fix allowed decreasing pos in a seq (ggml-org#25449), n_keep_tail in split_equal for recurrent
- llama: refactor fused ops (ggml-org#24646), TP fix for Phi3/Bert/Plamo2/3/ChatGLM
- ui: agentic content UX, reasoning effort on mobile add sheet, MCP panel fixes, thinking menu fix
- vendor: BoringSSL 0.20250713.0
- tests: actually exercise test-recurrent-state-rollback, ds_v4_hc sentinel init, export-graph-ops graceful exit

CachyLLama preservation work (conflict resolution):

1. tools/server/server-task.h: Accept upstream's server_prompt refactor (no data member, clear() method).
   Move our t_last_used field from server_prompt to server_prompt_cache_state (where it now lives
   after the refactor). server_prompt_cache_state already has the size() method, so our old
   size() on server_prompt is no longer needed.

2. tools/server/server-context.cpp (create_checkpoint): Take upstream's min-step eviction
   pre-filter as the FIRST pass, then keep our existing highest-pos_min eviction as the
   capacity overflow fallback. These are complementary: min-step removes redundant checkpoints
   from the same task; highest-pos_min keeps the rec-window-friendly checkpoints when at cap.

3. tools/server/server-context.cpp (handle_completions_impl): Keep our std::vector<server_task>
   tasks batching for multi-prompt requests and per-user concurrency check, AND take upstream's
   res->set_req(&req) for spipe ownership transfer.

4. tools/server/server-task.cpp: Fix references to entry.tokens -> entry.prompt.tokens,
   entry.checkpoints -> entry.prompt.checkpoints, entry.n_tokens() -> entry.prompt.n_tokens().
   Update find_eviction_candidate return type from list<server_prompt>::iterator to
   list<server_prompt_cache_state>::iterator.

5. ggml/src/ggml-cuda/mmq.cuh + new mmq-config-rdna3_5.cuh: Upstream's massive MMQ refactor
   moved per-architecture config into separate files but did NOT add RDNA3.5 (gfx1150/1/2/3,
   Strix Halo). Create mmq-config-rdna3_5.cuh (231 CASE entries) derived from rdna2 with
   nthreads=128 (4 warps) and I=48 (smaller X tile) matching our original Strix Halo tuning.
   Wire into both host and device dispatch paths before the RDNA4 / RDNA2 fallback.

6. README.md and AGENTS.md: Keep CachyLLama-specific links and project context where upstream
   added parallel content.

Verified:
- cmake --build builds clean (Release, CPU-only)
- llama-server starts, --help shows all CachyLLama flags preserved:
  --cache-ssd-hot-ram, --cache-ssd-warm-ram, --cache-ssd-system-prompts,
  --cache-ssd-system-max-days, --cache-ssd-no-fsync, --cache-ssd-max-conversations,
  --max-concurrent-per-user
- /expert-stats and /expert-tracking endpoints preserved
- 55/58 tests pass; 3 failures unrelated to merge:
  - test-tokenizers-ggml-vocabs: missing model downloads
  - test-jinja-py: missing jinja2 Python module
  - test-quant-type-selection: snapshot mismatch on upstream's new MXFP4_MOE heuristic

Custom CachyLLama files untouched (no upstream conflicts):
- common/kv-ssd-cache.{cpp,h}, common/kv-ssd-posix.h, common/kv-ssd-system-cache.{cpp,h}
- common/kv_page_manager.{cpp,h}
- tools/server/server-context-page-manager.{cpp,h}
- tools/server/server-context-ssd-cache.{cpp,h}
- test_kv_page_manager.cpp, tests/test-ssd-cache-caps.cpp
- STRIX_HALO_NOTES.md, docs/development/user-isolation-design.md
- .github/workflows/build-cpu.yml, build-cuda-windows.yml, build-vulkan.yml
CowboyTim pushed a commit to aardbeiplantje/llama.cpp that referenced this pull request Jul 21, 2026
ggerganov pushed a commit to am17an/llama.cpp that referenced this pull request Jul 28, 2026
smalinin pushed a commit to smalinin/llama.cpp that referenced this pull request Aug 4, 2026
satindergrewal pushed a commit to satindergrewal/llama.cpp that referenced this pull request Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants