Skip to content

fix(A1): iOS vision 'Multimodal support not enabled' — keep only precision in mmproj on-disk name#605

Open
alichherawalla wants to merge 7 commits into
mainfrom
fix/ios-vision-mmproj-not-initialized
Open

fix(A1): iOS vision 'Multimodal support not enabled' — keep only precision in mmproj on-disk name#605
alichherawalla wants to merge 7 commits into
mainfrom
fix/ios-vision-mmproj-not-initialized

Conversation

@alichherawalla

@alichherawalla alichherawalla commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Fixes ALL vision-projector (mmproj) bugs on device — the iOS "Multimodal support not enabled" crash and the family of projector-naming mismatches behind it.

Bug (device report 2026-07-23, Christophe — iPhone 12, SmolVLM-256M)

A ggml-org vision model loads, Repair reports success, image attaches, then generation throws "Multimodal support not enabled. Call initMultimodal first." unsloth Qwen worked — a publisher-specific split, not a family one.

Two seams, both fixed

1. On-disk rename (mmProjLocalName) — ggml-org names projectors mmproj-<ModelName>-<quant>.gguf (model name AFTER mmproj). The old /mmproj.*$/ baked that name in → doubled identity stem → mmProjBelongsToModel refused it → linkOrphanMmProj cleared the link → text-only load → initMultimodal never ran. Fix: keep only the precision token → always <model>-mmproj-<precision>.gguf.

2. Download pairing (pickMmProjForDownload / modelIdentityStem) — a second class found in the live-HF sweep where no projector paired at all:

  • mmproj-model-<prec> literal placeholder (ggml-org gemma-3, openbmb MiniCPM) → treated as a generic repo-single-model projector.
  • unsloth UD- dynamic-quant packaging (Mistral-Small-3.1) → stripped from the identity stem.
  • moondream -text-model weights marker → normalized so weights and -mmproj share a stem.

Strict matching preserved — E2B still refuses an E4B projector (no wrong-arch mispairing).

Verification

  • Live-HF fixture test (visionMmprojFromHF.test.ts): captures the REAL Hugging Face listing + real GGUF download probes for every curated + popular vision repo, replays the real pair→rename→belongs pipeline offline. Now asserts EVERY vision repo (all naming shapes) pairs and its on-disk name belongs. Reverting either fix turns repos red. Plus offline unit regressions in parallelMmproj.test.ts.
  • On-device (iPhone 17 Pro Max, iOS 26.5.2): downloaded ggml-org SmolVLM-256M-Q8_0 + mmproj, attached a photo, asked "what's in this image?" → real description, no error. Device log: mmproj saved as smolvlm-256m-instruct-mmproj-Q8_0.gguf (fixed name) and [WIRE-VISION] … initialized:true, support:{vision:true}.

Summary by CodeRabbit

  • Bug Fixes

    • Improved vision projector-to-model matching and local projector filename generation to prevent mismatched downloads and repair issues.
    • Normalized projector/model identity handling for quantization/precision naming patterns and refined generic projector fallback selection.
  • Tests

    • Added/updated integration coverage using real-world Hugging Face fixture data to validate projector pairing, renamed projector ownership, and GGUF integrity.
    • Expanded unit tests for precision-based local naming, ownership checks, deduplication across quantizations, and additional real naming shapes.
  • Documentation

    • Updated the GAPS backlog with a verified mmproj-related watch item for iOS/Android and an Android first-attempt investigation note.

…HF vision repos

Hits real Hugging Face (no mocks) for every curated vision repo + popular families,
runs the real pair→rename→belongs pipeline, and asserts the projector belongs after
rename. Red today for the ggml-org `mmproj-<ModelName>-<quant>` shape (SmolVLM, Qwen2.5-VL,
pixtral, InternVL). Also locks the separate no-pairing bug set as a tracked ledger.
ggml-org names projectors `mmproj-<ModelName>-<quant>.gguf` (model name AFTER the mmproj
token). The old `/mmproj.*$/` kept that model name, doubling the on-disk identity stem, so
mmProjBelongsToModel refused it, linkOrphanMmProj cleared the link, and the model loaded
text-only → initMultimodal never ran → 'Multimodal support not enabled' on a vision send
(device 2026-07-23, iPhone 12, SmolVLM). Extract only the precision token so the on-disk
stem always matches the model. Verified against live HF: 8 rename-broken repos now pass,
the 6 already-working stay working.
…ne test

Replace the always-network tests (flaky, slow) with the capture-once/reuse pattern:
UPDATE_HF_FIXTURES=1 downloads the real HF listing + real GGUF header bytes for every vision
repo into a committed fixture; normal runs replay the real pair→rename→belongs pipeline over
that captured real data in ~0.5s, offline, deterministic. Reverting the fix flips the ggml-org
curated repos red. Tracks the separate no-pairing bug as a ledger.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Vision projector matching now normalizes role and packaging markers, recognizes generic projector placeholders, and derives precision-based local names. Unit tests and committed Hugging Face fixtures validate pairing, renamed ownership, and GGUF probes across vision repositories.

Changes

Vision projector pairing

