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"
Download modilify_mlx/latent_deliberation.py from modilify/Modilify-Mk1-MLX: direct link, hf CLI and curl.
- Browser
- Download file 16.9 kB
-
https://huggingface.co/modilify/Modilify-Mk1-MLX/resolve/main/modilify_mlx/latent_deliberation.py
- Command line
-
hf download hf://modilify/Modilify-Mk1-MLX/modilify_mlx/latent_deliberation.py
-
curl -L -o latent_deliberation.py https://huggingface.co/modilify/Modilify-Mk1-MLX/resolve/main/modilify_mlx/latent_deliberation.py
16.9 kB
| # Copyright 2026 Modilify | |
| # SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0 | |
| """Fixed-shape latent deliberation state for Modilify Mk1 decoding. | |
| PyTorch → MLX: | |
| nn.LayerNorm mlx.nn.LayerNorm | |
| nn.Linear mlx.nn.Linear | |
| nn.MultiheadAttention in_proj BiasedMHA query/key/value_proj (split QKV) | |
| nn.Sequential token_ff/memory_ff mlx.nn.Sequential (layers.0 / layers.2) | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| import math | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| class LatentDeliberationState: | |
| """Persistent, fixed-size state for one or more canvas episodes.""" | |
| token_latents: mx.array | |
| memory_slots: mx.array | |
| confidence: mx.array | |
| entropy: mx.array | |
| age: mx.array | |
| token_changed: mx.array | |
| confidence_delta: mx.array | |
| entropy_delta: mx.array | |
| ponder_steps: mx.array | |
| stagnation_steps: mx.array | |
| def empty( | |
| cls, | |
| *, | |
| batch_size: int, | |
| canvas_length: int, | |
| latent_dim: int, | |
| memory_slots: int, | |
| dtype: mx.Dtype, | |
| ) -> "LatentDeliberationState": | |
| """Create a zero-initialized recurrent state.""" | |
| return cls( | |
| token_latents=mx.zeros((batch_size, canvas_length, latent_dim), dtype=dtype), | |
| memory_slots=mx.zeros((batch_size, memory_slots, latent_dim), dtype=dtype), | |
| confidence=mx.zeros((batch_size, canvas_length), dtype=mx.float32), | |
| entropy=mx.zeros((batch_size, canvas_length), dtype=mx.float32), | |
| age=mx.zeros((batch_size, canvas_length), dtype=mx.int32), | |
| token_changed=mx.zeros((batch_size, canvas_length), dtype=mx.float32), | |
| confidence_delta=mx.zeros((batch_size, canvas_length), dtype=mx.float32), | |
| entropy_delta=mx.zeros((batch_size, canvas_length), dtype=mx.float32), | |
| ponder_steps=mx.zeros((batch_size,), dtype=mx.int32), | |
| stagnation_steps=mx.zeros((batch_size,), dtype=mx.int32), | |
| ) | |
| def shift( | |
| self, committed: int, *, entropy_fill_value: float = 0.0 | |
| ) -> "LatentDeliberationState": | |
| """Drop committed canvas positions without changing long-term memory.""" | |
| canvas_length = self.token_latents.shape[1] | |
| if not 0 <= committed <= canvas_length: | |
| raise ValueError("`committed` must be in [0, canvas_length].") | |
| if committed == 0: | |
| return self | |
| def shifted(tensor: mx.array, fill_value: float | int = 0) -> mx.array: | |
| result = mx.full(tensor.shape, fill_value, dtype=tensor.dtype) | |
| if committed < canvas_length: | |
| kept = tensor[:, committed:] | |
| result = mx.concatenate( | |
| [kept, mx.full( | |
| (tensor.shape[0], committed, *tensor.shape[2:]), | |
| fill_value, | |
| dtype=tensor.dtype, | |
| )], | |
| axis=1, | |
| ) if tensor.ndim > 2 else mx.concatenate( | |
| [ | |
| kept, | |
| mx.full( | |
| (tensor.shape[0], committed), | |
| fill_value, | |
| dtype=tensor.dtype, | |
| ), | |
| ], | |
| axis=1, | |
| ) | |
| return result | |
| return LatentDeliberationState( | |
| token_latents=shifted(self.token_latents), | |
| memory_slots=self.memory_slots, | |
| confidence=shifted(self.confidence), | |
| entropy=shifted(self.entropy, entropy_fill_value), | |
| age=shifted(self.age), | |
| token_changed=shifted(self.token_changed), | |
| confidence_delta=shifted(self.confidence_delta), | |
| entropy_delta=shifted(self.entropy_delta), | |
| ponder_steps=mx.zeros_like(self.ponder_steps), | |
| stagnation_steps=mx.zeros_like(self.stagnation_steps), | |
| ) | |
| def advance_trajectory_clocks( | |
| ponder_steps: mx.array, | |
| stagnation_steps: mx.array, | |
| *, | |
| commit_lengths: mx.array, | |
| active_rows: mx.array, | |
| progress_scores: mx.array, | |
| min_progress: float, | |
| ) -> tuple[mx.array, mx.array]: | |
| """Advance useful-ponder and true-stagnation clocks for each row.""" | |
| if min_progress < 0: | |
| raise ValueError("`min_progress` must be non-negative.") | |
| if not ( | |
| ponder_steps.shape | |
| == stagnation_steps.shape | |
| == commit_lengths.shape | |
| == active_rows.shape | |
| == progress_scores.shape | |
| ): | |
| raise ValueError("Trajectory clock inputs must share shape [batch].") | |
| committed = commit_lengths > 0 | |
| waiting = active_rows & (~committed) | |
| improving = progress_scores >= min_progress | |
| next_ponder = mx.where( | |
| committed, | |
| mx.zeros_like(ponder_steps), | |
| ponder_steps + waiting.astype(mx.int32), | |
| ) | |
| next_stagnation = mx.where( | |
| committed, | |
| mx.zeros_like(stagnation_steps), | |
| mx.where( | |
| waiting & improving, | |
| mx.zeros_like(stagnation_steps), | |
| stagnation_steps + waiting.astype(mx.int32), | |
| ), | |
| ) | |
| return next_ponder.astype(mx.int32), next_stagnation.astype(mx.int32) | |
| def should_force_trajectory_jump( | |
| ponder_steps: mx.array, | |
| stagnation_steps: mx.array, | |
| *, | |
| max_ponder_steps: int, | |
| stagnation_threshold: int, | |
| ) -> mx.array: | |
| """Return whether a row has exhausted ponder or stagnation budget.""" | |
| if max_ponder_steps <= 0 or stagnation_threshold <= 0: | |
| raise ValueError("Trajectory jump limits must be positive.") | |
| return (ponder_steps >= max_ponder_steps) | ( | |
| stagnation_steps >= stagnation_threshold | |
| ) | |
| class BiasedMHA(nn.Module): | |
| """Explicit QKV attention matching PyTorch MultiheadAttention weights.""" | |
| def __init__(self, dims: int, num_heads: int) -> None: | |
| super().__init__() | |
| if dims % num_heads: | |
| raise ValueError("`dims` must be divisible by `num_heads`.") | |
| self.num_heads = num_heads | |
| self.head_dim = dims // num_heads | |
| self.scale = self.head_dim**-0.5 | |
| self.query_proj = nn.Linear(dims, dims, bias=True) | |
| self.key_proj = nn.Linear(dims, dims, bias=True) | |
| self.value_proj = nn.Linear(dims, dims, bias=True) | |
| self.out_proj = nn.Linear(dims, dims, bias=True) | |
| def __call__( | |
| self, | |
| queries: mx.array, | |
| keys: mx.array, | |
| values: mx.array, | |
| mask: mx.array | None = None, | |
| ) -> mx.array: | |
| batch, query_len, dims = queries.shape | |
| key_len = keys.shape[1] | |
| queries = self.query_proj(queries).reshape( | |
| batch, query_len, self.num_heads, self.head_dim | |
| ).transpose(0, 2, 1, 3) | |
| keys = self.key_proj(keys).reshape( | |
| batch, key_len, self.num_heads, self.head_dim | |
| ).transpose(0, 2, 1, 3) | |
| values = self.value_proj(values).reshape( | |
| batch, key_len, self.num_heads, self.head_dim | |
| ).transpose(0, 2, 1, 3) | |
| attn_mask = None | |
| if mask is not None: | |
| attn_mask = mask.reshape(1, 1, mask.shape[-2], mask.shape[-1]) | |
| output = mx.fast.scaled_dot_product_attention( | |
| queries, keys, values, scale=self.scale, mask=attn_mask | |
| ) | |
| output = output.transpose(0, 2, 1, 3).reshape(batch, query_len, dims) | |
| return self.out_proj(output) | |
| def _silu(x: mx.array) -> mx.array: | |
| return x * mx.sigmoid(x) | |
| class _TemporalTransformerCell(nn.Module): | |
| """One-step recurrent token update with fixed-slot memory attention.""" | |
| def __init__( | |
| self, | |
| latent_dim: int, | |
| num_heads: int, | |
| local_attention_window: int, | |
| ) -> None: | |
| super().__init__() | |
| self.state_norm = nn.LayerNorm(latent_dim) | |
| self.observation_norm = nn.LayerNorm(latent_dim) | |
| self.memory_address_norm = nn.LayerNorm(latent_dim) | |
| self.memory_value_norm = nn.LayerNorm(latent_dim) | |
| self.temporal_update = nn.Linear(2 * latent_dim, 2 * latent_dim) | |
| self.local_attention = BiasedMHA(latent_dim, num_heads) | |
| self.local_attention_window = local_attention_window | |
| self.token_memory_attention = BiasedMHA(latent_dim, num_heads) | |
| self.memory_token_attention = BiasedMHA(latent_dim, num_heads) | |
| self.token_ff_norm = nn.LayerNorm(latent_dim) | |
| self.memory_ff_norm = nn.LayerNorm(latent_dim) | |
| self.stored_token_norm = nn.LayerNorm(latent_dim) | |
| self.stored_memory_norm = nn.LayerNorm(latent_dim) | |
| expansion = latent_dim * 4 | |
| self.token_ff = nn.Sequential( | |
| nn.Linear(latent_dim, expansion), | |
| nn.SiLU(), | |
| nn.Linear(expansion, latent_dim), | |
| ) | |
| self.memory_ff = nn.Sequential( | |
| nn.Linear(latent_dim, expansion), | |
| nn.SiLU(), | |
| nn.Linear(expansion, latent_dim), | |
| ) | |
| def _local_mask(self, length: int, dtype: mx.Dtype) -> mx.array: | |
| positions = mx.arange(length) | |
| allowed = ( | |
| mx.abs(positions[:, None] - positions[None, :]) < self.local_attention_window | |
| ) | |
| return mx.where( | |
| allowed, | |
| mx.zeros((length, length), dtype=dtype), | |
| mx.full((length, length), mx.finfo(dtype).min, dtype=dtype), | |
| ) | |
| def __call__( | |
| self, | |
| previous_tokens: mx.array, | |
| observation: mx.array, | |
| memory: mx.array, | |
| memory_slot_identity: mx.array, | |
| ) -> tuple[mx.array, mx.array]: | |
| gate_logits, candidate = mx.split( | |
| self.temporal_update( | |
| mx.concatenate( | |
| ( | |
| self.state_norm(previous_tokens), | |
| self.observation_norm(observation), | |
| ), | |
| axis=-1, | |
| ) | |
| ), | |
| 2, | |
| axis=-1, | |
| ) | |
| gate = mx.sigmoid(gate_logits) | |
| tokens = gate * previous_tokens + (1.0 - gate) * _silu(candidate) | |
| local_mask = self._local_mask(tokens.shape[1], tokens.dtype) | |
| normed_tokens = self.state_norm(tokens) | |
| tokens = tokens + self.local_attention( | |
| normed_tokens, normed_tokens, normed_tokens, mask=local_mask | |
| ) | |
| addressed_memory = self.memory_address_norm(memory + memory_slot_identity) | |
| memory_values = self.memory_value_norm(memory) | |
| normed_tokens = self.state_norm(tokens) | |
| tokens = tokens + self.token_memory_attention( | |
| normed_tokens, addressed_memory, memory_values | |
| ) | |
| tokens = tokens + self.token_ff(self.token_ff_norm(tokens)) | |
| normed_tokens = self.state_norm(tokens) | |
| memory = memory + self.memory_token_attention( | |
| addressed_memory, normed_tokens, normed_tokens | |
| ) | |
| memory = memory + self.memory_ff(self.memory_ff_norm(memory)) | |
| return ( | |
| self.stored_token_norm(tokens), | |
| self.stored_memory_norm(memory) + memory_slot_identity, | |
| ) | |
| class LatentDeliberationTransformer(nn.Module): | |
| """Small recurrent Transformer that compresses repeated denoise context.""" | |
| def __init__( | |
| self, | |
| *, | |
| hidden_size: int, | |
| latent_dim: int = 512, | |
| memory_slots: int = 16, | |
| num_layers: int = 2, | |
| num_heads: int = 8, | |
| local_attention_window: int = 32, | |
| dropout: float = 0.0, | |
| ) -> None: | |
| super().__init__() | |
| del dropout | |
| if latent_dim % num_heads: | |
| raise ValueError("`latent_dim` must be divisible by `num_heads`.") | |
| if local_attention_window <= 0: | |
| raise ValueError("`local_attention_window` must be positive.") | |
| self.hidden_size = hidden_size | |
| self.latent_dim = latent_dim | |
| self.memory_slots = memory_slots | |
| self.heavy_projection = nn.Linear(hidden_size, latent_dim, bias=False) | |
| self.embedding_projection = nn.Linear(hidden_size, latent_dim, bias=False) | |
| self.scalar_projection = nn.Linear(11, latent_dim, bias=False) | |
| self.blocks = [ | |
| _TemporalTransformerCell(latent_dim, num_heads, local_attention_window) | |
| for _ in range(num_layers) | |
| ] | |
| self.output_norm = nn.LayerNorm(latent_dim) | |
| self.output_projection = nn.Linear(latent_dim, hidden_size, bias=False) | |
| self.memory_slot_identity = mx.zeros((memory_slots, latent_dim)) | |
| def scaled_memory_slot_identity( | |
| self, | |
| *, | |
| batch_size: int, | |
| dtype: mx.Dtype, | |
| ) -> mx.array: | |
| """Return unit directions scaled to LayerNorm RMS.""" | |
| identity = self.memory_slot_identity.astype(mx.float32) | |
| identity = identity / mx.maximum( | |
| mx.linalg.norm(identity, axis=-1, keepdims=True), 1.0e-12 | |
| ) | |
| identity = identity * math.sqrt(self.latent_dim) | |
| return mx.broadcast_to( | |
| identity.astype(dtype)[None, :, :], | |
| (batch_size, self.memory_slots, self.latent_dim), | |
| ) | |
| def project_context(self, token_latents: mx.array) -> mx.array: | |
| """Translate latent state into a self-conditioning embedding.""" | |
| return self.output_projection(self.output_norm(token_latents)) | |
| def __call__( | |
| self, | |
| *, | |
| heavy_hidden: mx.array, | |
| token_embeddings: mx.array, | |
| confidence: mx.array, | |
| entropy: mx.array, | |
| state: LatentDeliberationState, | |
| ) -> tuple[mx.array, LatentDeliberationState]: | |
| """Advance latent memory and produce decoder self-conditioning.""" | |
| if heavy_hidden.ndim != 3: | |
| raise ValueError("`heavy_hidden` must have shape [batch, canvas, hidden].") | |
| if heavy_hidden.shape != token_embeddings.shape: | |
| raise ValueError( | |
| "`heavy_hidden` and `token_embeddings` must have the same shape." | |
| ) | |
| batch_size, canvas_length, hidden_size = heavy_hidden.shape | |
| if hidden_size != self.hidden_size: | |
| raise ValueError("Unexpected hidden size for latent deliberation.") | |
| expected_state = (batch_size, canvas_length, self.latent_dim) | |
| if state.token_latents.shape != expected_state: | |
| raise ValueError("State token latents do not match the current canvas.") | |
| if state.memory_slots.shape != ( | |
| batch_size, | |
| self.memory_slots, | |
| self.latent_dim, | |
| ): | |
| raise ValueError("State memory slots do not match this module.") | |
| dtype = heavy_hidden.dtype | |
| position = mx.linspace(-1.0, 1.0, canvas_length, dtype=dtype) | |
| position = mx.broadcast_to(position[None, :], (batch_size, canvas_length)) | |
| age = mx.minimum(state.age.astype(dtype), 32767).astype(dtype) | |
| entropy_delta = state.entropy_delta.astype(dtype) | |
| scalars = mx.stack( | |
| ( | |
| confidence.astype(dtype), | |
| mx.log1p(entropy.astype(dtype)), | |
| mx.log1p(age), | |
| position, | |
| state.token_changed.astype(dtype), | |
| state.confidence_delta.astype(dtype), | |
| mx.sign(entropy_delta) * mx.log1p(mx.abs(entropy_delta)), | |
| mx.broadcast_to( | |
| mx.log1p(state.ponder_steps.astype(dtype))[:, None], | |
| (batch_size, canvas_length), | |
| ), | |
| mx.broadcast_to( | |
| mx.log1p(state.stagnation_steps.astype(dtype))[:, None], | |
| (batch_size, canvas_length), | |
| ), | |
| confidence.astype(dtype) | |
| * mx.exp(-mx.maximum(entropy.astype(dtype), 0.0)), | |
| mx.maximum(state.confidence_delta.astype(dtype), 0.0) | |
| + mx.log1p(mx.maximum(-entropy_delta, 0.0)), | |
| ), | |
| axis=-1, | |
| ) | |
| observation = ( | |
| self.heavy_projection(heavy_hidden) | |
| + self.embedding_projection(token_embeddings) | |
| + self.scalar_projection(scalars) | |
| ) | |
| tokens = state.token_latents | |
| memory = state.memory_slots | |
| slot_identity = self.scaled_memory_slot_identity( | |
| batch_size=batch_size, | |
| dtype=memory.dtype, | |
| ) | |
| for block in self.blocks: | |
| tokens, memory = block(tokens, observation, memory, slot_identity) | |
| observation = tokens | |
| next_state = LatentDeliberationState( | |
| token_latents=tokens, | |
| memory_slots=memory, | |
| confidence=confidence.astype(mx.float32), | |
| entropy=entropy.astype(mx.float32), | |
| age=state.age, | |
| token_changed=state.token_changed, | |
| confidence_delta=state.confidence_delta, | |
| entropy_delta=state.entropy_delta, | |
| ponder_steps=state.ponder_steps, | |
| stagnation_steps=state.stagnation_steps, | |
| ) | |
| return self.project_context(tokens), next_state | |