Skip to content

Add Mage-VL image and video support - #472

Open
justinchuby wants to merge 4 commits into
mainfrom
justinchuby-add-mage-vl
Open

Add Mage-VL image and video support#472
justinchuby wants to merge 4 commits into
mainfrom
justinchuby-add-mage-vl

Conversation

@justinchuby

Copy link
Copy Markdown
Member

Summary

Adds first-class support for microsoft/Mage-VL (model_type: mage_vl) as a standardized three-model package:

  • decoder: Qwen3 text decoder with ordinary 1D RoPE
  • vision_encoder: custom 24-layer Mage-ViT for images and streaming video
  • embedding: batch-safe text/visual feature mixer

The vision graph matches Mage-VL's fused biased QKV layout, exact 4:6:6 temporal/height/width RoPE, original sampled-frame temporal positions, 2x2 patch merger, and independent four-frame attention windows. CUDA/DML builds use packed attention with dynamically derived window boundaries; portable builds retain a standard-ONNX block-diagonal fallback.

This also adds remote config extraction, registry/public exports, exact checkpoint weight routing, ordered mixed image/video processing, ORT config hooks, real media fixtures, and L1-L5 coverage.

Validation

  • Unsupported status verified before implementation; no existing mage_vl registry/config/model support was present.
  • Upstream revision studied and pinned: d88b153285f1633a61b2f693c59c8576693af185.
  • Real processor path exercised with a nonzero Apache-2.0 image and soccer video: 4,284 packed patches, five sampled frames, original temporal indices [0, 180, 360, 539, 719], and 1,138 prompt tokens.
  • python -m pytest tests/build_graph_test.py tests/weight_alignment_test.py tests/mage_vl_parity_test.py -k "mage_vl" -q: 10 passed.
    • Includes exact checkpoint-key alignment, FP32 parity (atol/rtol=1e-4), FP16 parity (1e-2), four-frame-boundary video coverage, empty-media decode, multi-row media ordering, and packed-CUDA graph coverage.
  • python -m pytest tests/yaml_schema_test.py src/mobius/_testing/golden_test.py src/mobius/integrations/ort_genai/auto_export_test.py -q: 345 passed.
  • python -m pytest tests/build_graph_test.py tests/cli_test.py src/ -q -k "not phi4mm and not apply_weights_unknown" --tb=short -n auto: 3,639 passed, 57 skipped.
  • python -m pytest tests/quantization_integration_test.py -v: 6 passed.
  • lintrunner -a: clean.
  • Automated code review was run repeatedly; findings for batch indexing, strict image-only processors, and quadratic video attention were fixed. Final review reported no significant issues.

Real checkpoint / GPU

Hardware: NVIDIA RTX A1000, 8,188 MiB; ORT 1.28.0 CUDA EP.

  • BF16 L4 mixed real image/video, CUDA-specialized build and CUDAExecutionProvider: 1 passed in 324.12s.
  • BF16 L5 deterministic generation, 24 new tokens, same real image/video and CUDA path: 1 passed in 236.19s.
  • The committed HF golden's 24-token continuation begins: The image depicts a group of four men standing around a table in a stadium....
  • No peak-memory number is claimed.

CLI exports

Both completed successfully with real weights and emitted decoder/, vision_encoder/, embedding/, tokenizer files, genai_config.json, and image_processor.json:

python -m mobius build --model microsoft/Mage-VL --trust-remote-code \
  --dtype f16 --ep cuda --runtime ort-genai <output>

python -m mobius build --model microsoft/Mage-VL --trust-remote-code \
  --dtype f16 --ep default --runtime ort-genai <output>

Olive

Olive 0.10.2 NF4 quantization completed on the real FP16 decoder in 74.46s, reducing external weights from 8,112,046,080 bytes to 2,204,314,624 bytes. The quantized decoder loaded in ORT CPU, produced finite (1, 3, 151936) logits, and generated:

The capital of France is Paris. The capital of Germany is Berlin

Waivers / known runtime limits

  • ORT GenAI native generation: waived with runtime evidence. ORT GenAI 0.15.2 can parse/load the portable package, but its qwen2_5_vl path supplies 3D MRoPE position_ids; Mage-VL's Qwen3 decoder correctly requires 2D 1D-RoPE positions, so generation fails with Invalid rank for input: position_ids Got: 3 Expected: 2. Mapping to qwen3 loads but expects decoder input_ids, incompatible with the standardized multimodal inputs_embeds pipeline.
  • ORT GenAI native media: waived with schema evidence. Mage-VL requires patch_positions containing original sampled frame indices. ORT GenAI 0.15.2 rejects that vision input mapping as unknown, and its Qwen processor does not produce it. The exporter emits a precise warning and a schema-loadable config; direct Mobius ONNX image/video execution is covered by the passing real CUDA L4/L5 tests.
  • Foundry Local: Foundry Local 0.10.2 is installed, but it uses the same ORT GenAI pipeline and is blocked by the position/media contracts above. A misleading smoke pass is not claimed.
  • Full real-weight FP32/FP16 generation matrix: FP32 and FP16 are covered by independent synthetic numerical parity; FP16 is additionally covered by both full real-weight CLI exports; native BF16 is covered by real CUDA L4/L5. A full FP32 checkpoint run would approximately double the 9.5 GB BF16 checkpoint footprint and was not repeated on this 8 GB GPU.