Layer / File(s) Summary
Normalize projector identity and selection
src/services/mmproj.ts, __tests__/unit/services/parallelMmproj.test.ts
Projector identity matching now ignores -text-model and qualifying UD- packaging markers; generic model placeholders participate in fallback selection, with tests for real-world naming variants and mismatches.
Normalize local projector names
src/services/modelManager/download.ts, __tests__/unit/services/parallelMmproj.test.ts
mmProjLocalName extracts precision tokens for local filenames, with tests covering ggml-org names, ownership checks, and quant-independent deduplication.
Capture and validate Hugging Face vision fixtures
__tests__/integration/models/visionMmprojFromHF.test.ts, __tests__/fixtures/hf/vision-repos.json, docs/GAPS_BACKLOG.md
Optional refresh mode captures repository GGUF listings and range probes; cached tests validate pairing, renamed ownership, GGUF magic bytes, and an empty broken-repository set across examined repositories. The backlog records device verification and an Android first-load watch item.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant visionMmprojFromHFTest
  participant HuggingFaceAPI
  participant mmproj
  participant FixtureFile
  visionMmprojFromHFTest->>HuggingFaceAPI: Fetch repository GGUF listing
  HuggingFaceAPI-->>visionMmprojFromHFTest: Return model and projector filenames
  visionMmprojFromHFTest->>mmproj: Select paired projector and derive local name
  visionMmprojFromHFTest->>HuggingFaceAPI: Probe selected files with range requests
  HuggingFaceAPI-->>visionMmprojFromHFTest: Return GGUF probe metadata
  visionMmprojFromHFTest->>FixtureFile: Persist or load fixture data
  visionMmprojFromHFTest->>mmproj: Validate pairing and renamed ownership
Loading

Possibly related PRs

  • off-grid-ai/OGAM#558: Modifies the same projector matching and downloading logic with related pairing tests and fixtures.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed but skips several required template sections, including Type of Change, Checklist, Related Issues, and Additional Notes. Add the missing template sections, especially Type of Change, checklist items, related issues, and any additional notes or follow-up work.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main fix: mmproj on-disk naming for vision models causing the iOS multimodal failure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ios-vision-mmproj-not-initialized

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@__tests__/integration/models/visionMmprojFromHF.test.ts`:
- Around line 55-61: Update fetchTreeGgufFiles to throw an error when the
Hugging Face fetch response is not OK instead of returning an empty array.
Preserve the existing parsing and filtering behavior for successful responses so
fixture refreshes cannot persist transient failures as missing files.
- Around line 145-156: Update the noPairing filter in the known-open repository
test to include repositories with missing pairs, failed
repoPairsAndBelongs(repo).belongs ownership, or an invalid probe result. Keep
the existing tracked repository expectation unchanged so any non-known-open
pairing or rename regression, including SmolVLM, fails the test.

In `@src/services/modelManager/download.ts`:
- Around line 35-36: Update the precision extraction in the projector naming
logic near mmProjSourceName so it selects the final precision-like token, or
specifically the token associated with the .gguf suffix, rather than the first
match. Preserve the existing fallback to mmproj.gguf, and add a regression test
covering mmproj-Q8_0-Vision-f16.gguf expecting mmproj-f16.gguf.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f6e6c115-115f-440b-bb69-f6f01b9ddf32

📥 Commits

Reviewing files that changed from the base of the PR and between 9c9e908 and 0e29da9.

📒 Files selected for processing (4)
  • __tests__/fixtures/hf/vision-repos.json
  • __tests__/integration/models/visionMmprojFromHF.test.ts
  • __tests__/unit/services/parallelMmproj.test.ts
  • src/services/modelManager/download.ts

Comment thread __tests__/integration/models/visionMmprojFromHF.test.ts
Comment thread __tests__/integration/models/visionMmprojFromHF.test.ts Outdated
Comment thread src/services/modelManager/download.ts Outdated
…eam)

Fixes the second mmproj bug class found in the live-HF sweep, so ALL vision-projector
shapes pair + belong:
- `mmproj-model-<prec>` literal placeholder → treated as a generic repo-single-model
  projector (ggml-org gemma-3, openbmb MiniCPM).
- unsloth `UD-` dynamic-quant packaging stripped from the identity stem so a UD-quant
  model matches its non-UD projector (Mistral-Small-3.1).
- moondream `-text-model` weights marker normalized so the weights and `-mmproj` file
  share a stem.
Strict matching preserved — E2B still refuses an E4B projector (unit test). Verified over
live-HF fixture data: every curated + popular vision repo now pairs and its on-disk name belongs.
…oken)

Addresses SonarCloud (super-linear regex backtracking, download.ts:35) and CodeRabbit
(take the projector's OWN trailing precision, not the first precision-like token in the
name) in one change. Also: fixture refresh now throws on a HF 429/5xx instead of silently
persisting an empty fixture.
@sonarqubecloud

Copy link
Copy Markdown

…Android fail was transient (load-timing watch-item, not a blocker)

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/GAPS_BACKLOG.md`:
- Around line 377-379: Update the new documentation entry around “A1 mmproj fix”
by removing both em dashes, replacing them with a colon or parentheses while
preserving the heading and verdict meaning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d7954ff-1b01-4d08-abf1-a65e2f7ef2f3

📥 Commits

Reviewing files that changed from the base of the PR and between 85510b4 and 02ae6cb.

📒 Files selected for processing (1)
  • docs/GAPS_BACKLOG.md

Comment thread docs/GAPS_BACKLOG.md
Comment on lines +377 to +379
## A1 mmproj fix — possible load-immediately-after-download vision race (2026-07-24)

**Verdict: instrument-and-revisit (LOW — unconfirmed). NOT a merge blocker; PR #605 verified on both

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove em dashes from the new documentation entry.

Replace the em dashes in the heading and verdict with a colon or parentheses.

As per coding guidelines, documentation content must avoid em dashes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/GAPS_BACKLOG.md` around lines 377 - 379, Update the new documentation
entry around “A1 mmproj fix” by removing both em dashes, replacing them with a
colon or parentheses while preserving the heading and verdict meaning.

Source: Coding guidelines

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