Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/cache_config/hf_models_and_datasets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ models:
- ibm-granite/granite-3.3-2b-instruct # granite2b
- ibm-granite/granite-4.0-1b-base # granite4
- ibm-granite/granite-4.0-1b # granite4_instruct
- ibm-research/granite-4.1-20b # granite41_20b
# Embedding models
- BAAI/bge-base-en-v1.5 # bge_base (embed-compare)
- BAAI/bge-m3 # bge_m3 (load)
39 changes: 20 additions & 19 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ which models are supported on Spyre.
| Granite 3.3 2B | granite | 64→128 | 64 | Yes (padded) | Yes | Yes | Yes |
| Granite 4.0 1B Base | granitemoehybrid | 128 | 64 | Yes | Yes | Yes | Yes |
| Granite 4.0 1B Instruct | granitemoehybrid | 128 | 64 | Yes | Yes | Yes | Yes |
| Granite 4.1 20B (bf16) | granite_swa | 128 | 64 | Yes | Yes | Yes | Yes |
| SmolLM3 3B | smollm3 | 128 | 64 | Yes | Yes | Yes | Yes |
| Llama 3.2 3B | llama | 128 | 64 | Yes | Yes | Yes | Yes |
| TinyLlama 1.1B | llama | 64→128 | 64 | Yes (padded) | Yes | Yes | Yes |
Expand All @@ -29,8 +30,6 @@ which models are supported on Spyre.
| Yi 1.5 6B | llama | 128 | 64 | Yes | Yes | Yes | Yes |
| Granite Vision 4.1 4B (text backbone) | granite (text) | 64→128 | 64 | Yes (padded) | Yes | Yes | Yes |
| Gemma 4 12B | gemma4\_unified | 256 / 512 | 128 / 256 | Yes | Yes | Yes | Yes |
| Gemma 4 12B Base | gemma4\_unified | 256 / 512 | 128 / 256 | Yes | Yes | Yes | Yes |
| Gemma 4 31B | gemma4 | 256 / 512 | 128 / 256 | Yes | Yes | Yes | Yes |
| Gemma 3 1B | gemma3\_text | 256 | 128 | Yes | Yes | Yes | Yes |
| GPT-2 124M | gpt2 | 64 | n/a (no RoPE) | Yes | Yes | Yes | Yes |
| GPT-Neo 125M | gpt_neo | 64 | n/a (no RoPE) | Yes | Yes | Yes | Yes |
Expand Down Expand Up @@ -112,8 +111,8 @@ single-token decode path (seq_len=1), not an adapter issue.
> adapter or verify a checkpoint, update *only* this file (and the badge
> counts in README.md, noted below).

