Summary
For a text-only model, mlx_engine/generate.py's model-kit selection dispatches purely on "vision_config" in config_json (around load_model, the is_batchable() branch). If that key is absent, it unconditionally calls mlx_lm_load(model_path, lazy=True) — even when the architecture in question has no mlx_lm implementation at all, but does have one in mlx_vlm, bundled in the exact same environment.
Concretely, for deepseek_v4 (a genuinely text-only architecture — no vision component): the bundled environment's mlx_lm/models/ stops at deepseek_v32, so loading fails outright. The same bundled environment's mlx_vlm/models/deepseek_v4/ is fully implemented (including an MTP speculative-decoding drafter), but it's never reached, because the dispatch never considers mlx_vlm for a model that lacks vision_config.
Repro
Load any local MLX checkpoint with model_type: deepseek_v4 (text-only, no vision_config key) via lms load or the GUI.
Result:
ValueError: Model type deepseek_v4 not supported.
At:
.../mlx_lm/utils.py(192): _get_classes
.../mlx_lm/utils.py(348): load_model
.../mlx_lm/utils.py(509): load
.../mlx_engine/generate.py(289): is_batchable
Workaround found
Adding an empty "vision_config": {} to the checkpoint's config.json routes the same load through the BatchedVisionModelKit → mlx_vlm path instead, which has the architecture and loads/generates correctly (confirmed end-to-end, including at extended context lengths). mlx_vlm.utils.load_image_processor degrades gracefully for a model class with no ImageProcessor attribute, so this doesn't require faking any real vision capability — it's purely a dispatch trick, and a fragile one for users who don't know to do it.
Suggested fix
Before deciding "no vision_config → use mlx_lm", check whether mlx_lm actually has an implementation for the model's model_type (a cheap _get_classes-style lookup) and fall back to mlx_vlm if not, rather than dispatching solely on the presence of a config key that's orthogonal to which package actually implements the architecture. This matters more as new architectures increasingly land in mlx_vlm first even when text-only.
Environment
Summary
For a text-only model,
mlx_engine/generate.py's model-kit selection dispatches purely on"vision_config" in config_json(aroundload_model, theis_batchable()branch). If that key is absent, it unconditionally callsmlx_lm_load(model_path, lazy=True)— even when the architecture in question has nomlx_lmimplementation at all, but does have one inmlx_vlm, bundled in the exact same environment.Concretely, for
deepseek_v4(a genuinely text-only architecture — no vision component): the bundled environment'smlx_lm/models/stops atdeepseek_v32, so loading fails outright. The same bundled environment'smlx_vlm/models/deepseek_v4/is fully implemented (including an MTP speculative-decoding drafter), but it's never reached, because the dispatch never considersmlx_vlmfor a model that lacksvision_config.Repro
Load any local MLX checkpoint with
model_type: deepseek_v4(text-only, novision_configkey) vialms loador the GUI.Result:
Workaround found
Adding an empty
"vision_config": {}to the checkpoint'sconfig.jsonroutes the same load through theBatchedVisionModelKit→mlx_vlmpath instead, which has the architecture and loads/generates correctly (confirmed end-to-end, including at extended context lengths).mlx_vlm.utils.load_image_processordegrades gracefully for a model class with noImageProcessorattribute, so this doesn't require faking any real vision capability — it's purely a dispatch trick, and a fragile one for users who don't know to do it.Suggested fix
Before deciding "no
vision_config→ usemlx_lm", check whethermlx_lmactually has an implementation for the model'smodel_type(a cheap_get_classes-style lookup) and fall back tomlx_vlmif not, rather than dispatching solely on the presence of a config key that's orthogonal to which package actually implements the architecture. This matters more as new architectures increasingly land inmlx_vlmfirst even when text-only.Environment
app-mlx-generate-mac26-arm64@33deepseek-ai/DeepSeek-V4-Flash(deepseek_v4architecture, text-only)