Add Muse Glimmer model support - #51655
Conversation
|
Documentation preview: https://vllm--51655.org.readthedocs.build/en/51655/ |
|
This pull request has merge conflicts that must be resolved before it can be |
a35a0a9 to
288bd0e
Compare
|
Thanks for this - we have been serving Muse-Glimmer 30B from this branch for a 1. A parameter declared and the whole write is rejected. The value is a string in the tool's own schema; 2. The model sometimes swallows the opening <atem:invoke name="read.filePath">/tmp/norm.txt</atem:parameter>
</atem:invoke>No Patch in xianbaoqian#7, rebased onto the current head of this branch. It Deployment notes, in case they are usefulThe branch itself works. What it took to get there:
One aside that is not about this branch: while chasing the above, none of the |
|
We are using this branch to serve Muse-Glimmer 30B, thanks for adding support. We noticed a possible long-context perf issue in
For long-context structured-output requests, that puts O(context) work in the scheduler hot path once per request per step. This looks like the same class of issue handled in Would it make sense for Muse Glimmer to make the streaming check incremental/windowed as well? For example, decode only a bounded tail containing the current assistant turn, with fallback to the existing full decode if the assistant-turn marker is not found? In a synthetic long-context structured-output test, this shape reduced runtime from ~115.8s to ~7.0s while preserving parser decisions. |
|
Separate possible correctness issue for structured outputs: For requests using structured outputs but no tool calls, That would allow the model to generate unconstrained output even though the request asked for structured output. Is this intended for Muse Glimmer? If not, should the structured-output/no-tool path treat reasoning as ended once the model is in the user/response channel, or otherwise separate the structured-output gate from the tool-parser handoff semantics? |
vLLM 0.27.1 has no Muse Glimmer, so run-muse serves it through the generic Transformers backend -- which is the root cause of every shim in patches/: the dropped embedding RMSNorm, the unread output_multiplier, layer_types read from the wrong config level, and the silently-broken aux hidden state capture. vllm-project/vllm#51655 adds a native implementation. Waiting for it to merge and then rebuilding is a multi-hour proposition and the PR is still moving, so instead vendor the files it ADDS and load them out of tree: ./scripts/muse-native-sync # fetch the PR, vendor it, report what moved NATIVE=1 ./run-muse # serve with it, no rebuild Verified offline against 0.27.1: all five vendored modules import, the config registry resolves muse_glimmer to upstream's MuseGlimmerConfig, and vLLM now reports Resolved architecture: MuseGlimmerForConditionalGeneration -> vllm.model_executor.models.muse_glimmer.MuseGlimmerForCausalLM with no "no vLLM implementation, falling back" warning. Serving is NOT yet verified -- that needs a boot. How it is kept in step with upstream: * vendor/ holds only files the PR ADDS under vllm/ matching muse|glimmer. Nothing by those names exists in 0.27.1, so they can only add modules, never shadow core vLLM. They are byte-identical to upstream and never edited, so a refresh is a copy and never a merge. muse_native.py resolves them under their real dotted names via a MetaPathFinder, so load order does not matter and a file added by a later revision of the PR needs no code change here. * The PR's edits to EXISTING core files cannot be vendored without dragging in an unrelated newer tree, so the few that matter are replayed instead: registry.py:530 as --model-class-overrides, registry.py:631 by patches/muse-dflash, and config.py:104-107 as shim 7 in sitecustomize. muse-native-sync prints those upstream lines on every run, so drift shows up. * UPSTREAM pins the vendored commit. vendor/ itself is gitignored (.gitignore:1, same as every other vendor tree here) and regenerated from that pin; run-muse refuses NATIVE=1 with a pointer to the sync if it is missing. * --check reports without writing and exits non-zero when upstream has moved, so it can be run from cron. NATIVE=0 remains the default and that path is untouched. When NATIVE=1 the shims that exist only to paper over the fallback are disabled: the embed-norm patch (native keeps the norm) and --hf-overrides (native reads output_multiplier itself, and forcing text_config.logit_scale on top would double-scale the logits). Note upstream drives the DFlash draft head with the STOCK qwen3_dflash.DFlashQwen3ForCausalLM -- the same class patches/muse-dflash subclasses. So this is not expected to change the long-context acceptance collapse measured in 6da9c19. Claude-Session: https://claude.ai/code/session_01TY4DzEkNyJv8zFmd6cNMSr
Signed-off-by: Jee Jee Li <jeejeelee@inferact.ai>
|
/ci run |
|
/ci run |
|
✅ Triggered Buildkite CI #83669 for commit |
| # MuseGlimmerForCausalLM marks its inner MuseGlimmerModel as the language | ||
| # model, so get_language_model() already returns the inner module and has | ||
| # no .model of its own. | ||
| target_inner = getattr(target_language_model, "model", target_language_model) |
There was a problem hiding this comment.
Can we pls add same logic to vllm/v1/worker/gpu/spec_decode/dspark/utils.py?
I've been working on DSpark support and I'm getting interesting results ~+28% speedup due to way better acceptance of deep tokens but need that update in DSpark path for it to work.
|
@maxw1489 Ran into this exact thing on a GB10 (DGX Spark, sm_121) with the same image, so can confirm it's not SM120-specific. Spent a while chasing it because the assert fires way downstream in the draft model's attention, but the actual problem is upstream in how the draft buffers get sized. DFlash runs the speculative tokens for every request in one forward pass, so a draft step is max_num_seqs x (1 + num_speculative_tokens) tokens wide. But the draft input buffers (and the compile/profiling shape ranges) all get sized based on max_num_batched_tokens, which gets auto-capped to 2048 for spec decode. With the default max_num_seqs of 256 and num_speculative_tokens=15 that's 256 x 16 = 4096, so it's writing query positions right off the end of a 2048-wide buffer. The positions tensor comes back full of garbage (I was seeing values like 8.6e14) and that's what trips the < max_model_len assert later. Explains why it's fine at num_speculative_tokens=1 (256 x 2 = 512) and why swapping attention backends doesn't help. The corruption already happened before attention runs. Got it working just by bumping the token budget so the math fits: --max-num-batched-tokens 4096 (basically anything >= max_num_seqs x (1 + num_speculative_tokens)). Boots clean at 15 tokens after that and runs great. There's already a warning about max_num_batched_tokens being low but it says "suboptimal performance," which really undersells it since it's a hard crash, not just slow. Only tested on sm_121 with --attention-backend TRITON_ATTN (FA2 is separately broken on this chip), but since the overflow is in input prep it should be the same everywhere. Probably worth vllm just enforcing that relationship or auto-bumping the budget instead of capping to 2048 and walking off the end. |
Muse-Glimmer-30B-assistant has five sliding_attention layers and declares no causality, so it resolves causal under the layer-type default. vllm-project#51655 handled that by treating any uniform layer_types as non-causal, which changes the default for every DFlash and DSpark drafter and breaks test_dflash_causality.py::test_dflash_has_any_non_causal[config3-False] -- the only failure in the amd-v1-spec-decode-mi300-1 job of build 83669. Drop that change, leaving _dflash_layer_causal byte-identical to main, and declare the head's causality on MuseGlimmerAssistantConfig instead. The head is bidirectional over the draft block: transformers' modeling_muse_glimmer_assistant sets is_causal = False and builds bidirectional masks for both layer types, and SGLang declares the same on its config class. SGLang widened the default first and reverted it in sgl-project/sglang#34524 after gemma-4-31B-it-DFlash acceptance fell from 5.62 to 5.27. Checkpoints that declare their own causality are unaffected either way: poolside/Laguna-S-2.1-DFlash, poolside/Laguna-XS-2.1-DFlash and the nvidia Nemotron DSpark head all ship dflash_config.causal, and a checkpoint-supplied value still overrides the default added here. Test: pytest v1/spec_decode/test_dflash_causality.py -> 10 passed, with the test file byte-identical to upstream. Signed-off-by: zixi-qi <zixi@inferact.ai> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Declare Muse Glimmer draft causality instead of widening the DFlash default
|
/ci run |
|
✅ Triggered Buildkite CI #83817 for commit |
load_dspark_model() unconditionally does `target_language_model.model`. For a multimodal target whose get_language_model() already returns the inner decoder (Muse Glimmer), there is no further `.model` and this raises AttributeError before any weights are shared, so method="dspark" cannot start. The dflash path already applies the unwrap-only-if-needed rule; this brings the dspark path in line with it. Unchanged for text-only targets, where `.model` still exists and getattr returns it. Branch = PR vllm-project#51655 + this fix, so a DSpark drafter for Muse Glimmer can be served without local patching.
Signed-off-by: Tiezhen Wang <tiezhen@inferact.ai> Signed-off-by: Jee Jee Li <jeejeelee@inferact.ai> Signed-off-by: zixi-qi <zixi@inferact.ai> Co-authored-by: Beto de Paola <betodepaola@meta.com> Co-authored-by: Giancarlo Delfin <gdelfin@inferact.ai> Co-authored-by: Jee Jee Li <jeejeelee@inferact.ai> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Jee Jee Li <pandaleefree@gmail.com> Co-authored-by: zixi-qi <zixi@inferact.ai>
Signed-off-by: Tiezhen Wang <tiezhen@inferact.ai> Signed-off-by: Jee Jee Li <jeejeelee@inferact.ai> Signed-off-by: zixi-qi <zixi@inferact.ai> Co-authored-by: Beto de Paola <betodepaola@meta.com> Co-authored-by: Giancarlo Delfin <gdelfin@inferact.ai> Co-authored-by: Jee Jee Li <jeejeelee@inferact.ai> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Jee Jee Li <pandaleefree@gmail.com> Co-authored-by: zixi-qi <zixi@inferact.ai> Signed-off-by: Alessandra005 <aurib032@fiu.edu>
Dense 29.6B vision-language model with a ViT-G/14 perception encoder and 128K context. Adds the model, its config and processor, channel-scoped reasoning and ATEM tool-call parsers, and DFlash speculative decoding support for its draft head.
The model does not emit JSON tool calls and does not wrap reasoning in tags. Every turn is a sequence of channel-scoped messages, and both parsers key off that framing, so --tool-call-parser muse_glimmer and --reasoning-parser muse_glimmer are used together. The reasoning parser forces skip_special_tokens=False; without it the markers are stripped before parsing and both channels collapse into content.
tool_choice="required" and named tool_choice set
supports_required_and_named=False so vLLM does not apply JSON guided decoding to them -- that path assumes JSON tool calls, and forcing it here either trapped the call in the reasoning channel or leaked the raw framing into content.
The DFlash draft head (MuseGlimmerAssistantModel) reuses the existing qwen3_dflash implementation: same architecture, same tensors. It reads the target's residual stream at layers [1, 13, 25, 37, 49] and predicts a 16-slot block per forward.
Squashed from the onyx-support integration branch (30 commits).
Purpose
Test Plan
Test Result
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.