justinchuby and others added 3 commits August 10, 2026 11:38
Implement the Mage-ViT vision tower, Qwen3 decoder package, explicit sampled-frame RoPE, four-frame packed attention, processor/config extraction, and exact checkpoint weight routing for microsoft/Mage-VL.

Add synthetic multi-dtype parity, batch-safe media embedding coverage, real mixed image/video L4 and 24-token L5 goldens, CUDA and runtime export integration, and multimodal golden infrastructure.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
State the ORT GenAI position and media-input limitations precisely, and resolve lint findings in the synthetic parity reference.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
Apply the repository formatter to the newly tracked model module.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
@justinchuby
justinchuby requested review from a team and a lite review from Copilot August 10, 2026 18:43
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing 78437cd3eda7ff

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 68 68 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 107 107 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 62 62 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 58 58 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 98 98 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 60 60 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 56 56 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 62 62 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 58 58 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 275 275 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 129 129 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 431 431 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 78437cd3eda7ff

Model Sub-model Changes Status
bert (feature-extraction) model 0
falcon model 0
gemma2 model 0
gemma4 (gemma4) decoder 0
gemma4 (gemma4) embedding 0
gemma4 (gemma4) vision_encoder 0
gemma4_text model 0
gpt2 model 0
llama model 0
llama (static-cache) model 0
mamba (ssm-text-generation) model 0
phi3 model 0
phi3 (static-cache) model 0
qwen model 0
qwen (static-cache) model 0
qwen2 model 0
qwen2 (static-cache) model 0
qwen2_moe model 0
qwen2_moe (static-cache) model 0
qwen3 model 0
qwen3 (static-cache) model 0
qwen3_5_moe (hybrid-text-generation) model 0
qwen3_5_text (hybrid-text-generation) model 0
qwen3_5_vl (hybrid-qwen-vl) decoder 0
qwen3_5_vl (hybrid-qwen-vl) embedding 0
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0
qwen3_moe model 0
qwen3_moe (static-cache) model 0
qwen3_next (hybrid-text-generation) model 0
t5 (seq2seq) decoder 0
t5 (seq2seq) encoder 0
whisper (speech-to-text) decoder 0
whisper (speech-to-text) encoder 0

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-class support for the microsoft/Mage-VL (model_type: mage_vl) vision-language model as a standardized 3-model package (decoder / vision_encoder / embedding), including streaming-video-specific vision inputs (patch_positions) and updates to the test + export infrastructure to exercise real image+video pipelines.

Changes:

  • Introduces the Mage-VL model implementation, task split, and HF-config extraction for the custom Mage-ViT vision tower.
  • Extends golden/e2e infrastructure and fixtures to support ordered image+video inputs (including deterministic frame sampling and pixel budgets).
  • Updates ORT GenAI auto-export to propagate trust_remote_code, emit Mage-VL’s processor config filename, and keep configs schema-loadable when the runtime can’t accept patch_positions.

Reviewed changes

