Skip to content

Commit 7806c35

Browse files
authored
SWA Granite HF registration (#1017)
## Description Drops the dependency on the granite-swa-model transformers [fork](https://github.com/Bharat-Runwal/transformers/tree/granite-swa-model). Routes `granite_swa` through the generic FMS hf_pretrained/hf_configured path. Pairs with [this](foundation-model-stack/foundation-model-stack#538) FMS registration PR. <!-- Provide a clear description of your changes. --> ## Related Issues none <!-- Link related issues, e.g., `Fixes #` or `Relates to #456` --> ## Test Plan none <!-- Describe how you tested your changes. Include commands or steps to reproduce. --> ## Checklist - [x] I have read the [contributing guidelines](https://docs.vllm.ai/projects/spyre/en/latest/contributing) - [x] My code follows the project's code style (run `bash format.sh`) - [ ] I have added tests for my changes (if applicable) - [ ] I have updated the documentation (if applicable) - [x] My commits include a `Signed-off-by:` line (DCO compliance) --------- Signed-off-by: Yannick Schnider <Yannick.Schnider1@ibm.com>
1 parent a7b1056 commit 7806c35

5 files changed

Lines changed: 122 additions & 3 deletions

File tree

sendnn_inference/model_executor/model_loader/spyre.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,13 @@ def __init__(
178178
self.parallel_config
179179
)
180180

181-
if self.config.model_type in {"llama", "granite", "granitemoehybrid", "qwen3"}:
181+
if self.config.model_type in {
182+
"llama",
183+
"granite",
184+
"granitemoehybrid",
185+
"qwen3",
186+
"granite_swa",
187+
}:
182188
self.kv_cache_specs["num_layers"] = self.config.num_hidden_layers
183189
self.kv_cache_specs["head_dim"] = getattr(
184190
self.fms_model.config,
@@ -237,6 +243,7 @@ def load_weights(
237243
# FMS's `hf_configured` path, which fetches only config.json and then
238244
# random-inits the model via `reset_parameters()`.
239245
variant: str | None = None
246+
source: str | None = None
240247
if self.load_config.load_format == "dummy":
241248
logger.info(
242249
"Loading model %s with random weights.",
@@ -271,6 +278,7 @@ def load_weights(
271278
architecture=architecture,
272279
variant=variant,
273280
model_path=model_path,
281+
source=source,
274282
distributed_strategy=distributed_strategy,
275283
group=dist.group.WORLD,
276284
fused_weights=False,

sendnn_inference/platform.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,25 @@ def _get_matching_warmup_shapes(
582582

583583
@classmethod
584584
def pre_register_and_update(cls, parser: FlexibleArgumentParser | None = None) -> None:
585+
# vLLM has no GraniteSWAForCausalLM architecture. FMS loads the real weights
586+
# via the hf_pretrained path (model_loader/spyre.py); vLLM only needs
587+
# a class for metadata (runner_type, defaults), so alias it to GraniteForCausalLM.
588+
from vllm import ModelRegistry
589+
590+
if "GraniteSWAForCausalLM" not in ModelRegistry.get_supported_archs():
591+
ModelRegistry.register_model(
592+
"GraniteSWAForCausalLM",
593+
"vllm.model_executor.models.granite:GraniteForCausalLM",
594+
)
595+
596+
# Importing fms.models.hf registers the granite_swa model_type with
597+
# transformers AutoConfig. This must run before vLLM's frontend parses
598+
# config.json (in ModelConfig) so a granite_swa checkpoint loads without a
599+
# granite_swa implementation in transformers. Done here (not in
600+
# sendnn_inference/__init__.py, which deliberately avoids importing torch
601+
# early); pre_register_and_update runs before ModelConfig is built.
602+
import fms.models.hf # noqa: F401
603+
585604
if parser is None:
586605
return
587606

sendnn_inference/v1/worker/spyre_model_runner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ def __init__(
144144
self.pad_token_id = getattr(self.model_config.hf_config, "pad_token_id", None) or 0
145145
if self.model_config.get_sliding_window():
146146
logger.warning(
147-
"Sliding window is not supported on Spyre. "
148-
"The model will run without sliding window."
147+
"Sliding window is not fully supported on Spyre yet."
148+
"The model will run with experimental sliding window support."
149149
)
150150

151151
if vllm_config.device_config is None:

tests/e2e/test_granite_swa_load.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""Load-path test for the granite_swa model_type.
2+
3+
Constructing the LLM with --load-format dummy on a tiny granite_swa fixture
4+
flexes every granite_swa code path (each raises on failure):
5+
1. SpyrePlatform.pre_register_and_update — GraniteSWAForCausalLM arch alias +
6+
`import fms.models.hf` (granite_swa AutoConfig registration).
7+
2. vLLM ModelConfig / AutoConfig parse of model_type="granite_swa" + arch resolution.
8+
3. SpyreCausalLM.load_weights granite_swa kv-cache branch (no NotImplementedError).
9+
4. FMS hf_configured path building a native GraniteSWA via reset_parameters.
10+
5. The Spyre warmup that LLM() construction triggers (compile_or_warm_up_model
11+
-> _warmup_spyre_dynamic_size), which runs prefill + decode forward passes
12+
with dummy requests on eager CPU -- so a forward is exercised without generate().
13+
14+
Temporary: granite_swa is loaded via the FMS AutoConfig shim because transformers
15+
has no granite_swa implementation yet. Once transformers ships granite_swa natively,
16+
that code and this test will be removed.
17+
"""
18+
19+
from pathlib import Path
20+
21+
import pytest
22+
23+
# granite_swa ships only in FMS builds with fms/models/granite_swa.py, not in
24+
# released ibm-fms (<=1.12.1, which CI pins via uv.lock). Without it,
25+
# `import fms.models.hf` can't register granite_swa with AutoConfig and the load
26+
# fails. Skip cleanly where it's absent so CI stays green until FMS ships it.
27+
pytest.importorskip("fms.models.granite_swa")
28+
29+
from vllm import LLM, ModelRegistry # noqa: E402
30+
31+
from sendnn_inference import envs as envs_spyre # noqa: E402
32+
33+
TINY_GRANITE_SWA_DIR = str(
34+
Path(__file__).parent.parent / "fixtures" / "model_configs" / "ibm-granite" / "tiny-granite-swa"
35+
)
36+
# Reuse the cached micro-g3.3 tokenizer; the fixture's vocab_size (49159) matches it.
37+
TOKENIZER = "ibm-ai-platform/micro-g3.3-8b-instruct-1b"
38+
TOKENIZER_REVISION = "6e9c6465a9d7e5e9fa35004a29f0c90befa7d23f"
39+
40+
41+
@pytest.mark.cpu
42+
@pytest.mark.decoder
43+
def test_granite_swa_dummy_load():
44+
"""A granite_swa config loads via the dummy (random-init) path. No inference
45+
asserted — reaching the assertions means every granite_swa path above succeeded."""
46+
envs_spyre.override("SENDNN_INFERENCE_DYNAMO_BACKEND", "eager")
47+
48+
llm = LLM(
49+
model=TINY_GRANITE_SWA_DIR,
50+
tokenizer=TOKENIZER,
51+
tokenizer_revision=TOKENIZER_REVISION,
52+
load_format="dummy",
53+
max_model_len=512,
54+
max_num_seqs=4,
55+
max_num_batched_tokens=128,
56+
)
57+
58+
assert llm is not None
59+
# Cheap, version-stable confirmation the granite_swa arch alias was registered.
60+
assert "GraniteSWAForCausalLM" in ModelRegistry.get_supported_archs()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"architectures": ["GraniteSWAForCausalLM"],
3+
"model_type": "granite_swa",
4+
"vocab_size": 49159,
5+
"hidden_size": 128,
6+
"intermediate_size": 256,
7+
"num_hidden_layers": 4,
8+
"num_attention_heads": 4,
9+
"num_key_value_heads": 2,
10+
"head_dim": 32,
11+
"hidden_act": "silu",
12+
"max_position_embeddings": 2048,
13+
"initializer_range": 0.02,
14+
"rms_norm_eps": 1e-05,
15+
"attention_bias": false,
16+
"attention_dropout": 0.0,
17+
"mlp_bias": false,
18+
"embedding_multiplier": 1.0,
19+
"logits_scaling": 1.0,
20+
"residual_multiplier": 1.0,
21+
"attention_multiplier": 1.0,
22+
"sliding_window": 128,
23+
"layer_types": ["full_attention", "sliding_attention", "sliding_attention", "full_attention"],
24+
"rope_theta": 10000.0,
25+
"rope_parameters": {"rope_type": "default", "rope_theta": 10000.0},
26+
"tie_word_embeddings": true,
27+
"bos_token_id": 0,
28+
"eos_token_id": 0,
29+
"pad_token_id": 0,
30+
"use_cache": true,
31+
"dtype": "float32"
32+
}

0 commit comments

Comments
 (0)