**Coverage:** 27 adapters · 46 verified checkpoints · 100+ compatible models.
The 46 verified rows are 29 generative + 13 embedding + 4 vision-language (see the
**Coverage:** 28 adapters · 45 verified checkpoints · 100+ compatible models.
The 45 verified rows are 28 generative + 13 embedding + 4 vision-language (see the
Verified Checkpoints tables above). `hf_siglip_vision` and `hf_pixtral_vision` are
vision-tower components used by VLM adapters rather than standalone model adapters.
Granite Vision 4.1 is verified both as a text backbone (generative) and as a full VLM.
Expand All @@ -140,8 +139,9 @@ pattern, norms, and weight layout.
| hf\_ministral.py | ministral | 1 | Ministral-8B Instruct fine-tunes |
| hf\_phi3.py | phi3 | 1 | Phi-3 mini 4k/128k, Phi-3 small 8k |
| hf\_granitemoehybrid.py | granitemoehybrid | 2 | Granite 4.0 Micro |
| hf\_granite\_swa.py | granite\_swa | 1 | Granite 4.1 8B (unverified), Granite 4.1 20B |
| hf\_smollm3.py | smollm3 | 1 | — |
| hf\_gemma4.py | gemma4\_unified / gemma4 (dense) | 3 | Not E2B/E4B (PLE) or 26B-A4B (MoE). |
| hf\_gemma4.py | gemma4\_unified / gemma4 (dense) | 1 | Gemma 4 31B (dense). Not E2B/E4B (PLE) or 26B-A4B (MoE). |
| hf\_gemma4\_mm.py | gemma4\_unified (multimodal) | 1 | Gemma 4 31B (dense unified VLM). Not E2B/E4B (PLE) or 26B-A4B (MoE). |
| hf\_gemma3.py | gemma3\_text / gemma3 (dense) | 2 | Gemma 3 4B/12B/27B (text decoder of the multimodal checkpoints); EmbeddingGemma (bidirectional embedder). Not Gemma 3n (PLE). |
| hf\_olmo.py | olmo | 1 | OLMo 7B |
Expand Down Expand Up @@ -392,20 +392,21 @@ modification:

### Model-Specific Adaptations

| Feature | Granite 3.3 | Granite Vision 4.1 | Qwen3 | Granite 4.0 | SmolLM3 | Llama | Qwen2 | Mistral | Phi-4 mini | OLMo | OLMo2 | Gemma 3 | Gemma 4 |
|---------|------------|-------------------|-------|-------------|---------|-------|-------|---------|-----------|------|-------|---------|---------|
| Embedding multiplier | Yes | Yes | No | Yes | No | No | No | No | No | No | No | Yes | Yes |
| Residual multiplier | Yes | Yes | No | Yes | No | No | No | No | No | No | No | No | No |
| Logits scaling | Yes | Yes | No | Yes | No | No | No | No | No | No | No | No | No |
| Q/K RMSNorm | No | No | Yes (per-head) | No | No | No | No | No | No | No | Yes (flattened) | Yes (per-head Q/K) | Yes (per-head Q/K/V) |
| Fused QKV split | No | No | No | No | No | No | No | No | Yes | No | No | No | No |
| Fused MLP split | No | No | No | Yes | No | No | No | No | Yes | No | No | No | No |
| NoPE layers | No | No | No | No | Yes | No | No | No | No | No | No | No | No |
| Partial RoPE | No | No | No | No | No | No | No | No | Yes | No | No | No | Yes (global layers) |
| Head-dim padding | 2B only | Yes (64→128) | No | Micro only (64→128) | No | TinyLlama | No | No | No | No | No | No | No |
| Custom model loading | No | Yes (safetensor remap) | No | No | No | No | No | No | No | No | No | No | No |
| Attention scaling | `config.attention_multiplier` | `config.attention_multiplier` | `head_dim**-0.5` | `config.attention_multiplier` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `query_pre_attn_scalar**-0.5` | `1.0` (unscaled) |
| Norm type | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | LayerNorm (pre, no weight) | RMSNorm (post) | RMSNorm (sandwich) | RMSNorm (sandwich) |
| Feature | Granite 3.3 | Granite Vision 4.1 | Qwen3 | Granite 4.0 | Granite 4.1 SWA | SmolLM3 | Llama | Qwen2 | Mistral | Phi-4 mini | OLMo | OLMo2 | Gemma 3 | Gemma 4 |
|---------|------------|-------------------|-------|-------------|-----------------|---------|-------|-------|---------|-----------|------|-------|---------|---------|
| Embedding multiplier | Yes | Yes | No | Yes | Yes | No | No | No | No | No | No | No | Yes | Yes |
| Residual multiplier | Yes | Yes | No | Yes | Yes | No | No | No | No | No | No | No | No | No |
| Logits scaling | Yes | Yes | No | Yes | Yes | No | No | No | No | No | No | No | No | No |
| Q/K RMSNorm | No | No | Yes (per-head) | No | No | No | No | No | No | No | No | Yes (flattened) | Yes (per-head Q/K) | Yes (per-head Q/K/V) |
| Fused QKV split | No | No | No | No | No | No | No | No | No | Yes | No | No | No | No |
| Fused MLP split | No | No | No | Yes | No | No | No | No | No | Yes | No | No | No | No |
| NoPE layers | No | No | No | No | No | Yes | No | No | No | No | No | No | No | No |
| Partial RoPE | No | No | No | No | No | No | No | No | No | Yes | No | No | No | Yes (global layers) |
| Sliding-window layers | No | No | No | No | Yes (alternating) | No | No | No | No | No | No | No | No | No |
| Head-dim padding | 2B only | Yes (64→128) | No | Micro only (64→128) | No | No | TinyLlama | No | No | No | No | No | No | No |
| Custom model loading | No | Yes (safetensor remap) | No | No | No | No | No | No | No | No | No | No | No | No |
| Attention scaling | `config.attention_multiplier` | `config.attention_multiplier` | `head_dim**-0.5` | `config.attention_multiplier` | `config.attention_multiplier` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `head_dim**-0.5` | `query_pre_attn_scalar**-0.5` | `1.0` (unscaled) |
| Norm type | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | RMSNorm (pre) | LayerNorm (pre, no weight) | RMSNorm (post) | RMSNorm (sandwich) | RMSNorm (sandwich) |

**Partial RoPE** (Phi-4): `PartialPrecomputedRotaryEmbedding` pads
the rotation matrix with identity `[[1,0],[0,1]]` entries so
Expand Down
34 changes: 28 additions & 6 deletions hf_adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
| 6 | hf\_mistral.py | `mistral` | 1 | 4+ |
| 7 | hf\_phi3.py | `phi3` | 1 | 3+ |
| 8 | hf\_granitemoehybrid.py | `granitemoehybrid` | 2 | 1+ |
| 9 | hf\_smollm3.py | `smollm3` | 1 | — |
| 10 | hf\_olmo.py | `olmo` | 1 | 1+ |
| 11 | hf\_olmo2.py | `olmo2` | 1 | 1+ |
| 9 | hf\_granite\_swa.py | `granite_swa` | 1 | 1+ |
| 10 | hf\_smollm3.py | `smollm3` | 1 | — |
| 11 | hf\_olmo.py | `olmo` | 1 | 1+ |
| 12 | hf\_olmo2.py | `olmo2` | 1 | 1+ |

---

Expand Down Expand Up @@ -173,7 +174,28 @@ Vision-specific adapter; extracts and adapts the text backbone from Granite Visi

---

## 9. hf\_smollm3.py
## 9. hf\_granite\_swa.py

**HF model\_type:** `granite_swa`

Spyre adapter for `GraniteSWAForCausalLM`. Decoder layers alternate between full
attention and sliding-window attention (`layer_type` on each `GraniteSWADecoderLayer`).
Sliding-window layers restrict attention to a local `sliding_window`-wide band;
full-attention layers use the standard causal mask.

### Verified Checkpoints

| Checkpoint | head\_dim | Notes |
|-----------|---------|-------|
| Granite 4.1 20B (`ibm-research/granite-4.1-20b`) | 128 | ibm-research org; bfloat16 |

### Compatible Models

- Granite 4.1 8B / 20B variants

---

## 10. hf\_smollm3.py

**HF model\_type:** `smollm3`

Expand All @@ -189,7 +211,7 @@ No additional compatible models listed.

---

## 10. hf\_olmo.py
## 11. hf\_olmo.py

**HF model\_type:** `olmo`

Expand All @@ -205,7 +227,7 @@ No additional compatible models listed.

---

## 11. hf\_olmo2.py
## 12. hf\_olmo2.py

**HF model\_type:** `olmo2`

Expand Down
4 changes: 4 additions & 0 deletions hf_adapters/auto_spyre_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
Granite4VisionConfig,
GraniteConfig,
GraniteMoeHybridConfig,
GraniteSWAConfig,
Comment thread
lipikaworkemail-ctrl marked this conversation as resolved.
LlamaConfig,
MistralConfig,
ModernBertConfig,
Expand Down Expand Up @@ -88,6 +89,7 @@
hf_gpt_neo,
hf_gpt_neox,
hf_granite,
hf_granite_swa,
hf_granite_vision,
hf_granite_vision_mm,
hf_granitemoehybrid,
Expand Down Expand Up @@ -128,6 +130,7 @@
Granite4VisionConfig: hf_granite_vision,
GraniteConfig: hf_granite,
GraniteMoeHybridConfig: hf_granitemoehybrid,
GraniteSWAConfig: hf_granite_swa,
LlamaConfig: hf_llama,
MistralConfig: hf_mistral,
MinistralConfig: hf_ministral,
Expand Down Expand Up @@ -189,6 +192,7 @@
"google/gemma-4-31b": torch.bfloat16,
"ibm-granite/granite-4.0-1b-base": torch.float32,
"ibm-granite/granite-4.0-1b": torch.float32,
"ibm-research/granite-4.1-20b": torch.bfloat16,
}


