Skip to content

Sync with upstream main: Gemma4 video input coexisting with our audio port - #3

Merged
Satchitananda merged 25 commits into
mainfrom
sync/upstream-main-20260726
Jul 26, 2026
Merged

Sync with upstream main: Gemma4 video input coexisting with our audio port#3
Satchitananda merged 25 commits into
mainfrom
sync/upstream-main-20260726

Conversation

@Satchitananda

@Satchitananda Satchitananda commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Merges the 24 upstream commits our lineage was missing (upstream tip 3cbf928). The two stacks now coexist:

Headline reconciliations

Also inherited: upstream MTP enablement for 12B unified (ml-explore#415), Gemma4Text MoE (ml-explore#364), Hunyuan V1, DeepSeek-V2, TurboQuant KV compression (ml-explore#232), Qwen3.5 M-RoPE optimization, FoundationModels fixes, integration-test infra.

Verification

  • swift build clean.
  • swift test: 398 tests / 51 suites, 5 issues — the same pre-existing Gemma4 chunked-prefill float-tolerance cases that fail identically on main before this merge. The new upstream MixedPrecisionQuantLoadTests suite passes with the verification resolution above.
  • Downstream: mlx-video-catalog-kit swift build clean; SightRoll dev app BUILD SUCCEEDED against this branch.

Post-merge

  • Tag the tip to reflect the upstream base per our new scheme — upstream main is past the 3.31.4 release, so suggest 3.31.4-fork.2 (or wait for upstream's next release tag to align exactly).
  • Real-model QA before the next beta: 12B unified + E-series describe pass (the merge touches the Gemma4 prompt-expansion path), and optionally try upstream's native video input as a future alternative to frame extraction.

CharlieTLe and others added 25 commits July 17, 2026 07:57
* Track the current SDK's SamplingMode.Kind case names

FoundationModels renamed GenerationOptions.SamplingMode.Kind's .top/
.nucleus cases to .randomTopK/.randomProbabilityThreshold, which broke
compilation against the newer SDK.
…er compiled (ml-explore#438)

* Add behavior-neutral generation-event observation for tests

Route every channel send through typed emit helpers that also hand a
readable GenerationEvent mirror to an optional task-local observer. The
observer is nil in shipping builds, so channel sends are unchanged. This
gives tests a way to read streamed output that the macOS 27 SDK otherwise
keeps opaque.

* Make TestResponseStream yield readable GenerationEvent values

Attach the executor's observation hook via the task-local and relay its
readable mirror events to tests, draining and discarding the opaque SDK
channel events. Tests now iterate GenerationEvent instead of pattern-
matching the sealed channel actions.
…l's processor (fixes ml-explore#433) (ml-explore#435)

The tool-calling path hand-built its LMInput from raw applyChatTemplate
output, producing a 1-D [N] token array. LLM models tolerate that (the
default LLMModel.prepare adds the batch axis), but every VLM prepare
implementation requires [1, N] and indexes dim(1), so any VLM-loaded
model with .toolCalling declared aborted with a fatal 'SmallVector out
of range' (mlx-c array.cpp:335) the moment the constrained turn began.

Route the tool-aware prompt through context.processor.prepare (as the
unconstrained and guided paths already do) so the input carries the
token rank its model family requires — and so image/video content in
the transcript reaches the model instead of being silently dropped.
The think-then-call Phase-2 continuation is built by a new rank- and
media-preserving helper, with unit tests.
Merge message draft:

---

**Fix `ropeOffset` protocol witness trap in `BaseKVCache`**

`BaseKVCache` does not declare `ropeOffset`, so the `KVCache` extension default (`.scalar(offset)`) is bound as the protocol witness at its conformance. A subclass that declares a property of the same name only shadows it statically: every access through a `KVCache` reference — which is how models read it, e.g. `let offset = cache?.ropeOffset` — still resolves to `.scalar(offset)` and silently ignores the subclass.

Nothing in the tree overrides `ropeOffset` today, so no current behavior changes. But `BaseKVCache` is `open` and this is a trap for any cache that needs a non-scalar offset: a batched cache returning a per-row `.batch(...)` compiles cleanly and is then ignored, feeding every row the same scalar offset.

Declare `ropeOffset` as an `open` class property on `BaseKVCache` (same `.scalar(offset)` default) so it is the real witness and subclasses get a dynamically-dispatched override point.

**Testing**

Added a test that subclasses `BaseKVCache`, overrides `ropeOffset` with a per-row `.batch(...)`, and reads it back through a `KVCache` reference. Without this change the test does not compile ("property does not override any property from its superclass"), which is precisely the defect.
… on the FM-27 SDK (ml-explore#439)

The FM-27 beta `.swiftinterface` declares
`LanguageModelExecutorGenerationChannel.Response.Action.updateUsage(input:output:metadata: = [:])`,
but the shipping FoundationModels dylib exports only the older two-parameter
`updateUsage(input:output:)`. Because `emitUsage` relied on the `metadata:`
default, the compiler resolved the call to the three-parameter symbol, which
does not exist at runtime. dyld cannot bind it, so every `respond()` path
crashed (SIGSEGV) the instant generation finished and usage was emitted.

Drop the `channel.send(.updateUsage(...))` call, which was the only reference
to the missing symbol. A runtime guard cannot help: the compiled reference
alone aborts the process at load under chained-fixups linking, before any
check runs. The `generationObserver` notification is preserved, so usage is
still observable. Restore the send, documented inline, once a later SDK ships
a dylib that matches its interface.

Verified on-device: UpdateUsageEmissionTests passes on all three respond()
paths (unconstrained, guided, tool-calling).
…lore#440)

Set OS_ACTIVITY_MODE=disable on the IntegrationTesting scheme TestAction
to suppress log spam from MLX runtime shader JIT compilation, e.g.:

  [Metal Compiler Warning] Warning: Compilation succeeded with:
  mlx/backend/metal/kernels/defines.h:10:32: warning: unused variable
  MAX_REDUCE_SPECIALIZED_DIMS [-Wunused-const-variable]
  static MTL_CONST constexpr int MAX_REDUCE_SPECIALIZED_DIMS = 4;
                                  ^
  (and similarly for REDUCE_N_READS, REDUCE_N_WRITES, SOFTMAX_N_READS,
  RMS_N_READS, RMS_LOOPED_LIMIT)

These come from mlx-swift vendored MLX C++/Metal sources, not this repo.
* Add TurboQuant KV cache compression

Walsh-Hadamard rotation + Lloyd-Max codebook KV compression behind the
kvScheme parameter (ml-explore#230), with JIT Metal kernels: fused encode, a
SIMD-parallel two-pass online-softmax flash decode (packed-K, raw-fp16-K,
and 8-bit-affine-K score variants), and an inline sparse value kernel.

Schemes are asymmetric because keys are far more quality-sensitive than
values (softmax amplifies key error, value error averages out): the
recommended tiers keep K at fp16 (turbo0v4/v3/v2) or 8-bit affine
(turbo8v4/v3/v2) while V takes the compression; symmetric turbo4/3/2
compress both sides. Symmetric key quality rides on per-dimension key
calibration: at compression time the cache derives a per-dimension scale
from the post-rotation key variance of the prefill cache and folds it
into the query side, leaving the score kernels untouched; the calibrated
encode rotates through the codec's MLX ops so kernel and reference
quantize identical values. Boundary-layer protection converts the first
and last attention layers to 8-bit affine for fragile schemes. Rotating
(sliding-window) cache layers stay fp16 with a one-time notice; global
layers on mixed models compress normally.

Per-call kernel values travel in a parameter buffer rather than template
arguments, so each shape compiles once. Caches stay raw fp16 through
prefill, compress on the first decode step, and encode incrementally;
state round-trips through prompt cache save/restore, and speculative
decoding routes through the compressed path. Generation settles cache
state with the token eval and drains autoreleased temporaries per step so
long generations hold steady memory.

* Add TurboQuant tests

Codec round trips and pinned codebooks, rotation orthogonality, packing, kernel-vs-reference attention parity (GQA repeat factors 1/2/3, non-power-of-two head dims), calibrated-encode kernel exact index parity, prompt-cache round trips for all state layouts including calibration state, compressed partial trim with continued decode, speculative decoding with a turbo scheme, allocation-boundary growth, boundary-protection gating, mixed rotating/standard cache conversion, and end-to-end generation on tiny random-weight models. CI-runnable, no checkpoint downloads.

* Add KV cache quantization documentation

Scheme reference table, the asymmetric-first selection ladder, measured family sensitivity and calibration notes, performance guidance, and limitations.
…#387)

Expose Gemma3 encoder surface behind @_spi(GemmaEncoder) for frozen-text-encoder use

Multimodal generators (e.g. Lightricks LTX-2) increasingly condition on ALL hidden states of a frozen LLM text encoder rather than generated tokens. Since an encoder-style all-hidden-states tap uses a single uniform mask that bypasses Gemma3's per-layer sliding-window/global mask selection, it diverges from generic Gemma3 semantics for long texts — so it doesn't belong in MLXLLM itself. Instead, this exposes the minimal surface needed for client code to implement that tap:

Gemma3TextConfiguration.hiddenSize, .hiddenLayers
Gemma3TransformerBlock (class) and .callAsFunction(_:mask:cache:)
Gemma3Model.embedTokens, .layers, .config
All seven are gated @_spi(GemmaEncoder) public (not plain public) so they're usable via @_spi(GemmaEncoder) import MLXLLM without becoming advertised public API. GemmaEncoder is a new SPI group, distinct from the existing test-only @_spi(Testing).

No behavior changes — access levels, attributes, and doc comments only.

Gemma3EncoderAccessTests proves sufficiency by implementing the allHiddenStates tap in test code using only the exposed surface (no @testable), checking state count (numHiddenLayers + 1), (B, T, hiddenSize) shapes, first state == scaled embedding, and determinism on a tiny randomly-initialized model.
* Optimize Qwen3.5 interleaved M-RoPE
* Optimize Qwen3-VL interleaved M-RoPE
Brings the MLXVLM Gemma4 MoE implementation to the LLM text path so the
26B-A4B (MoE) checkpoint loads through MLXLLM, not just MLXVLM. Adds
Gemma4TextRouter, Gemma4TextExperts (SwitchGLU), the enable_moe_block config +
dense/sparse decoder branch, and the fused-expert weight remap in sanitize.
The E-series KV-sharing it pairs with is already upstream (ml-explore#342).

Ported from MLXVLM/Models/Gemma4.swift (ml-explore PRs ml-explore#180, ml-explore#228).
… pair (ml-explore#445)

Fix MTP integration test skip semantics; pin 31B checkpoints for fixture parity

- Convert checkpoint-gated tests from `Issue.record()+return` (which Swift Testing treats as a failure, not a skip) to auto-download via the existing `Downloader` mechanism, matching the pattern used elsewhere in the suite.
- Document that MTPRung4TokenParityTests' case_02_block4/case_03_block6 failures stem from stale fixtures (pre-dating a checkpoint update), not a Swift bug — confirmed by bit-for-bit match against a live mlx-vlm re-run.
- Pin the 31B target/drafter downloads to the exact commits live at fixture-generation time, restoring parity for all 3 Rung4TokenParityTests without regenerating fixtures. Verified weight blobs are unchanged at the pins via HF's per-commit LFS-OID listing.
- Add optional `targetRevision`/`drafterRevision` params to `loadTargetAndDrafter` so 31B call sites can pin while env-gated E4B/E2B sites stay on latest.
- Trim checkpoint-pin comments to self-contained one-liners.
…t + gemma4_unified_assistant drafter) (ml-explore#415)

* Add gemma4_unified_assistant MTP drafter support (12B target)

The 12B Gemma 4 (model_type gemma4_unified, class Gemma4Unified) could not
use MTP speculative decoding:

- Its drafter (google/gemma-4-12B-it-assistant and the mlx-community
  conversions) ships model_type gemma4_unified_assistant, which was not
  registered with MTPDrafterTypeRegistry.
- Gemma4Unified did not override callAsFunction(_:cache:state:), so the
  mtpEmitFlagKey opt-in was discarded by the protocol-extension default and
  the target never emitted drafter state — the MTP iterator silently fell
  back to single-token passthrough.
- Gemma4AssistantDraftModel.draftBlock only accepted a Gemma4 target and
  trapped on Gemma4Unified.

Fix: register gemma4_unified_assistant with the same creator (the drafter
implementation is shared; the unified variant differs only in its
text_config, which Gemma4TextConfiguration already decodes), add the
MTP-aware state entry point to Gemma4Unified (mirroring Gemma4), and accept
Gemma4Unified in draftBlock's target dispatch.

Tests: config-decode pin mirroring the published 12B assistant config, and
a synthetic end-to-end pin (Gemma4Unified emits state through the MTP entry
point; draftBlock accepts the unified target and returns [1, blockSize-1]).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Make draftBlock target-agnostic via Gemma4BackboneProviding

Replace the concrete-class switch in Gemma4AssistantDraftModel.draftBlock
(case Gemma4 / case Gemma4Unified) with a cast to a new internal
Gemma4BackboneProviding protocol exposing the shared text backbone.
Adopted by Gemma4 and Gemma4Unified in Gemma4.swift, where the
conformances can reach the private languageModel — this also reverts the
visibility widening of Gemma4Unified.languageModel from the previous
commit. Existing trap semantics for non-Gemma4 targets are kept.
…xplore#432) (ml-explore#434)

Fix tool-schema $defs resolution for grammar compilation in tool-calling envelopes.

Problem: Named sub-schemas (nested @Generable/DynamicGenerationSchema types) get serialized as root-level $defs with root-anchored #/$defs/... refs. When a tool's parameter schema is embedded under oneOf[i].properties.arguments, its $defs got buried there too, but refs still pointed to the envelope root — causing xgrammar's grammar compilation to fail (constraintCompilationFailed) for any tool with non-flat argument schemas.

Fix: Hoist each tool's $defs to the envelope root, namespacing them per tool (<tool>__<def>) and rewriting that tool's refs to match. Applied once in toolCallingEnvelopeObject, covering both envelope and structural-tag encoders.

Refinement (review feedback): The ref rewrite was made structure-aware — instead of a raw string find/replace, it walks the parsed schema tree and only rewrites string values under $ref keys. This prevents corrupting unrelated strings (e.g., a @Guide description that happens to mention #/$defs/Foo), and correctly handles refs nested inside other $defs bodies. Added a doubly-nested test fixture and a pinning test for the description-string edge case.

Fixes ml-explore#432
…l-explore#455)

The vision Attention materialized a [1, L, L] additive -1e9 mask over the
concatenated patch sequence of every image and fell back to unfused SDPA
(head dim 72 is outside the fused kernel's 64/80/128), so attention cost
16 * (4 * pads)^2 * 2 bytes in one Metal buffer: 19.3GB at a 6144-token
image, and a two-image 8146-token request exceeded maxBufferLength.

The mask was block-diagonal per image, so attend each cu_seqlens segment
independently with no mask (mlx-vlm's Python implementation does the
same), and zero-pad Q/K/V head dim 72 -> 80 to dispatch the fused kernel
(the gemma4EnsureFusedSDPA trick; padding is exact, scale is explicit).

Measured on Qwen3.5-9B, M4 Pro: single 6120-pad image peak 28.7 -> 12.5GB,
two-image 8140-pad request fatal -> 14.2GB, prefill 59.8 -> 36.3s, outputs
unchanged.
…ing (ml-explore#395)

The mlx-community Gemma 4 QAT checkpoints (gemma-4-12B-it-qat-4bit,
gemma-4-E4B-it-qat-4bit) quantize attention and embeddings at 4-bit and
every mlp.{gate,up,down}_proj at 8-bit, declared as per-module overrides
interleaved with the global settings in config.json's quantization dict.

Loading these checkpoints works today: QuantizationContainer decodes the
interleaved dict, PerLayerQuantization resolves each module path, and
loadWeights quantizes each module with its per-module bits (mirroring
Python mlx-lm's class_predicate). But nothing exercised that path end to
end — only the config-decode unit tests in BaseConfigurationTests — and
reports like ollama/ollama#16740 attribute empty/garbage generation to
loaders mis-decoding the 8-bit MLP payload as 4-bit, so it is behavior
worth pinning.

Add MixedPrecisionQuantLoadTests, which builds a tiny gemma4_unified
model, quantizes its own weights into a synthetic checkpoint with the QAT
layout (8-bit MLP / 4-bit rest), and verifies through loadWeights that:

- per-module overrides are honored (mlp projections come back as 8-bit
  QuantizedLinear, everything else 4-bit), and
- a load that ignores the overrides (global 4-bit only) fails loudly on
  the packed-shape mismatch instead of silently mis-decoding the payload.

Validated to bite: with the per-layer lookup in loadWeights replaced by
the global default, the first test fails with mismatchedSize on
layers.0.mlp.gate_proj ([128, 8] vs [128, 16]).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ion call (ml-explore#457)

Two bugs on the cached path, so DeepSeek-V3 could not generate.

1. `kvHeads` was declared `[Int] = []` and never assigned. `newCache` derives the
   layer count from `kvHeads.count`, so it returned an empty array, and the first
   `cache?[i]` in the layer loop trapped with "Index out of range". Any generation
   crashed; only a `cache: nil` forward pass worked. Populate it the way the other
   models do: one entry per layer.

2. Attention called `cache.update(...)` explicitly and then passed the resulting
   keys/values to `attentionWithCacheUpdate`, which performs the update itself.
   The second update appended the already-returned history back into the cache, so
   a prompt of L tokens left the cache at 2L and every subsequent token attended
   over duplicated keys. Drop the explicit update and let the helper own the single
   one, matching Llama/Qwen3/Cohere and the Python reference. This also routes the
   quantized-cache path correctly, which the manual update bypassed.

Adds DeepseekV3Tests with a cache-growth regression test: a 5-token forward pass
must leave every layer's cache at offset 5. With (1) unfixed the test crashes;
with (1) fixed and (2) unfixed it reports 10.

Reported by @davidkoski in ml-explore#379, where the same double-update was found in the
DeepSeek-V2 port.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add multi-round tool calling to MLXFoundationModels: replay prior tool calls/results into the prompt and keep the tool path active each round until the model answers, matching LanguageModelSession/ChatSession semantics.

Along the way, fixed several correctness issues surfaced by new characterization tests:
- Degenerate/non-deterministic outputs from Gemma and Qwen (thinking-mode collisions, grammar ordering, greedy free-text decoding).
- Tool-call grammar/tokenizer boundary bugs (EOS residuals, LFM2 prefix duplication, unfinished protocol tails, ordered-output streaming).
- Required/disallowed tool-mode handling, native allowed-tool routing, and removal of the synthetic final-answer tool in favor of native structured outputs.
- Foundation Models sampling-seed preservation and propagation.

Added a dormant diagnostic sink for guided-generation tracing, plus new characterization and integration tests (tool-call grammar, structured outputs, seed forwarding), with SDK/compile fixes to keep the FoundationModels integration target building.
Add DeepSeek-V2 model

Port of mlx-lm's deepseek_v2.py, reusing the in-tree V3 MLA machinery (q_a/q_b + kv_a_with_mqa/kv_b compression, qk nope/rope split, YaRN rope) and SwitchGLU MoE with shared experts. Router differs from V3: plain softmax scoring, optional group-limited-greedy masking, unconditional routed_scaling_factor. Dense layers below first_k_dense_replace use standard MLP.

Fixes along the way:
- Make `qLoraRank` optional (`Int?`); V2-Lite sets `q_lora_rank: null` and needs direct q_proj instead of the q_a/q_b LoRA path. Fixes loading mlx-community/DeepSeek-V2-Lite-Chat-4bit-mlx.
- Update the KV cache once per attention call instead of twice — the explicit `cache.update()` plus `attentionWithCacheUpdate`'s internal update was duplicating cached keys/values, doubling cache size and corrupting attention beyond the first token.
- Fix `topk_method` default typo ("gready" → "greedy"), cosmetic only.

Includes tests for logits shape, group-limited-greedy routing, expert-stacking in `sanitize()`, qLoraRank parsing, and KV cache growth.
…l-explore#347)

Add Hunyuan dense V1 (hunyuan_v1_dense): Hunyuan-MT-7B and Hy-MT2-7B

Adds Tencent's Hunyuan dense V1 architecture (HunYuanDenseV1ForCausalLM), used by
translation models Hunyuan-MT-7B and Hy-MT2-7B. A Llama-family dense transformer
close to the Qwen3 path: per-head QK RMSNorm (applied after RoPE, matching the
mlx-lm reference), GQA, SwiGLU MLP, pre/post RMSNorm blocks, tied embeddings, and
DynamicNTKAlphaRoPE (rescales RoPE base by alpha^(dim/(dim-2)), reusing the
existing freqs-based fast-RoPE path).

Changes:
- RoPEUtils.swift: DynamicNTKAlphaRoPE
- Models/Hunyuan.swift: model/config (flat decode, head_dim/attention_head_dim
  alias, conditional qk-norm)
- LLMModelFactory.swift: register hunyuan_v1_dense; presets for both models
  (4-bit/8-bit)
- HunyuanTests.swift: config decode, sanitize, forward, dynamic-RoPE, presets

Validated byte-identical greedy output against mlx-lm reference on locally
converted 4-bit weights for both models.
* Add GitHub workflow for IntegrationTesting tests

Runs the IntegrationTestingTests target manually (workflow_dispatch) and
nightly (schedule) on the self-hosted macOS runner. Kept off the PR path
since the tests download HF checkpoints, require Metal, and are long-running.
Uses -parallel-testing-enabled NO to avoid concurrent workers racing on the
shared on-disk Hugging Face cache.
…re#391)

Add video input to base Gemma4 (gemma4) VLM

Gemma4 has no separate video encoder — video frames run through the same vision tower as images and scatter onto `<video>` soft-token positions.

Model:
- `Gemma4Configuration` decodes `video_token_id` and `vision_soft_tokens_per_video_frame` (nil/default preserves old text+vision behavior)
- `getInputEmbeddings` accepts `videoPixelValues`; image/video paths share a new `scatterVisionFeatures`/`scatterVideoFeatures` helpers; per-layer mask excludes video soft tokens
- `prepare` routes `input.video?.pixels` and passes real `videoTokenId`
- No weight/sanitize changes — video reuses the vision tower

Processor:
- `Gemma4ProcessorConfiguration` decodes `video_token_id`, `video_soft_tokens_per_frame`, `video_max_frames`, and `videoFixedSize`
- `Gemma4Processor.processVideos` samples ≤32 frames at ~1fps and preprocesses to a stacked `[frames, C, H, W]` tensor
- `Gemma4Processor.prepare` populates `LMInput.video` and expands `<video>` placeholders per frame

Tests (`Gemma4VideoInputTests`): config decode, image/video logit equivalence, multi-frame truncation, processor frame extraction. `Gemma4ChunkedPrefillTests`/`Gemma4UnifiedTests` still pass.

Follow-ups: per-frame `mm:ss` timestamps; encoder-free `Gemma4Unified` processor video block.
The nightly IntegrationTesting job failed to compile on the Xcode 26.5
runner: the FoundationModels adapter (MLXFoundationModels) is gated behind
canImport(FoundationModels, _version: 2) (macOS 27 SDK only), but the
integration test files gated only on the always-set FoundationModelsIntegration
trait, so they referenced symbols absent on the 26 SDK.

- Extend the 37 FoundationModels-gated test files' top-level guard to
  '#if FoundationModelsIntegration && canImport(FoundationModels, _version: 2)',
  mirroring the library so they compile out on the 26 SDK and stay active on 27.
- Workflow: prefer Xcode 27 (via DEVELOPER_DIR) when the runner has it so the
  full suite runs; otherwise fall back to the default toolchain and run the
  SDK-agnostic suites (MTP, Qwen3VL/Qwen3.5 vision, Coherence, Gemma4, tool calls).
… new models

Brings the 24 upstream commits past our shared lineage, most notably:
- end-to-end video input for Gemma 4 (ml-explore#391) — merged to COEXIST with our
  audio port: config/CodingKeys/decode unions, both scatter paths in
  getInputEmbeddings (audio block then video block), both placeholder
  expansions in prepare(input:), LMInput carries image+video+audio
- upstream MTP enablement for 12B unified (ml-explore#415), Gemma4Text MoE (ml-explore#364)
- effectiveEOSTokenIds (nested text configs, ml-explore#449) — adopted in both
  factories, keeping our sampling-params surfacing
- per-layer-input masking generalized to image/audio/video soft tokens
  (upstream's multimodalMask supersedes our image+audio version)
- unified multimodalTokenCountMismatch error replaces imageTokenCountMismatch
- Hunyuan V1, DeepSeek-V2, TurboQuant KV compression, FoundationModels
  fixes, Qwen3.5 M-RoPE optimization

Load.swift verification collision resolved strict-by-default: upstream's
regression (ml-explore#395) requires verify .all so mismatched quantized shapes fail
loudly; our mxfp8 relaxation now applies ONLY when an mxfp8 layer was
actually built (its shapes legitimately differ until _updateInternal
reconciles).

swift test: 398 tests, 4 issues — all pre-existing Gemma4 float-tolerance
cases (identical on main before this merge).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Satchitananda
Satchitananda merged commit bbcc961 into main Jul 26, 2026
2 checks passed
Satchitananda added a commit that referenced this pull request Jul 26, 2026
Sync with upstream main: Gemma4 video input coexisting with our audio port
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.