From 2d886aeed9200885926ca6ef1af27da64c77238a Mon Sep 17 00:00:00 2001 From: Cryptopoly <31970407+cryptopoly@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:30:24 +0100 Subject: [PATCH] fix(mlx): explain why a Flint checkpoint cannot load instead of 'Model type flint not supported' Recursive's MLX INT4 export stores int8 embeddings in a layout only their own engine reads, and older MLX stacks lack the architecture. The worker now says so and points at the Flint GGUF. --- backend_service/mlx_worker_diagnostics.py | 26 ++++++++++++++++++ tests/test_mlx_models.py | 33 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/backend_service/mlx_worker_diagnostics.py b/backend_service/mlx_worker_diagnostics.py index f9c39362..463ece3d 100644 --- a/backend_service/mlx_worker_diagnostics.py +++ b/backend_service/mlx_worker_diagnostics.py @@ -32,6 +32,30 @@ _UNSUPPORTED_QUANT_ALGOS = {"NVFP4", "NVINT4"} +_FLINT_USE_GGUF = ( + "Use the Flint INT4 GGUF (RecursiveSI/flint-int4-gguf) with the llama.cpp backend instead." +) + + +def _reject_unloadable_flint(model_dir: Path) -> None: + """Say why a Flint checkpoint cannot load on MLX, instead of 'Model type flint not supported'.""" + if (model_dir / "flint-int4.json").exists(): + raise RuntimeError( + "Recursive's MLX INT4 build of Flint stores its embeddings in a layout only their own " + "rec engine reads, so MLX cannot load it. " + _FLINT_USE_GGUF + ) + shipped = [ + name + for name in ("mlx_lm", "mlx_vlm") + if importlib.util.find_spec(f"{name}.models.flint") is not None + ] + if not shipped: + raise RuntimeError( + "The installed MLX packages do not include the Flint architecture " + "(mlx-vlm 0.7.10 or newer does). " + _FLINT_USE_GGUF + ) + + def _reject_unsupported_quant(model_path: str) -> None: """Raise early if the model uses a quantisation format MLX cannot handle.""" cfg_path = Path(model_path) / "config.json" @@ -40,6 +64,8 @@ def _reject_unsupported_quant(model_path: str) -> None: try: with open(cfg_path) as f: cfg = json.load(f) + if cfg.get("model_type") == "flint": + _reject_unloadable_flint(Path(model_path)) qcfg = cfg.get("quantization_config") or {} algo = qcfg.get("quant_algo", "") method = qcfg.get("quant_method", "") diff --git a/tests/test_mlx_models.py b/tests/test_mlx_models.py index 5c2f7687..ad55b0d4 100644 --- a/tests/test_mlx_models.py +++ b/tests/test_mlx_models.py @@ -366,3 +366,36 @@ def apply_chat_template(self, messages, **kwargs): if __name__ == "__main__": unittest.main() + + +class FlintOnMlxTests(unittest.TestCase): + """A Flint checkpoint MLX cannot load fails with a reason and the way out.""" + + def _dir(self, *, marker: bool) -> str: + directory = Path(tempfile.mkdtemp()) + self.addCleanup(lambda: __import__("shutil").rmtree(directory, ignore_errors=True)) + (directory / "config.json").write_text(json.dumps({"model_type": "flint"}), encoding="utf-8") + if marker: + (directory / "flint-int4.json").write_text("{}", encoding="utf-8") + return str(directory) + + def test_recursives_int4_export_points_at_the_gguf(self) -> None: + from backend_service.mlx_worker_diagnostics import _reject_unsupported_quant + + with self.assertRaises(RuntimeError) as ctx: + _reject_unsupported_quant(self._dir(marker=True)) + self.assertIn("RecursiveSI/flint-int4-gguf", str(ctx.exception)) + + def test_a_stack_without_the_architecture_says_so(self) -> None: + from backend_service.mlx_worker_diagnostics import _reject_unsupported_quant + + with mock.patch("importlib.util.find_spec", return_value=None): + with self.assertRaises(RuntimeError) as ctx: + _reject_unsupported_quant(self._dir(marker=False)) + self.assertIn("do not include the Flint architecture", str(ctx.exception)) + + def test_a_stack_that_ships_it_is_left_alone(self) -> None: + from backend_service.mlx_worker_diagnostics import _reject_unsupported_quant + + with mock.patch("importlib.util.find_spec", return_value=object()): + _reject_unsupported_quant(self._dir(marker=False))