Expand Down
145 changes: 145 additions & 0 deletions hf_adapters/hf_granite_swa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Copyright 2025 The Torch-Spyre Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
HuggingFace Transformers adapter for Granite 4.1 SWA models on Spyre.

GraniteSWAForCausalLM is identical to Granite 3.x except that layers alternate
between full attention and sliding-window attention (``layer_type`` attribute on
each ``GraniteSWADecoderLayer``). Sliding-window layers receive a local attention
mask that restricts each token to attend only within the ``sliding_window`` window;
full-attention layers delegate to ``make_standard_gqa_block`` (same as Granite 3.x).

Usage::

from hf_adapters import AutoSpyreModelForCausalLM
from transformers import AutoTokenizer

model = AutoSpyreModelForCausalLM.from_pretrained(
"/tmp/models/granite-4.1-20b")
tokenizer = AutoTokenizer.from_pretrained("/tmp/models/granite-4.1-20b")
outputs = model.generate(tokenizer, ["Hello!"], max_new_tokens=32)
"""

import torch
import torch.nn.functional as F

from hf_adapters.hf_common import (
apply_rope_matmul,
get_backbone,
kv_cache_update,
make_standard_gqa_block,
pad_lm_head,
patch_rmsnorm,
prepare_rope_and_heads,
)
from hf_adapters.hf_granite import _run_backbone_forward, _run_forward # noqa: F401


def _make_compiled_block(layer, sliding_window: int):
"""Compiled block for a GraniteSWA sliding-window attention layer.

