Instructions to use cyankiwi/MiniMax-M3-AWQ-INT4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use cyankiwi/MiniMax-M3-AWQ-INT4 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="cyankiwi/MiniMax-M3-AWQ-INT4", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("cyankiwi/MiniMax-M3-AWQ-INT4", trust_remote_code=True) model = AutoModelForMultimodalLM.from_pretrained("cyankiwi/MiniMax-M3-AWQ-INT4", trust_remote_code=True, device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use cyankiwi/MiniMax-M3-AWQ-INT4 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "cyankiwi/MiniMax-M3-AWQ-INT4" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "cyankiwi/MiniMax-M3-AWQ-INT4", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/cyankiwi/MiniMax-M3-AWQ-INT4
- SGLang
How to use cyankiwi/MiniMax-M3-AWQ-INT4 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "cyankiwi/MiniMax-M3-AWQ-INT4" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "cyankiwi/MiniMax-M3-AWQ-INT4", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "cyankiwi/MiniMax-M3-AWQ-INT4" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "cyankiwi/MiniMax-M3-AWQ-INT4", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use cyankiwi/MiniMax-M3-AWQ-INT4 with Docker Model Runner:
docker model run hf.co/cyankiwi/MiniMax-M3-AWQ-INT4
# FP8 KV Cache Support on SM89 (RTX 4090) — Working Solution
FP8 KV Cache Support on SM89 (RTX 4090) — Working Solution
I got FP8 KV cache working on 8x RTX 4090D (SM89) with the patched vLLM fork from this repo. Sharing the fix for anyone interested.
Problem
The fused CUDA kernel fused_minimax_m3_qknorm_rope_kv_insert hardcodes bf16 cache writes (assertion: "kv_cache dtype must match qkv (bf16 cache only)"), so passing --kv-cache-dtype fp8_e4m3 crashes immediately.
Solution
The AMD model path (vllm/models/minimax_m3/amd/model.py) already implements an FP8 bypass — I ported the same pattern to the NVIDIA model.
File to modify
<vllm-install-path>/vllm/models/minimax_m3/nvidia/model.py
In the docker image from this repo (based on toncao/vllm minimax-m3-compressed-tensors branch), the full path is:
/workspace/vllm-awq/vllm/models/minimax_m3/nvidia/model.py
Patch (unified diff)
Apply with patch -p1 from the vllm root, or manually edit the three sections below.
--- a/vllm/models/minimax_m3/nvidia/model.py
+++ b/vllm/models/minimax_m3/nvidia/model.py
@@ -514,6 +514,11 @@
self.kv_cache_torch_dtype = kv_cache_dtype_str_to_dtype(
self.kv_cache_dtype, vllm_config.model_config
)
+ # fp8 main-K/V cache: the fused qknorm+rope+kv-insert op is bf16-cache-only
+ # (asserts kv_cache dtype == qkv), so on the fp8 path we run it in
+ # norm+rope-only mode and write the cache via the fp8-capable
+ # reshape_and_cache_flash in _insert_kv. (index cache stays bf16.)
+ self._fp8_kv = "fp8" in self.kv_cache_dtype
# Indexer side-cache dtype, mirroring --kv-cache-dtype for the main
# cache (--attention-config '{"indexer_kv_dtype": ...}').
self.indexer_kv_dtype = vllm_config.attention_config.indexer_kv_dtype
@@ -574,27 +579,25 @@
def _insert_kv(
- self, key: torch.Tensor, value: torch.Tensor, index_key: torch.Tensor
+ self,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ index_key: torch.Tensor,
+ main_slot_mapping: torch.Tensor,
+ index_slot_mapping: torch.Tensor,
) -> None:
- """Write main K/V and index-K into their paged caches.
+ """Write main K/V (fp8-quantizing) and index-K into their paged caches.
- No-op during the profiling run, where caches are not yet bound and
- ``attn_metadata`` is None.
+ Used only on the fp8-KV path: the fused op is bf16-cache-only, so it
+ runs in norm+rope-only mode and the (already normed/roped) k/v/index_k
+ are written here via ``reshape_and_cache_flash`` (which honors
+ kv_cache_dtype). The index cache stays bf16 (no quant).
"""
- attn_metadata = get_forward_context().attn_metadata
- if not isinstance(attn_metadata, dict):
- return
- main_meta = attn_metadata[self.layer_name]
- index_meta = attn_metadata[self.indexer.index_cache.prefix]
- assert isinstance(main_meta, MiniMaxM3SparseMetadata)
- assert isinstance(index_meta, MiniMaxM3IndexerMetadata)
-
- # Identity scale: unused for the bf16 cache, required arg of the op.
key_cache, value_cache = self.kv_cache.unbind(1)
scale = torch.ones((), device=key.device)
ops.reshape_and_cache_flash(
key.view(-1, self.num_kv_heads, self.head_dim),
value.view(-1, self.num_kv_heads, self.head_dim),
key_cache,
value_cache,
- main_meta.slot_mapping,
+ main_slot_mapping,
self.kv_cache_dtype,
scale,
scale,
)
-
# Index-key cache: single vector per token, scatter by slot.
idx_cache = self.indexer.index_cache.kv_cache.view(-1, self.idx_head_dim)
- idx_cache[index_meta.slot_mapping] = index_key.to(idx_cache.dtype)
+ idx_cache[index_slot_mapping] = index_key.to(idx_cache.dtype)
@@ -649,6 +652,12 @@
main_slot_mapping = fwd_slot_mapping[self.layer_name]
index_slot_mapping = fwd_slot_mapping[self.indexer.index_cache.prefix]
q = qkv.new_empty((num_tokens, self.q_size))
index_q = qkv.new_empty((num_tokens, self.index_q_size))
+ # On the fp8-KV path the fused op cannot write the (fp8) cache, so pass
+ # kv_cache/index_cache = None -> insert_kv=False (norm+rope only): it still
+ # de-interleaves q/index_q and rewrites the normed/roped k & index_k in
+ # place in qkv, leaving v raw (correct -- v is never normed/roped). We then
+ # write the cache via _insert_kv below.
+ insert_via_fused = not self._fp8_kv
ops.fused_minimax_m3_qknorm_rope_kv_insert(
qkv,
self.q_norm.weight,
@@ -665,11 +674,24 @@
main_slot_mapping,
index_slot_mapping,
- self.kv_cache,
- self.indexer.index_cache.kv_cache,
+ self.kv_cache if insert_via_fused else None,
+ self.indexer.index_cache.kv_cache if insert_via_fused else None,
self.kv_cache.size(2), # paged-cache block size
q,
index_q,
)
+ if not insert_via_fused:
+ # Extract the normed/roped k, raw v, normed/roped index_k from qkv
+ # ([q | k | v | index_q | index_k], all head_dim=128) and fp8-insert.
+ kv = self.num_kv_heads * self.head_dim
+ # These are strided views into qkv (row stride = full qkv width), but
+ # their last dim is contiguous, so `_insert_kv`'s `.view(-1, nkv,
+ # head_dim)` works on them and `reshape_and_cache_flash` honors the
+ # input stride -- no `.contiguous()` needed.
+ k = qkv[:, self.q_size : self.q_size + kv]
+ v = qkv[:, self.q_size + kv : self.q_size + 2 * kv]
+ ik0 = self.q_size + 2 * kv + self.index_q_size
+ index_k = qkv[:, ik0 : ik0 + self.num_idx_heads * self.idx_head_dim]
+ self._insert_kv(k, v, index_k, main_slot_mapping, index_slot_mapping)
output = torch.empty_like(q)
Launch command
vllm serve cyankiwi/MiniMax-M3-AWQ-INT4 \
--block-size 128 \
--kv-cache-dtype fp8_e4m3 \
--attention-backend TRITON_ATTN \
--tensor-parallel-size 8
Important: --attention-backend TRITON_ATTN is required on SM89 because FlashInfer does not support block_size=128 on non-Blackwell GPUs (it only advertises [16, 32, 64] when can_use_trtllm_attention returns False). The Triton backend supports MultipleOf(16) and has explicit fp8 support for SM89+.
How it works
The fused CUDA kernel fused_minimax_m3_qknorm_rope_kv_insert combines QK-norm + RoPE + KV cache insertion into one kernel launch. However it only supports writing bf16 to the KV cache.
The fix splits the fp8 path into two steps:
- Fused kernel runs in norm+rope-only mode (passing
kv_cache=None→ template parameterINSERT=false) reshape_and_cache_flash(a generic vLLM op that already supports fp8 quantization) handles the KV cache write separately
This is the exact same pattern already used in the AMD/ROCm model path (vllm/models/minimax_m3/amd/model.py).
Hardware requirements
| GPU | SM | FP8 KV Cache |
|---|---|---|
| RTX 4090 / 4090D | SM89 | ✅ works |
| H100 / H200 | SM90 | ✅ works |
| B200 | SM100 | ✅ works (MSA path) |
| A100 | SM80 | ❌ no hardware fp8 attention |
Benefit
FP8 KV cache halves the cache memory usage compared to bf16, allowing significantly longer context lengths on memory-constrained GPUs like the RTX 4090D (48GB).