Instructions to use IAAR-Shanghai/Metis-9B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use IAAR-Shanghai/Metis-9B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="IAAR-Shanghai/Metis-9B", 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 AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("IAAR-Shanghai/Metis-9B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use IAAR-Shanghai/Metis-9B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "IAAR-Shanghai/Metis-9B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IAAR-Shanghai/Metis-9B", "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/IAAR-Shanghai/Metis-9B
- SGLang
How to use IAAR-Shanghai/Metis-9B 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 "IAAR-Shanghai/Metis-9B" \ --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": "IAAR-Shanghai/Metis-9B", "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 "IAAR-Shanghai/Metis-9B" \ --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": "IAAR-Shanghai/Metis-9B", "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 IAAR-Shanghai/Metis-9B with Docker Model Runner:
docker model run hf.co/IAAR-Shanghai/Metis-9B
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from abc import ABC | |
| def _qk_kernel(x: torch.Tensor, kernel_type: str = "elu_plus_one") -> torch.Tensor: | |
| if kernel_type == "elu_plus_one": | |
| return F.elu(x) + 1.0 | |
| if kernel_type == "relu_square": | |
| return F.relu(x).square() | |
| if kernel_type == "softplus": | |
| return F.softplus(x) | |
| raise ValueError(f"Unsupported qk kernel type: {kernel_type}") | |
| def create_metis_local_memory(config): | |
| return eval(config.memory_configs['metis_local_memory_type'])(config) | |
| class MetisLocalMemoryBase(nn.Module, ABC): | |
| def __init__(self, config) -> None: | |
| super().__init__() | |
| self.config = config | |
| # Qwen 3.5 has text config, but Qwen 3 does not. | |
| self.text_cfg = getattr(config.backbone_configs, 'text_config', config.backbone_configs) | |
| def initialize(self) -> None: | |
| raise NotImplementedError | |
| def reset(self) -> None: | |
| raise NotImplementedError | |
| def read(self, query_for_memory): | |
| raise NotImplementedError | |
| def write(self, new_info) -> None: | |
| raise NotImplementedError | |
| def state(self): | |
| raise NotImplementedError | |
| class DeltaNetMetisLocalMemory(MetisLocalMemoryBase): | |
| """Linear (DeltaNet-style) memory matrix of shape (b, D, D). | |
| Read: output = Q_flat @ M, where Q_flat = (b, s, D) | |
| Write: M = new_state (forget + additive update computed by HyperMemory) | |
| """ | |
| def __init__(self, config) -> None: | |
| super().__init__(config) | |
| num_q_heads = self.text_cfg.num_attention_heads | |
| # If num_key_value_heads is not set, use num_attention_heads (MHA). | |
| num_kv_heads = getattr(self.text_cfg, "num_key_value_heads", num_q_heads) | |
| head_dim = getattr(self.text_cfg, "head_dim", self.text_cfg.hidden_size // num_q_heads) | |
| self.q_dim = num_q_heads * head_dim | |
| self.kv_dim = self._compute_kv_dim(num_q_heads, num_kv_heads, head_dim) | |
| self.num_kv_groups = self.q_dim // self.kv_dim | |
| self._state: torch.Tensor | None = None | |
| def _compute_kv_dim(num_q_heads: int, num_kv_heads: int, head_dim: int) -> int: | |
| """GQA layout: kv_dim = num_kv_heads * head_dim.""" | |
| return num_kv_heads * head_dim | |
| def initialize(self) -> None: | |
| self._state = None | |
| def reset(self) -> None: | |
| self._state = None | |
| def _ensure_ready( | |
| self, | |
| batch_size: int, | |
| device: torch.device, | |
| dtype: torch.dtype, | |
| ) -> None: | |
| if self._state is None or self._state.shape[0] != batch_size: | |
| self._state = torch.zeros( | |
| batch_size, self.kv_dim, self.kv_dim, device=device, dtype=dtype, | |
| ) | |
| def read(self, query_for_memory: torch.Tensor) -> torch.Tensor: | |
| """Linear memory read: output = Q_flat @ M. | |
| Args: | |
| query_for_memory: (b, h, s, d) | |
| Returns: | |
| (b, s, D) — memory readout, ready to be fused with attention output. | |
| """ | |
| bsz, _h, seq_len, _d = query_for_memory.shape | |
| self._ensure_ready(bsz, query_for_memory.device, query_for_memory.dtype) | |
| # (b, h, s, d) → (b, s, q_dim) | |
| q_flat = query_for_memory.transpose(1, 2).reshape(bsz, seq_len, -1) | |
| if self.num_kv_groups > 1: | |
| # GQA mode. | |
| q_2d = q_flat.view(bsz, seq_len * self.num_kv_groups, self.kv_dim) | |
| out_2d = torch.matmul(q_2d, self._state) | |
| return out_2d.view(bsz, seq_len, self.q_dim).contiguous() | |
| else: | |
| # For MHA mode | |
| # (b, s, q_dim) @ (b, q_dim, q_dim) → (b, s, q_dim) | |
| return torch.matmul(q_flat, self._state).contiguous() | |
| def write(self, new_state: torch.Tensor) -> None: | |
| self._state = new_state # no detach here, so gradients flow through W_k / W_v | |
| def state(self) -> torch.Tensor | None: | |
| return self._state | |
| def is_initialized(self) -> bool: | |
| return self._state is not None | |
| def norm(self) -> float: | |
| return self._state.norm().item() if self._state is not None else 0.0 | |
| class MHADeltaNetMetisLocalMemory(DeltaNetMetisLocalMemory): | |
| """Legacy MHA-style memory: kv_dim = num_q_heads * head_dim (no GQA grouping). | |
| Memory matrix is (b, q_dim, q_dim) — for Qwen3.5-4B that's 4096×4096. | |
| Read collapses to a single MHA matmul: (b, s, q_dim) @ (b, q_dim, q_dim). | |
| Use this for loading checkpoints trained before the GQA refactor | |
| (e.g. experiments/4.17-* and 4.18-*). | |
| """ | |
| def _compute_kv_dim(num_q_heads: int, num_kv_heads: int, head_dim: int) -> int: | |
| return num_q_heads * head_dim | |
| class NormalizedDeltaNetMetisLocalMemory(DeltaNetMetisLocalMemory): | |
| """DeltaNet memory with metis_modular-style key normalization. | |
| Read path: | |
| q = normalize(q) | |
| y = q @ state | |
| y = y / (q @ key_state + 1) | |
| The paired hyper-memory class ``FullTokensKeyNormMetisHyperMemory`` writes | |
| both ``state`` and ``key_state``. | |
| """ | |
| def __init__(self, config) -> None: | |
| super().__init__(config) | |
| self._key_state: torch.Tensor | None = None | |
| def initialize(self) -> None: | |
| self._state = None | |
| self._key_state = None | |
| def reset(self) -> None: | |
| self._state = None | |
| self._key_state = None | |
| def _ensure_ready( | |
| self, | |
| batch_size: int, | |
| device: torch.device, | |
| dtype: torch.dtype, | |
| ) -> None: | |
| if self._state is None or self._state.shape[0] != batch_size: | |
| self._state = torch.zeros( | |
| batch_size, self.kv_dim, self.kv_dim, device=device, dtype=dtype, | |
| ) | |
| self._key_state = torch.zeros( | |
| batch_size, self.kv_dim, 1, device=device, dtype=dtype, | |
| ) | |
| def read(self, query_for_memory: torch.Tensor) -> torch.Tensor: | |
| bsz, _h, seq_len, _d = query_for_memory.shape | |
| self._ensure_ready(bsz, query_for_memory.device, query_for_memory.dtype) | |
| query_for_memory = F.normalize(query_for_memory, dim=-1) | |
| q_flat = query_for_memory.transpose(1, 2).reshape(bsz, seq_len, -1) | |
| if self.num_kv_groups > 1: | |
| q_2d = q_flat.view(bsz, seq_len * self.num_kv_groups, self.kv_dim) | |
| out_2d = torch.matmul(q_2d, self._state) | |
| # if self._key_state is not None: | |
| norm_factor = torch.matmul(q_2d, self._key_state) | |
| # print(norm_factor[0]) | |
| out_2d = out_2d / (norm_factor + 1.0) | |
| return out_2d.view(bsz, seq_len, self.q_dim).contiguous() | |
| out = torch.matmul(q_flat, self._state) | |
| # if self._key_state is not None: | |
| norm_factor = torch.matmul(q_flat, self._key_state) | |
| # print(norm_factor.shape) | |
| out = out / (norm_factor + 1.0) | |
| return out.contiguous() | |
| def write(self, new_state: torch.Tensor, key_state: torch.Tensor) -> None: | |
| self._state = new_state | |
| self._key_state = key_state | |
| def key_state(self) -> torch.Tensor | None: | |
| return self._key_state | |
| def is_initialized(self) -> bool: | |
| return self._state is not None and self._key_state is not None | |
| class KernelizedDeltaNetMetisLocalMemory(NormalizedDeltaNetMetisLocalMemory): | |
| """DeltaNet memory read path with a kernel feature map on queries. | |
| Pair this with ``StrideKernelKeyNormMetisHyperMemory`` so the same feature | |
| map is applied to q and k before the key-state normalization: | |
| phi(q) = kernel(q) | |
| y = phi(q) @ state | |
| y = y / (phi(q) @ key_state + 1) | |
| """ | |
| def __init__(self, config) -> None: | |
| super().__init__(config) | |
| self.qk_kernel_type = config.memory_configs.get("qk_kernel_type", "elu_plus_one") | |
| def read(self, query_for_memory: torch.Tensor) -> torch.Tensor: | |
| bsz, _h, seq_len, _d = query_for_memory.shape | |
| self._ensure_ready(bsz, query_for_memory.device, query_for_memory.dtype) | |
| q_flat = query_for_memory.transpose(1, 2).reshape(bsz, seq_len, -1) | |
| if self.num_kv_groups > 1: | |
| q_2d = q_flat.view(bsz, seq_len * self.num_kv_groups, self.kv_dim) | |
| q_2d = _qk_kernel(q_2d, self.qk_kernel_type) | |
| out_2d = torch.matmul(q_2d, self._state) | |
| if self._key_state is not None: | |
| norm_factor = torch.matmul(q_2d, self._key_state) | |
| out_2d = out_2d / norm_factor | |
| return out_2d.view(bsz, seq_len, self.q_dim).contiguous() | |
| q_flat = _qk_kernel(q_flat, self.qk_kernel_type) | |
| out = torch.matmul(q_flat, self._state) | |
| if self._key_state is not None: | |
| norm_factor = torch.matmul(q_flat, self._key_state) | |
| out = out / norm_factor | |
| return out.contiguous() | |
| class L2NormalizedDeltaNetMetisLocalMemory(DeltaNetMetisLocalMemory): | |
| """DeltaNet memory with L2-normalized queries and no key-state denominator. | |
| Pair this with ``StrideL2NormMetisHyperMemory``: | |
| q = normalize(q) | |
| y = q @ state | |
| This keeps the read-side q scale controlled by L2 normalization while the | |
| write-side hyper memory normalizes k. | |
| """ | |
| def read(self, query_for_memory: torch.Tensor) -> torch.Tensor: | |
| bsz, _h, seq_len, _d = query_for_memory.shape | |
| self._ensure_ready(bsz, query_for_memory.device, query_for_memory.dtype) | |
| query_for_memory = F.normalize(query_for_memory, dim=-1) | |
| q_flat = query_for_memory.transpose(1, 2).reshape(bsz, seq_len, -1) | |
| if self.num_kv_groups > 1: | |
| q_2d = q_flat.view(bsz, seq_len * self.num_kv_groups, self.kv_dim) | |
| out_2d = torch.matmul(q_2d, self._state) | |
| return out_2d.view(bsz, seq_len, self.q_dim).contiguous() | |
| return torch.matmul(q_flat, self._state).contiguous() | |
| class OneStepAblationMetisLocalMemory(MetisLocalMemoryBase): | |
| def __init__(self, config) -> None: | |
| super().__init__(config) | |
| def initialize(self) -> None: | |
| self.memory_state = None | |
| def reset(self) -> None: | |
| self.initialize() | |
| def read(self, query_for_memory): | |
| return self.memory_state | |
| def write(self, new_info) -> None: | |
| self.memory_state = new_info | |