Builds a band mask: positions further than ``sliding_window`` steps back
are set to ``-inf``.
"""
attn = layer.self_attn
mlp = layer.mlp
input_ln = layer.input_layernorm
post_attn_ln = layer.post_attention_layernorm
res_mult = layer.residual_multiplier
v_head_dim = getattr(attn, "v_head_dim", attn.head_dim)

def block_forward(
hidden_states,
selected_freqs,
attn_mask,
key_cache,
value_cache,
is_filling,
token_index,
cache_position,
):
residual = hidden_states
h = input_ln(hidden_states)

bsz, seq_len, _ = h.shape
q = attn.q_proj(h).view(bsz, seq_len, -1, attn.head_dim).transpose(1, 2)
k = attn.k_proj(h).view(bsz, seq_len, -1, attn.head_dim).transpose(1, 2)
v = attn.v_proj(h).view(bsz, seq_len, -1, v_head_dim).transpose(1, 2)

q = apply_rope_matmul(q, selected_freqs)
k = apply_rope_matmul(k, selected_freqs)

key_cache, value_cache = kv_cache_update(
k,
v,
key_cache,
value_cache,
is_filling,
token_index,
cache_position,
)

cache_len = key_cache.shape[2]
q_len = q.shape[2]
q_pos = (token_index + torch.arange(q_len, device=q.device)).unsqueeze(
1
) # [q, 1]
k_pos = torch.arange(cache_len, device=q.device).unsqueeze(0) # [1, k]
window_mask = (q_pos - k_pos) >= sliding_window # [q, k]
swa_mask = attn_mask.clone()
swa_mask = swa_mask.masked_fill(
window_mask.unsqueeze(0).unsqueeze(0), float("-inf")
)

attn_out = F.scaled_dot_product_attention(
q,
key_cache,
value_cache,
attn_mask=swa_mask,
dropout_p=0.0,
scale=attn.scaling,
enable_gqa=True,
)
attn_out = attn_out.transpose(1, 2).reshape(bsz, seq_len, -1)
attn_out = attn.o_proj(attn_out)

h = residual + attn_out * res_mult

residual = h
h = post_attn_ln(h)
h = mlp(h)
h = residual + h * res_mult

return h, key_cache, value_cache

return torch.compile(block_forward, dynamic=False)


def prepare_for_spyre(model):
"""Apply Spyre adaptations to a GraniteSWA model in-place."""
from transformers.models.granite_swa.modeling_granite_swa import GraniteSWARMSNorm

sliding_window = model.config.sliding_window
prepare_rope_and_heads(model)
patch_rmsnorm(GraniteSWARMSNorm)
pad_lm_head(model)
model._spyre_compiled_blocks = [
(
_make_compiled_block(layer, sliding_window)
if getattr(layer, "layer_type", "full_attention") == "sliding_attention"
else make_standard_gqa_block(layer, True)
)
for layer in get_backbone(model).layers
]
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license = { file = "LICENSE.txt" }
requires-python = ">=3.11,<3.15"
dependencies = [
"torch>=2.0",
"transformers>=5.12.1,<6.0.0",
"transformers>=5.15.0,<6.0.0",
"sentencepiece",
"accelerate",
"sentence_transformers",
Expand Down
9 changes: 8 additions & 1 deletion tests/model_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ def _include_gated() -> bool:
"adapter": "hf_granitemoehybrid.py",
"size": "3b",
},
# hf_granite_swa.py
"granite41_20b": {
"name": "Granite 4.1 20B",
"path": "ibm-research/granite-4.1-20b",
"adapter": "hf_granite_swa.py",
Comment thread
lipikaworkemail-ctrl marked this conversation as resolved.
"size": "20b",
},
# hf_granite_vision.py
"granite-vision": {
"name": "Granite Vision 4.1 4B",
Expand Down Expand Up @@ -594,4 +601,4 @@ def xfail_non_blocking(paths: list[str], *, table: dict[str, str]) -> list[objec
},
}

RERANKER_PATHS: list[str] = [m["path"] for m in RERANKER_MODELS.values()]
RERANKER_PATHS: list[str] = [m["path"] for m in RERANKER_MODELS.values()]
Loading
Loading