Skip to content

Auto-load a matching mmproj so vision models just work - #254

Closed
GiuseppeCapaldo93 wants to merge 2 commits into
thomas9120:mainfrom
GiuseppeCapaldo93:feat/auto-mmproj-for-vision
Closed

Auto-load a matching mmproj so vision models just work#254
GiuseppeCapaldo93 wants to merge 2 commits into
thomas9120:mainfrom
GiuseppeCapaldo93:feat/auto-mmproj-for-vision

Conversation

@GiuseppeCapaldo93

@GiuseppeCapaldo93 GiuseppeCapaldo93 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Motivation

Multimodal (vision) GGUF models need a companion multimodal projector
(mmproj) passed via -mm. Today the projector has to be located and wired up
by hand; if it is forgotten the model loads without vision and there is no
obvious hint why.

Change

When a model is launched, look for an mmproj whose filename correlates with
the model file and pass it as -mm automatically:

  • Searches the dedicated models/mmproj/ tree recursively (so a projector the
    downloader stored under models/mmproj/<repo-slug>/ is rediscovered after a
    reload) and models/ shallowly, recognizing mmproj / clip* / projector
    names.
  • Correlates by a normalized key that ignores quantization/precision tokens and
    shard suffixes, so e.g. Nemotron-…-Q4_K_M.gguf matches
    mmproj-Nemotron-…-BF16.gguf.
  • Requires exact normalized-key equality, so a close-but-different family version
    (e.g. -V-2.6 vs -V-2) or an unrelated model will not grab a stray
    projector.
  • Uses the effective (last) model source, so a repeated -m or a later remote
    -hf/--model-url is honored rather than the first -m.
  • Respects an explicit -mm / --mmproj / --mmproj-url / --no-mmproj, and
    only applies to llama-server / llama-cli / llama-mtmd-cli.
  • Can be disabled with LLAMA_GUI_AUTO_MMPROJ=0.

Testing

  • Added AutoMmprojTests covering key normalization, companion matching,
    false-positive avoidance, and the override/disable paths.
  • Full backend test suite passes (486 tests).

