Image-Text-to-Text
MLX
Safetensors
inkling_mm_model
inkling
Mixture of Experts
multimodal
text-generation
apple-silicon
conversational
Instructions to use mlx-community/Inkling-Small-mlx-2bit with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use mlx-community/Inkling-Small-mlx-2bit 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("mlx-community/Inkling-Small-mlx-2bit") config = load_config("mlx-community/Inkling-Small-mlx-2bit") # 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 mlx-community/Inkling-Small-mlx-2bit with Pi:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "mlx-community/Inkling-Small-mlx-2bit"
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "mlx-lm": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "mlx-community/Inkling-Small-mlx-2bit" } ] } } }Run Pi
# Start Pi in your project directory: pi
- OpenClaw new
How to use mlx-community/Inkling-Small-mlx-2bit with OpenClaw:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "mlx-community/Inkling-Small-mlx-2bit"
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 "mlx-community/Inkling-Small-mlx-2bit" \ --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"
- Hermes Agent
How to use mlx-community/Inkling-Small-mlx-2bit 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 "mlx-community/Inkling-Small-mlx-2bit"
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 mlx-community/Inkling-Small-mlx-2bit
Run Hermes
hermes
| """Inkling MLP variants: dense SwiGLU (with a learned output scale) and the | |
| sparse MoE (sigmoid router with correction bias, softmax-over-selected weights, | |
| route/global scaling, and 2 always-on shared experts forming a routing "sink"). | |
| Mirrors ``InklingMLP`` / ``InklingTopkRouter`` / ``InklingExperts`` / | |
| ``InklingSharedExperts`` / ``InklingMoE`` from transformers PR #47347. | |
| """ | |
| from __future__ import annotations | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| from mlx_lm.models.switch_layers import SwitchGLU | |
| from .config import TextConfig | |
| class DenseMLP(nn.Module): | |
| """SwiGLU MLP with a learned scalar output gain (``global_scale``). | |
| The checkpoint fuses gate+up into ``w13_dn``; the converter splits it into | |
| ``gate_proj``/``up_proj`` so the standard MLX quantizer sees plain ``nn.Linear``s. | |
| """ | |
| def __init__(self, config: TextConfig): | |
| super().__init__() | |
| h = config.hidden_size | |
| inter = config.dense_intermediate_size | |
| self.gate_proj = nn.Linear(h, inter, bias=False) | |
| self.up_proj = nn.Linear(h, inter, bias=False) | |
| self.down_proj = nn.Linear(inter, h, bias=False) | |
| self.global_scale = mx.ones((1,)) | |
| def __call__(self, x: mx.array) -> mx.array: | |
| y = self.down_proj(nn.silu(self.gate_proj(x)) * self.up_proj(x)) | |
| return y * self.global_scale | |
| class Router(nn.Module): | |
| """Sigmoid top-k router with a correction bias and a shared-expert sink. | |
| Kept in full precision (tiny). Returns per-token routed weights/indices plus | |
| the two shared-expert gammas produced by the same softmax (the "sink"). | |
| """ | |
| def __init__(self, config: TextConfig): | |
| super().__init__() | |
| self.num_experts = config.n_routed_experts | |
| self.n_shared = config.n_shared_experts | |
| self.n_total = self.num_experts + self.n_shared | |
| self.top_k = config.num_experts_per_tok | |
| self.route_scale = config.route_scale | |
| self.hidden = config.hidden_size | |
| self.weight = mx.zeros((self.n_total, config.hidden_size)) | |
| self.bias = mx.zeros((self.num_experts,)) # e_score_correction_bias | |
| self.global_scale = mx.ones((1,)) | |
| def __call__(self, x: mx.array): | |
| # Routing (esp. the top-k selection) is precision-sensitive: in bf16 the | |
| # rounding of near-tied expert scores flips which experts fire, and a wrong | |
| # choice compounds over 64 MoE layers into incoherent output. Compute the | |
| # whole router in fp32. | |
| flat = x.reshape(-1, self.hidden).astype(mx.float32) | |
| router_logits = flat @ self.weight.T.astype(mx.float32) # [T, n_total] | |
| scores = mx.sigmoid(router_logits) | |
| routed_scores = scores[:, : self.num_experts] | |
| scores_for_choice = routed_scores + self.bias | |
| # top-k experts (order within the top-k is irrelevant downstream) | |
| topk_idx = mx.argpartition(-scores_for_choice, kth=self.top_k - 1, axis=-1)[:, : self.top_k] | |
| routed_logits = router_logits[:, : self.num_experts] | |
| shared_logits = router_logits[:, self.num_experts :] # [T, n_shared] | |
| gathered = mx.take_along_axis(routed_logits, topk_idx, axis=-1) # [T, top_k] | |
| topk_logits = mx.concatenate([gathered, shared_logits], axis=-1) # [T, top_k+n_shared] | |
| # softmax over the selected (+shared) logits, computed in the log domain | |
| log_probs = -mx.logaddexp(mx.zeros_like(topk_logits), -topk_logits) # logsigmoid | |
| weights = mx.softmax(log_probs, axis=-1) | |
| weights = weights * self.route_scale * self.global_scale | |
| shared_gammas = weights[:, self.top_k :].astype(x.dtype) # [T, n_shared] | |
| topk_weights = weights[:, : self.top_k].astype(x.dtype) # [T, top_k] | |
| return topk_weights, topk_idx, shared_gammas | |
| class MoE(nn.Module): | |
| def __init__(self, config: TextConfig): | |
| super().__init__() | |
| self.config = config | |
| self.n_shared = config.n_shared_experts | |
| self.gate = Router(config) | |
| self.experts = SwitchGLU( | |
| config.hidden_size, config.moe_intermediate_size, config.n_routed_experts, bias=False | |
| ) | |
| self.shared_experts = SwitchGLU( | |
| config.hidden_size, config.moe_intermediate_size, config.n_shared_experts, bias=False | |
| ) | |
| def __call__(self, x: mx.array) -> mx.array: | |
| B, L, H = x.shape | |
| topk_weights, topk_idx, shared_gammas = self.gate(x) | |
| xf = x.reshape(-1, H) # [T, H] | |
| T = xf.shape[0] | |
| routed = self.experts(xf, topk_idx) # [T, top_k, H] | |
| routed = (routed * topk_weights[..., None]).sum(axis=1) | |
| shared_idx = mx.broadcast_to( | |
| mx.arange(self.n_shared)[None], (T, self.n_shared) | |
| ) | |
| shared = self.shared_experts(xf, shared_idx) # [T, n_shared, H] | |
| shared = (shared.astype(mx.float32) * shared_gammas[..., None].astype(mx.float32)).sum(axis=1) | |
| shared = shared.astype(routed.dtype) | |
| return (routed + shared).reshape(B, L, H) | |