feat: MLX provider family — mlx_image + mlx_video + mlx_caption (shared env)#311
feat: MLX provider family — mlx_image + mlx_video + mlx_caption (shared env)#311ziyu4huang wants to merge 4 commits into
Conversation
…ontage Add the first Apple-Silicon-native image provider. `mlx_image` shells to the sibling mlx-movie-director repo's `run.py image` (Z-Image / Flux2 Klein / Lens) and is auto-discovered by `image_selector` with zero selector edits — the selector-plus-provider seam from the story→image review. Closes OpenMontage's biggest verified image gap: a local, $0 path that advertises the full control surface (controlnet / img2img / reference_image / faceswap / lora / multi_lora) that today only grok_image's edit mode partially covers. The scorer rewards it accordingly — it ranks calesthio#1 by weighted score (control=1.0, cost_efficiency=1.0) and survives the edit-mode candidate filter. - tools/graphics/mlx_image.py: provider; action routing (t2i/i2i/controlnet/ faceswap), CLI arg mapping, availability gate (arm64 + MLX_MOVIE_DIRECTOR_DIR + venv + models), JSON_SUMMARY output parsing with dir-scan fallback. - .agents/skills/mlx-movie-director/SKILL.md: Layer-3 bridge (env contract, pipeline choice, controlnet/i2i/faceswap mapping, LoRA stacks). - tests/tools/test_mlx_image.py: 24 tests — registration, capability surface, zero-edit discovery, edit-filter survival, gate (env/venv/full), action routing, arg building, mocked-subprocess execute success + error paths. - docs/REVIEW-story-to-image.md, docs/REVIEW-image-to-video-voice.md: the grounded reviews that motivated this provider (gap analysis + MLX side-by-side). Verified end-to-end: a real Z-Image generation (828 KB PNG, 17.6s, $0) flows through image_selector -> mlx_image -> run.py and lands at output_path with asset_manifest-ready provenance (source_tool=mlx_image, model=mlx-zimage, seed). Two upstream MLX-repo deps (opencv-python, mflux) are missing from its requirements.txt — noted in install_instructions + the skill (tracked upstream). Co-Authored-By: Claude <noreply@anthropic.com>
The motion stitch of the OM↔MLX seam. `mlx_video` wraps LTX-2.3 22B (`run.py video generate` / `video t2i2v`) and is auto-discovered by `video_selector` with zero selector edits. Refined value (REVIEW-image-to-video-voice §7): OM's four $0 local i2v providers (wan/hunyuan/ltx/cogvideo) need CUDA diffusers and don't run on Apple Silicon. MLX LTX-2.3 is the MPS-native i2v/t2v path — a free offline local default, NOT a premium-cinema replacement (deliberately does NOT advertise cinematic_quality/lip_sync/multi_shot/native_audio). - tools/video/mlx_video.py: provider; action routing (prompt-only → t2i2v 3-stage; image present → generate I2V; explicit action override), LTX-2.3 arg mapping (frames 8k+1, /64 dims), availability gate reusing the mlx_image pattern, dir-scan output fallback (video subparser doesn't register --json-summary — an upstream MLX-repo asymmetry, documented). fallback_tools excludes image_selector so a motion-required brief can never silently degrade to a still. - .agents/skills/mlx-movie-director/SKILL.md: video section (action routing table, honest surface, motion-required governance note). - tests/tools/test_mlx_video.py: 25 tests — registration, honest-surface assertion (premium flags MUST be absent), zero-edit discovery, i2v filter survival, gate (env/venv/full), action routing, arg building, output parsing, mocked-subprocess execute success + error paths. End-to-end smoke status: DEFERRED to G1. The MLX video path needs a heavier upstream dep stack than requirements.txt provides — cv2, mflux, and the three ltx-2-mlx workspace members (ltx-core-mlx / ltx-pipelines-mlx / ltx-trainer, installable per-member since the ltx-2-mlx root fails uv install under PEP 639), plus a transformers/mlx_lm version conflict at the NewlineTokenizer register call. The provider logic is fully covered by mocked-subprocess tests; the image analog (mlx_image) is smoked end-to-end. install_instructions documents the full chain. Co-Authored-By: Claude <noreply@anthropic.com>
…LX env The third leg of the MLX-runtime trio (image gen + video gen + VLM analysis), all bridging the sibling mlx-movie-director run.py on Apple Silicon. mlx_caption (tools/analysis/mlx_caption.py): - Bridges run.py `caption` (Qwen3-VL via LM Studio localhost:1234/v1) into the `analysis` capability — the $0, offline, private image/video-understanding path. Fills a verified gap: no tool used local LM Studio before (video_understand is a direct-transformers VLM that downloads+loads a model each run; mlx_caption talks to an already-running server the box already serves mlx_image/video from). - Auto-discovered by the registry (13th analysis tool, zero selector edits). - Availability gate: MLX_MOVIE_DIRECTOR_DIR + venv + LM Studio socket-probe (need_models=False — caption talks to LM Studio, not the mlx-models stack). - 21 spawn-free tests (capability surface, env gates incl. LM Studio down, arg-mapping for all --style/--lang/--model/--api-url combos, output parsing). Shared env module (tools/_mlx/env.py): - Factors the duplicated _resolve_env() out of mlx_image + mlx_video (the TODO both files carried: 'if a third MLX provider appears, factor into tools/_mlx/env.py'). resolve_mlx_env(need_models, need_lm_studio) serves all three. All 49 existing mlx_image/mlx_video tests still green.
calesthio
left a comment
There was a problem hiding this comment.
Thank you for this contribution. The MLX provider family is directionally valuable for OpenMontage, especially as a $0 Apple-Silicon-native path for image/video generation and local VLM analysis, and the provider metadata/skill linkage looks generally aligned with the registry model.
I need to request changes because the advertised spawn-free test set is not currently portable: on this Windows/AMD64 review machine, the focused MLX tests fail before merge.
Verification I ran:
python -m py_compile tools/_mlx/env.py tools/analysis/mlx_caption.py tools/graphics/mlx_image.py tools/video/mlx_video.py tests/tools/test_mlx_caption.py tests/tools/test_mlx_image.py tests/tools/test_mlx_video.py
# passed
python -m pytest tests/tools/test_mlx_caption.py tests/tools/test_mlx_image.py tests/tools/test_mlx_video.py -q
# 64 passed, 4 skipped, 2 failed
The failing tests are:
tests/tools/test_mlx_image.py::test_status_available_with_full_env
tests/tools/test_mlx_video.py::test_status_available_with_full_env
Both build a fake complete MLX env and then assert env["ok"] is True, but resolve_mlx_env() correctly rejects non-Apple-Silicon hosts with MLX runs on Apple Silicon only (got AMD64). Please make these tests explicitly mock the platform/architecture when asserting the arm64-available path, or otherwise skip/branch the ok=True assertion on non-arm64 CI while still testing the unavailable path.
I did confirm the registry sees mlx_image, mlx_video, and mlx_caption in their intended capability buckets. I did not find an actionable security issue in the reviewed diff, but the focused test suite needs to be green before merge.
The two test_status_available_with_full_env tests asserted env["ok"] is True unconditionally, but resolve_mlx_env() correctly rejects non-Apple-Silicon hosts (MLX runs on Apple Silicon only). So both tests failed on the Windows/ AMD64 review machine and on ubuntu x86_64 CI — the production code was right, the tests were not portable. Mock platform.machine() -> "arm64" inside these two tests so the full available-path (including the arch guard) executes on ANY host, and drop the now-stale `if env["arm64"]` skip-guard in favor of direct assertions. The unavailable-path tests stay honest (they don't force arch). Verified: - real arm64: 70 passed (3 MLX suites) - simulated AMD64: 66 passed, 4 skipped (unrelated caption/LM Studio), 0 failed — incl. both previously-failing tests green under the simulated host Addresses calesthio review on calesthio#311 (CHANGES_REQUESTED). Co-Authored-By: Claude <noreply@anthropic.com>
|
Thanks for the careful review @calesthio — you're right, the production Change (test-only; # Mock Apple Silicon so the full available-path (incl. the arch guard in
# resolve_mlx_env) runs on ANY CI host, not just native arm64 reviewers.
monkeypatch.setattr("tools._mlx.env.platform.machine", lambda: "arm64")…then assert I went with mocking rather than Verification. This Mac is arm64, so I couldn't natively reproduce your AMD64 failure — I simulated the review host by rebindng The previously-failing tests are green under the simulated non-arm64 host; the focused suite is clean. CI (ubuntu x86_64, |
Bridges the sibling
mlx-movie-directorApple-Silicon runtime into OpenMontage as the $0, offline, MPS-native option where existing local tools need CUDA. Three providers, one shared env module.The trio
mlx_image(graphics)run.py imaget2i/i2i/controlnet/faceswap/LoRAmlx_video(video)run.py videogenerate/t2i2v (LTX-2.3)mlx_caption(analysis)run.py caption(Qwen3-VL via LM Studio localhost:1234)Shared env module (
tools/_mlx/env.py)Factors the duplicated
_resolve_env()out of mlx_image + mlx_video (the TODO both files carried: “if a third MLX provider appears, factor into tools/_mlx/env.py”).resolve_mlx_env(need_models, need_lm_studio)serves all three; mlx_caption usesneed_models=False, need_lm_studio=True(socket-probes localhost:1234 so the menu shows ANALYZE::mlx unavailable when LM Studio isn't running).Env contract
MLX_MOVIE_DIRECTOR_DIR— repo root withpython/mlx-movie-director/run.pyMLX_VENV_PYTHON— the MLX venv interpreter (per-machine, not auto-created)http://localhost:1234/v1with a vision model (caption only)Tests
tests/tools/pass, 0 regressions.--style/--lang/--model/--api-urlcombos, output parsing (styles-map + flat-legacy + dict-with-text).Known follow-ups (tracked in the repo's next-goal-md)
restore/quality/expansion; mlx_video doesn't exposerelay/segment.