When a model is launched, look for a multimodal projector (mmproj) whose filename correlates with the model (quantization/precision tokens ignored, e.g. Nemotron-...-Q4_K_M matches mmproj-Nemotron-...-BF16) in models/mmproj/ or models/, and pass it as -mm automatically. Respects an explicit -mm/--mmproj/--no-mmproj and can be disabled via LLAMA_GUI_AUTO_MMPROJ=0. Requires a substantial name overlap to avoid false matches (e.g. LFM2 won't grab an unrelated projector).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@GiuseppeCapaldo93
GiuseppeCapaldo93 force-pushed the feat/auto-mmproj-for-vision branch from e4463e6 to 88bed79 Compare July 28, 2026 09:18

Copy link
Copy Markdown
Owner

P2 — Make automatic mmproj matching conservative

The feature direction looks good, but the current substring/length fallback can attach an incompatible same-family projector. For example:

  • model MiniCPM-V-2.6-Q4_K_M.gguf normalizes to minicpmv26
  • projector mmproj-MiniCPM-V-2-F16.gguf normalizes to minicpmv2

Because minicpmv2 is contained in minicpmv26 and is longer than eight characters, it is accepted for V2.6. Automatically attaching the wrong projector can make the model fail to load or leave vision behavior broken; in this case, declining to auto-match is safer.

A narrow fix would be:

  1. Normalize common Qn_n suffixes such as Q4_0 and Q5_1 as complete quantization tokens. The current regex can consume only the Q4 portion and leave a trailing 0.
  2. Accept a candidate only when its normalized key equals the model's normalized key; remove the substring/score fallback.
  3. Add a positive regression test for Vision-Model-Q4_0mmproj-Vision-Model-BF16, and a negative test proving MiniCPM-V-2.6 does not match mmproj-MiniCPM-V-2.

This keeps the existing directory scan, explicit -mm / --no-mmproj handling, tool scope, environment toggle, and launch integration unchanged. Exact normalized equality still supports the advertised quantization/precision-insensitive matches, while broader fuzzy matching can be added later if real filename cases demonstrate a need for it.

@GiuseppeCapaldo93

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review — addressed in the latest push (fdfdcbc).

  • Full Qn_n quant tokens: the quant regex now consumes the whole token, including legacy Q4_0 / Q5_1, so no trailing digit is left behind.
  • Exact equality only: find_matching_mmproj now requires the model and mmproj filenames to reduce to the same normalized key; the substring/score fallback is removed, so an uncertain case declines to auto-match rather than attaching the wrong projector.
  • Regression tests: added positive Vision-Model-Q4_0mmproj-Vision-Model-BF16 and negative MiniCPM-V-2.6 vs mmproj-MiniCPM-V-2 (both at the _normalize_model_key level and end-to-end through find_matching_mmproj).

The directory scan, explicit -mm / --no-mmproj handling, tool scope, env toggle, and launch integration are unchanged. Full backend suite passes (489 tests).

@thomas9120 thomas9120 self-assigned this Jul 28, 2026
@thomas9120

Copy link
Copy Markdown
Owner

Almost there,

Thanks — fdfdcbc resolves the previous concerns cleanly. The complete Q4_0/Q5_1 normalization, exact-key matching, and positive/negative regression tests all look good.

I found one remaining P2 edge case in compute_auto_mmproj_args(): it stops at the first local -m/--model argument, while the rest of Llama-GUI treats the last model-source flag as effective. Custom Launch Args intentionally permit duplicates and are placed before the selected UI model, so arguments such as:

-m models/old-Q4_K_M.gguf -m models/selected-Q4_K_M.gguf

can launch the selected model while auto-attaching the old model’s projector. Similarly, a later -hf or --model-url source could receive an unrelated local projector.

The narrow fix would be to reuse the existing helper:

model_flag, model_path = _last_launch_flag_entry(
    flat_launch_args, _MODEL_VALUE_FLAGS
)
if model_flag not in _LOCAL_MODEL_VALUE_FLAGS or not model_path:
    return []

Launching a vision model previously required manually passing the multimodal
projector. When a matching mmproj file is present, add "-mm <path>" for the
vision-capable tools (llama-server / llama-cli / llama-mtmd-cli), unless the user
already set an mmproj flag or --no-mmproj.

Matching is conservative: model and mmproj filenames must reduce to the same
normalized key (extension, shard suffix, a leading "mmproj" marker, and
quantization/precision tokens are stripped), so a quantized model matches its
BF16/F16 projector while a close-but-different family version does not. The
q-quant token is normalized whole, including legacy Qn_n forms such as Q4_0 and
Q5_1, so no trailing digit is left behind.

The effective (last) model source drives matching, via the existing
_last_launch_flag_entry precedence helper: Custom Launch Args may repeat
-m/--model and sit before the UI-selected model, and a later remote -hf/-mu
source suppresses auto-detection so no local projector is wrongly attached.

Projector discovery scans the dedicated models/mmproj/ tree recursively (so a
projector the downloader stored under models/mmproj/<repo-slug>/<file>.gguf is
rediscovered after a reload) and the plain models/ directory shallowly (for
manually placed files), recognizing the same names the downloader accepts
(mmproj / clip* / projector) via is_mmproj_filename.

Adds tests for quant normalization, exact-key matching (positive and negative),
last/effective model source, remote-source suppression, recursive discovery of a
downloaded projector, and clip/projector name recognition. The directory scan,
explicit -mm / --no-mmproj handling, tool scope, environment toggle, and launch
integration are unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@GiuseppeCapaldo93
GiuseppeCapaldo93 force-pushed the feat/auto-mmproj-for-vision branch from fdfdcbc to f550445 Compare July 29, 2026 08:33
@GiuseppeCapaldo93

Copy link
Copy Markdown
Contributor Author

Thanks — all three points are addressed (f550445).

  • Effective (last) model source: compute_auto_mmproj_args() now uses the existing precedence helper exactly as suggested —
    model_flag, model_path = _last_launch_flag_entry(flat_launch_args, _MODEL_VALUE_FLAGS)
    if model_flag not in _LOCAL_MODEL_VALUE_FLAGS or not model_path:
        return []
    so a repeated -m uses the last one and an effective remote -hf/--model-url suppresses auto-detection. Tests cover both (two local -m → last wins; trailing -hf → no projector).
  • Recursive projector discovery: the dedicated models/mmproj/ tree is now scanned recursively (so a projector stored under models/mmproj/<repo-slug>/<file>.gguf is rediscovered after a reload), while models/ stays a shallow scan for manually placed files. Candidate recognition reuses the downloader's own is_mmproj_filename() (mmproj / clip* / projector). Added a regression test with a projector under models/mmproj/<repo-slug>/, plus one for clip/projector name recognition.
  • Doc nit: the PR description now says "exact normalized-key equality" instead of "substantial name overlap".

Full backend suite passes (493 tests).

(For reference, the same review was also posted on #253 — these fixes live here on the mmproj branch.)

@thomas9120

Copy link
Copy Markdown
Owner

This one will take a little extra time on my end, I'm putting it on hold for a bit.

@thomas9120

Copy link
Copy Markdown
Owner

Thanks — all three points are addressed (f550445).

* **Effective (last) model source**: `compute_auto_mmproj_args()` now uses the existing precedence helper exactly as suggested —
  ```python
  model_flag, model_path = _last_launch_flag_entry(flat_launch_args, _MODEL_VALUE_FLAGS)
  if model_flag not in _LOCAL_MODEL_VALUE_FLAGS or not model_path:
      return []
  ```
  
  
      
        
      
  
        
      
  
      
    
  so a repeated `-m` uses the last one and an effective remote `-hf`/`--model-url` suppresses auto-detection. Tests cover both (two local `-m` → last wins; trailing `-hf` → no projector).

* **Recursive projector discovery**: the dedicated `models/mmproj/` tree is now scanned recursively (so a projector stored under `models/mmproj/<repo-slug>/<file>.gguf` is rediscovered after a reload), while `models/` stays a shallow scan for manually placed files. Candidate recognition reuses the downloader's own `is_mmproj_filename()` (`mmproj` / `clip*` / `projector`). Added a regression test with a projector under `models/mmproj/<repo-slug>/`, plus one for clip/projector name recognition.

* **Doc nit**: the PR description now says "exact normalized-key equality" instead of "substantial name overlap".

Full backend suite passes (493 tests).

(For reference, the same review was also posted on #253 — these fixes live here on the mmproj branch.)

Oops, didn't realize that happened.

@thomas9120

Copy link
Copy Markdown
Owner

Sadly this conflicts with changes that were in progress regarding model discoverability in subfolders. I'll have to close for now.

@thomas9120 thomas9120 closed this Jul 30, 2026
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.

2 participants