Copilot reviewed 28 out of 30 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/weight_alignment_test.py Adds Mage-VL HF checkpoint → package-parameter alignment test.
tests/mage_vl_parity_test.py Adds synthetic parity coverage for Mage-VL vision + embedding behaviors (incl. packed-attn CUDA build).
tests/e2e_golden_test.py Extends VLM golden runner to preprocess videos and cast vision feeds to session input dtypes.
tests/cli_test.py Verifies --trust-remote-code propagates into ORT GenAI config generation.
tests/_test_configs.py Adds tiny mage_vl config to VL test configs.
testdata/golden/vision-language/mage-vl.json Adds Mage-VL golden logits/token IDs fixture.
testdata/golden/vision-language/mage-vl_generation.json Adds Mage-VL golden deterministic generation fixture.
testdata/cases/vision-language/mage-vl.yaml Adds real image+video Mage-VL golden case (CI-skipped) with frame/pixel controls.
testdata/cases/schema.json Adds schema support for videos, video_num_frames, media_max_pixels.
src/mobius/tasks/_vision_language_3model.py Adds MageVLTask vision builder with patch_positions input.
src/mobius/tasks/init.py Exports/registers MageVLTask under task name mage-vl.
src/mobius/models/mage_vl.py Implements Mage-VL decoder, vision encoder, embedding mixer, and weight routing.
src/mobius/models/init.py Publicly exports MageVLForConditionalGeneration.
src/mobius/integrations/ort_genai/auto_export.py Adds mage_vl mapping, trust_remote_code plumb-through, Mage-VL processor filename, and runtime-compat config handling.
src/mobius/integrations/ort_genai/auto_export_test.py Adds tests for Mage-VL processor config emission + trust_remote_code propagation + patch_positions omission in runtime config.
src/mobius/components/init.py Exposes build_packed_token_offset in the public components API.
src/mobius/_testing/torch_reference.py Adds Mage-VL-specific optional import shim for HF remote-code loading in test reference.
src/mobius/_testing/golden.py Extends GoldenTestCase to include video inputs and media pixel/frame controls.
src/mobius/_testing/golden_test.py Updates golden test expectations for new GoldenTestCase fields.
src/mobius/_registry.py Registers mage_vl model_type → Mage-VL module/task association.
src/mobius/_configs/per_model/_mage_vl_vision.py Adds vision-config extraction hook for Mage-VL’s custom Mage-ViT config.
src/mobius/_configs/per_model/init.py Imports the Mage-VL vision hook for registration side effects.
src/mobius/_configs/_sub_configs.py Adds video/vision token IDs + windowing fields to VisionConfig.
src/mobius/_configs/_extractors.py Propagates new shared vision fields into extracted VisionConfig.
src/mobius/_configs/_base.py Adds new top-level multimodal fields and ensures Mage-VL model_type retention in some hierarchical cases.
src/mobius/main.py Propagates CLI --trust-remote-code into ORT GenAI artifact generation.
scripts/generate_golden.py Extends golden generation to include videos + media pixel/frame controls and float-casts logits before extraction.
README.md Lists Mage-VL as a supported multimodal model.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/e2e_golden_test.py
Comment on lines +895 to +916
image_processor = getattr(processor, "image_processor", None)
video_processor = getattr(processor, "video_processor", None)
saved_image_max = getattr(image_processor, "max_pixels", None)
image_size = getattr(image_processor, "size", None)
saved_image_longest = getattr(image_size, "longest_edge", None)
saved_video_max = getattr(video_processor, "max_pixels", None)
try:
if case.media_max_pixels is not None:
if image_processor is not None:
image_processor.max_pixels = case.media_max_pixels
if image_size is not None and saved_image_longest is not None:
image_size.longest_edge = case.media_max_pixels
if video_processor is not None:
video_processor.max_pixels = case.media_max_pixels
processed_pt = processor(**kwargs)
finally:
if image_processor is not None and saved_image_max is not None:
image_processor.max_pixels = saved_image_max
if image_size is not None and saved_image_longest is not None:
image_size.longest_edge = saved_image_longest
if video_processor is not None and saved_video_max is not None:
video_processor.max_pixels = saved_video_max
Comment on lines +482 to +487
if image_processor is not None and saved_image_max is not None:
image_processor.max_pixels = saved_image_max
if image_size is not None and saved_image_longest is not None:
image_size.longest_edge = saved_image_longest
if video_processor is not None and saved_video_max is not None:
video_processor.max_pixels = saved_video_max
Register the public Mage-VL checkpoint as the architecture-validation model so registry coverage and remote-config graph validation include the new architecture.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
@justinchuby

Copy link
Copy Markdown
Member Author

CI triage update

The first CI run on 05b908f had two distinct results:

  • Mage-VL: L1 passed. The general Ubuntu matrix found a missing L2 registry metadata entry (test_model_id), which is Mage-specific; commit 3eda7ff adds microsoft/Mage-VL and the focused coverage plus real remote-config graph validation now pass locally (4 passed and 3 passed, respectively).
  • Bamba: L3 and all three Ubuntu unit-test matrices reported only the same unrelated pre-existing numerical-threshold failures, with unchanged argmax/top-10 behavior:
    • bamba_0: max abs 0.001375, mean abs 0.000201, cosine 0.999997, argmax match True
    • bamba_1: max abs 0.001662, mean abs 0.000172, cosine 0.999996, argmax match True

No Bamba source, configuration, tolerance, or test code is changed by this PR, and I will not modify unrelated Bamba behavior to make this PR green.

Baseline reference: the PR base is 78437cd; its main-branch CI run 31230202537 already fails overall (L4/L5). For precision, that baseline run's L3 job itself passed, so the Bamba result is treated as an unrelated numerical flake rather than claimed as an identical deterministic failure in that specific baseline L3 execution.

The follow-up CI run for 3eda7ff is in progress. Mage-specific L1/L4/L5 results will be recorded when it completes.

@justinchuby

Copy link
Copy Markdown
Member Author

Follow-up CI result (3eda7ff)

The Mage-specific L2 metadata fix is effective:

  • Focused registry coverage: 4 passed.
  • L2 remote config + full-size graph validation for microsoft/Mage-VL: 3 passed.
  • Follow-up CI L1 Smoke Test: passed.
  • All six Linux/Windows unit matrices now report 5,039 passed and only the same two Bamba failures; the prior Mage registry-coverage failures are gone.
  • Follow-up L3 again reports 100 passed, with only bamba_0 and bamba_1 failing at the exact metrics documented above. No Bamba changes were made.

The self-hosted A10 L4/L5 jobs are still queued with no runner assigned, so I am not claiming those CI jobs ran. Mage-specific L4/L5 are nevertheless independently verified by the real nonzero image+five-frame video CUDA runs already detailed in the PR body: BF16 L4 passed in 324.12s and deterministic 24-token BF16 L5 passed in 236.19s on CUDAExecutionProvider.

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