Image-Text-to-Text
MLX
Safetensors
modilify_mk1
diffusion
multimodal
mixture-of-experts
conversational
Instructions to use modilify/Modilify-Mk1-MLX with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use modilify/Modilify-Mk1-MLX with MLX:
# Make sure mlx-vlm is installed # pip install --upgrade mlx-vlm from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template from mlx_vlm.utils import load_config # Load the model model, processor = load("modilify/Modilify-Mk1-MLX") config = load_config("modilify/Modilify-Mk1-MLX") # Prepare input image = ["http://images.cocodataset.org/val2017/000000039769.jpg"] prompt = "Describe this image." # Apply chat template formatted_prompt = apply_chat_template( processor, config, prompt, num_images=1 ) # Generate output output = generate(model, processor, formatted_prompt, image) print(output) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Pi
How to use modilify/Modilify-Mk1-MLX with Pi:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "modilify/Modilify-Mk1-MLX"
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "mlx-lm": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "modilify/Modilify-Mk1-MLX" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Hermes Agent
How to use modilify/Modilify-Mk1-MLX with Hermes Agent:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "modilify/Modilify-Mk1-MLX"
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default modilify/Modilify-Mk1-MLX
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use modilify/Modilify-Mk1-MLX with OpenClaw:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "modilify/Modilify-Mk1-MLX"
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "modilify/Modilify-Mk1-MLX" \ --custom-provider-id mlx-lm \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
| #!/usr/bin/env python3 | |
| # Copyright 2026 Modilify | |
| # SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0 | |
| """Convert an official Modilify Mk1 checkpoint into native MLX shards.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| import mlx.core as mx | |
| ROOT = Path(__file__).resolve().parent | |
| if str(ROOT) not in sys.path: | |
| sys.path.insert(0, str(ROOT)) | |
| from modilify_mlx.config import MODEL_TYPE, TEXT_MODEL_TYPE, ModilifyMk1Config | |
| from modilify_mlx.convert_utils import remap_weight, should_keep_source_key | |
| COPY_PATTERNS = ( | |
| "tokenizer.json", | |
| "tokenizer_config.json", | |
| "chat_template.jinja", | |
| "processor_config.json", | |
| "generation_config.json", | |
| "special_tokens_map.json", | |
| "preprocessor_config.json", | |
| ) | |
| MAX_SHARD_BYTES = 5 * 1024**3 | |
| def _load_json(path: Path) -> dict: | |
| with path.open(encoding="utf-8") as handle: | |
| return json.load(handle) | |
| def _write_config(source: Path, destination: Path) -> None: | |
| payload = _load_json(source / "config.json") | |
| if payload.get("model_type") != MODEL_TYPE: | |
| raise ValueError( | |
| f"Source model_type must be {MODEL_TYPE!r}, got {payload.get('model_type')!r}." | |
| ) | |
| payload.pop("auto_map", None) | |
| payload["architectures"] = ["ModilifyMk1ForBlockDiffusion"] | |
| text = dict(payload.get("text_config") or {}) | |
| text["model_type"] = TEXT_MODEL_TYPE | |
| payload["text_config"] = text | |
| payload["model_type"] = MODEL_TYPE | |
| # Validate through the native config so a bad export fails here. | |
| ModilifyMk1Config.from_dict(payload) | |
| with (destination / "config.json").open("w", encoding="utf-8") as handle: | |
| json.dump(payload, handle, indent=2) | |
| handle.write("\n") | |
| def _copy_sidecar_files(source: Path, destination: Path) -> None: | |
| for name in COPY_PATTERNS: | |
| src = source / name | |
| if src.exists(): | |
| shutil.copy2(src, destination / name) | |
| def _flush_shard( | |
| destination: Path, | |
| shard: dict[str, mx.array], | |
| shard_index: int, | |
| planned_count: int, | |
| weight_map: dict[str, str], | |
| ) -> int: | |
| if not shard: | |
| return shard_index | |
| name = f"model-{shard_index:05d}-of-{planned_count:05d}.safetensors" | |
| mx.save_safetensors( | |
| str(destination / name), | |
| shard, | |
| metadata={"format": "mlx"}, | |
| ) | |
| for key in shard: | |
| weight_map[key] = name | |
| return shard_index + 1 | |
| def convert(source: Path, destination: Path) -> None: | |
| source = source.resolve() | |
| destination = destination.resolve() | |
| destination.mkdir(parents=True, exist_ok=True) | |
| index = _load_json(source / "model.safetensors.index.json") | |
| source_map: dict[str, str] = index["weight_map"] | |
| shard_names = [] | |
| for name in source_map.values(): | |
| if name not in shard_names: | |
| shard_names.append(name) | |
| remapped: dict[str, mx.array] = {} | |
| kept_source = 0 | |
| dropped_source = 0 | |
| for shard_name in shard_names: | |
| print(f"[convert] reading {shard_name}", flush=True) | |
| loaded = mx.load(str(source / shard_name)) | |
| for key, value in loaded.items(): | |
| if not should_keep_source_key(key): | |
| dropped_source += 1 | |
| continue | |
| kept_source += 1 | |
| for new_key, new_value in remap_weight(key, value): | |
| if new_key in remapped: | |
| raise ValueError(f"Duplicate remapped key: {new_key}") | |
| remapped[new_key] = new_value | |
| del loaded | |
| print( | |
| f"[convert] kept {kept_source} source tensors, dropped {dropped_source}, " | |
| f"wrote {len(remapped)} MLX tensors", | |
| flush=True, | |
| ) | |
| latent_keys = [key for key in remapped if key.startswith("latent_deliberation.")] | |
| if len(latent_keys) < 150: | |
| raise RuntimeError( | |
| f"Expected the latent stack to survive conversion, found {len(latent_keys)} keys." | |
| ) | |
| items = sorted(remapped.items()) | |
| shards: list[dict[str, mx.array]] = [] | |
| current: dict[str, mx.array] = {} | |
| current_bytes = 0 | |
| for key, value in items: | |
| tensor_bytes = int(value.nbytes) | |
| if current and current_bytes + tensor_bytes > MAX_SHARD_BYTES: | |
| shards.append(current) | |
| current = {} | |
| current_bytes = 0 | |
| current[key] = value | |
| current_bytes += tensor_bytes | |
| if current: | |
| shards.append(current) | |
| planned = max(1, len(shards)) | |
| weight_map: dict[str, str] = {} | |
| total_size = 0 | |
| for index_i, shard in enumerate(shards, start=1): | |
| print(f"[convert] writing shard {index_i}/{planned}", flush=True) | |
| name = ( | |
| f"model-{index_i:05d}-of-{planned:05d}.safetensors" | |
| if planned > 1 | |
| else "model.safetensors" | |
| ) | |
| mx.save_safetensors( | |
| str(destination / name), | |
| shard, | |
| metadata={"format": "mlx"}, | |
| ) | |
| for key, value in shard.items(): | |
| weight_map[key] = name | |
| total_size += int(value.nbytes) | |
| index_payload = { | |
| "metadata": {"total_size": total_size}, | |
| "weight_map": {key: weight_map[key] for key in sorted(weight_map)}, | |
| } | |
| with (destination / "model.safetensors.index.json").open( | |
| "w", encoding="utf-8" | |
| ) as handle: | |
| json.dump(index_payload, handle, indent=2) | |
| handle.write("\n") | |
| _write_config(source, destination) | |
| _copy_sidecar_files(source, destination) | |
| print(f"[convert] done -> {destination}", flush=True) | |
| print(f"[convert] tensors={len(weight_map)} bytes={total_size}", flush=True) | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument( | |
| "--source", | |
| type=Path, | |
| default=Path.home() / "Modilify-Mk1", | |
| help="Official Mk1 Hugging Face directory", | |
| ) | |
| parser.add_argument( | |
| "--destination", | |
| type=Path, | |
| default=ROOT, | |
| help="Native MLX output directory", | |
| ) | |
| args = parser.parse_args() | |
| convert(args.source, args.destination) | |
| if __name__ == "__main__": | |
| main() | |