Instructions to use masahiroid/pyrite-stream with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use masahiroid/pyrite-stream with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # if on a CUDA device, also pip install mlx[cuda] # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("masahiroid/pyrite-stream") prompt = "Once upon a time in" text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- MLX LM
How to use masahiroid/pyrite-stream with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Generate some text mlx_lm.generate --model "masahiroid/pyrite-stream" --prompt "Once upon a time"
- Atomic Chat
File size: 7,743 Bytes
cab2ef1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #!/usr/bin/env python3
"""Streaming MoE Layer Implementation for Apple Silicon MLX with Quantized Metal Matmul.
Replaces standard heavy MoE layer. Keeps routing gate and shared experts resident in RAM,
while streaming routed experts on-demand from NVMe storage with an LRU cache.
"""
from collections import OrderedDict
from typing import Dict, List, Optional, Tuple, Any
import mlx.core as mx
import mlx.nn as nn
from expert_registry import SafetensorsIndex, ModelSafetensorsIndex
class ExpertLRUCache:
"""Least-Recently-Used (LRU) In-Memory Cache for MoE Experts."""
def __init__(self, max_experts_in_ram: int = 16):
self.max_experts = max_experts_in_ram
self.cache: OrderedDict[int, Dict[str, mx.array]] = OrderedDict()
def get(self, expert_id: int) -> Optional[Dict[str, mx.array]]:
if expert_id in self.cache:
self.cache.move_to_end(expert_id)
return self.cache[expert_id]
return None
def put(self, expert_id: int, weights: Dict[str, mx.array]):
if expert_id in self.cache:
self.cache.move_to_end(expert_id)
else:
if len(self.cache) >= self.max_experts:
self.cache.popitem(last=False)
self.cache[expert_id] = weights
class StreamingMoELayer(nn.Module):
"""Memory-efficient streaming MoE layer for Apple Silicon MLX.
Dynamically routes tokens to experts, streaming weights directly from
NVMe mmap indices without loading entire multi-gigabyte models into RAM.
"""
def __init__(
self,
layer_idx: int,
gate_weight: mx.array,
shared_experts: Optional[Dict[str, mx.array]],
index_reader: Any,
top_k: int = 6,
gate_scales: Optional[mx.array] = None,
gate_biases: Optional[mx.array] = None,
routed_scaling_factor: float = 1.0,
topk_method: str = "greedy",
n_group: int = 1,
topk_group: int = 1,
max_cache_experts: int = 16,
moe_prefix: str = "mlp",
softmax_before_topk: bool = True,
):
super().__init__()
self.layer_idx = layer_idx
self.gate_weight = gate_weight
self.gate_scales = gate_scales
self.gate_biases = gate_biases
self.shared = shared_experts
self.index_reader = index_reader
self.top_k = top_k
self.routed_scaling_factor = routed_scaling_factor
self.topk_method = topk_method
self.n_group = n_group
self.topk_group = topk_group
self.moe_prefix = moe_prefix
self.softmax_before_topk = softmax_before_topk
self.cache = ExpertLRUCache(max_experts_in_ram=max_cache_experts)
def _get_expert_tensors(self, expert_id: int) -> Dict[str, mx.array]:
cached = self.cache.get(expert_id)
if cached is not None:
return cached
l_str = f"model.layers.{self.layer_idx}.{self.moe_prefix}.switch_mlp"
weights = {
"gate_w": self.index_reader.get_expert_slice(f"{l_str}.gate_proj.weight", expert_id),
"gate_s": self.index_reader.get_expert_slice(f"{l_str}.gate_proj.scales", expert_id),
"gate_b": self.index_reader.get_expert_slice(f"{l_str}.gate_proj.biases", expert_id),
"up_w": self.index_reader.get_expert_slice(f"{l_str}.up_proj.weight", expert_id),
"up_s": self.index_reader.get_expert_slice(f"{l_str}.up_proj.scales", expert_id),
"up_b": self.index_reader.get_expert_slice(f"{l_str}.up_proj.biases", expert_id),
"down_w": self.index_reader.get_expert_slice(f"{l_str}.down_proj.weight", expert_id),
"down_s": self.index_reader.get_expert_slice(f"{l_str}.down_proj.scales", expert_id),
"down_b": self.index_reader.get_expert_slice(f"{l_str}.down_proj.biases", expert_id),
}
self.cache.put(expert_id, weights)
return weights
@staticmethod
def _qmatmul(x: mx.array, w: mx.array, s: mx.array, b: Optional[mx.array] = None) -> mx.array:
"""Dynamic quantized matrix multiplication auto-detecting bits and group size."""
k = x.shape[-1]
bits = (w.shape[-1] * 32) // k
group_size = k // s.shape[-1]
return mx.quantized_matmul(
x, w, scales=s, biases=b, transpose=True, group_size=group_size, bits=bits
)
def forward_expert(self, x: mx.array, exp_id: int) -> mx.array:
"""Run forward pass for a single expert."""
exp_t = self._get_expert_tensors(exp_id)
h_gate = self._qmatmul(x, exp_t["gate_w"], exp_t["gate_s"], exp_t["gate_b"])
h_up = self._qmatmul(x, exp_t["up_w"], exp_t["up_s"], exp_t["up_b"])
h_expert = nn.silu(h_gate) * h_up
return self._qmatmul(h_expert, exp_t["down_w"], exp_t["down_s"], exp_t["down_b"])
def __call__(self, x: mx.array) -> mx.array:
orig_shape = x.shape
x_flat = x.reshape(-1, orig_shape[-1])
num_tokens, hidden_dim = x_flat.shape
# 1. Router Gate (resident in RAM)
if self.gate_scales is not None:
logits = self._qmatmul(x_flat, self.gate_weight, self.gate_scales, self.gate_biases)
else:
logits = x_flat @ self.gate_weight.T
k = self.top_k
if self.softmax_before_topk:
scores = mx.softmax(logits, axis=-1, precise=True)
if self.topk_method == "group_limited_greedy" and self.n_group > 1:
grouped_scores = scores.reshape(num_tokens, self.n_group, -1)
group_scores = grouped_scores.max(axis=-1, keepdims=True)
k_grp = self.n_group - self.topk_group
group_idx = mx.argpartition(group_scores, kth=k_grp - 1, axis=-2)[..., :k_grp, :]
scores = mx.put_along_axis(
grouped_scores, group_idx, mx.array(0.0, scores.dtype), axis=-2
).reshape(num_tokens, -1)
top_k_indices = mx.argpartition(-scores, kth=k - 1, axis=-1)[..., :k]
top_k_scores = mx.take_along_axis(scores, top_k_indices, axis=-1)
if self.routed_scaling_factor != 1.0:
top_k_scores = top_k_scores * self.routed_scaling_factor
else:
# Mixtral style: Top-k on logits first, then softmax across the top-k chosen logits
top_k_indices = mx.stop_gradient(mx.argpartition(-logits, kth=k - 1, axis=-1)[..., :k])
chosen_logits = mx.take_along_axis(logits, top_k_indices, axis=-1)
top_k_scores = mx.softmax(chosen_logits, axis=-1, precise=True)
# 2. Shared Experts (resident in RAM)
if self.shared is not None:
sh_gate = self._qmatmul(x_flat, self.shared["gate_w"], self.shared["gate_s"], self.shared.get("gate_b"))
sh_up = self._qmatmul(x_flat, self.shared["up_w"], self.shared["up_s"], self.shared.get("up_b"))
h_shared = nn.silu(sh_gate) * sh_up
out_shared = self._qmatmul(h_shared, self.shared["down_w"], self.shared["down_s"], self.shared.get("down_b"))
else:
out_shared = mx.zeros_like(x_flat)
# 3. Dynamic Routed Experts Streaming (Vectorized on Metal GPU)
out_routed = mx.zeros_like(x_flat)
unique_experts = list(set(top_k_indices.flatten().tolist()))
for exp_id in unique_experts:
weight = mx.where(
top_k_indices == exp_id,
top_k_scores,
mx.zeros_like(top_k_scores)
).sum(axis=-1, keepdims=True)
exp_out = self.forward_expert(x_flat, exp_id)
out_routed = out_routed + exp_out * weight
final_out = (out_shared + out_routed).reshape(orig_shape)
return final_out
|