Skip to content

[Model] Remove native LongCat implementations - #53811

Open
xianbaoqian wants to merge 2 commits into
vllm-project:mainfrom
xianbaoqian:remove-longcat-native
Open

xianbaoqian wants to merge 2 commits into
vllm-project:mainfrom
xianbaoqian:remove-longcat-native

Conversation

@xianbaoqian

@xianbaoqian xianbaoqian commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Mutually agree with the LongCat team.


Depends on:


Purpose

Remove the native LongCat-Flash Chat, Lite, and MTP implementations and route the supported causal-LM architectures through the Transformers modeling backend, following the migration pattern from #53608 and #53615.

This change:

  • routes LongcatFlashForCausalLM and LongcatFlashNgramForCausalLM to TransformersMoEForCausalLM;
  • removes the native model files, native MTP integration, and the now-unused n-gram CUDA operator;
  • teaches the generic MoE fuser to load LongCat's nested router projection and preserve zero-expert routing;
  • handles the public Chat checkpoint's missing model_type without executing its stale remote modeling code;
  • preserves LongCat's dual-attention layer accounting and the Lite compilation/cudagraph safeguard;
  • documents that Chat uses the official Transformers implementation while Lite still requires --trust-remote-code for its custom n-gram wrapper.

Duplicate-work checks found no open PR for LongCat removal or migration to the Transformers backend. Open LongCat feature PRs are unrelated and the generic LongCat tool parser is intentionally retained.

This PR was prepared with AI assistance. The human submitter must review and understand every changed line before marking it ready for review.

Test Plan

.venv/bin/pre-commit run --from-ref origin/main --to-ref HEAD
.venv/bin/python -m pytest tests/models/transformers/fusers/test_moe.py tests/transformers_utils/test_config.py -k 'moe_fuser or longcat' -q
.venv/bin/python -m pytest tests/models/test_registry.py -k LongcatFlash -q
.venv/bin/python -m pytest tests/config/test_model_arch_config.py -k LongCat -q

Before marking ready:

  • Run a LongCat generation/evaluation comparison on GPU.
  • Build the removed CUDA extension paths in a CUDA development environment.
  • Complete human review of every changed line.

Test Result

  • Pre-commit: passed, including Ruff, mypy, clang-format, Markdown, and repository policy hooks.
  • MoE fuser and LongCat config tests: 19 passed.
  • LongCat registry tests: 2 passed.
  • LongCat architecture regression test against the public Chat checkpoint: 1 passed.
  • Both public Chat and Lite configs resolved to TransformersMoEForCausalLM; meta-device construction matched all LongCat MoE and MLA blocks.
  • Chat resolved through the built-in Transformers config without trusted remote code. Lite resolved through its remote n-gram config with --trust-remote-code.
  • GPU model evaluation: not run because no GPU is available in the development environment; this draft must not be marked ready until results are added.
  • CUDA build: not run because CUDA/CMake toolchains are unavailable in the development environment.

@mergify

mergify Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Documentation preview: https://vllm--53811.org.readthedocs.build/en/53811/

@mergify mergify Bot added documentation Improvements or additions to documentation ci/build labels Aug 26, 2026
Route LongCat-Flash models through the Transformers backend and retain the compatibility handling needed by Chat and Lite.

Assisted-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Xianbao QIAN <xianbao.qian@gmail.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@DarkLight1337

Copy link
Copy Markdown
Member

I am concerned this will conflict with the WIP model PRs. Maybe wait for them to be merged first?

@xianbaoqian

Copy link
Copy Markdown
Contributor Author

I am concerned this will conflict with the WIP model PRs. Maybe wait for them to be merged first?

#48553 Do you mean this one?

@DarkLight1337

Copy link
Copy Markdown
Member

No, I mean Qwen3.8-Next and GLM5.3-Next. They touch a lot of files

@mergify

mergify Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @xianbaoqian.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? This config is already registered in Transformers

Comment on lines +556 to +558
class LongCatModelArchConfigConvertor(ModelArchConfigConvertorBase):
def get_num_hidden_layers(self) -> int:
return self.hf_text_config.num_layers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What reads this?

Comment on lines +281 to +285
hf_linear = (
hf_gate.get_submodule(self.gate_linear_name)
if self.gate_linear_name
else hf_gate
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be unified to:

        hf_gate_weight = hf_gate.get_submodule(self.gate_weight_location)

In the case where the weight belongs directly to the gate self.gate_weight_location will be "" and get_submodule will just return hf_gate

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the gate_linear_name -> gate_weight_location is playing a big part in clarity. I spent the first 5 minutes trying to figure out where another linear layer was coming from

Comment on lines +472 to +477
num_decoder_layers = (
self.text_config.num_layers
if self.text_config.model_type
in ("longcat_flash", "longcat_flash_ngram")
else self.text_config.num_hidden_layers
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to store the layers_name from pipeline_parallel rather than use model specific attributes like this


# kv_lora_rank indicates that this is an MLA model
if getattr(text_config, "kv_lora_rank", None) is not None:
num_attn_module = (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can continue to use num_hidden_layers as the ground truth for "how many attention modules are in this model". Then we can infer this number from num_hidden_layers / len(layers)

Conflicts resolved:
- CMakeLists.txt, csrc/libtorch_stable/{ops.h,torch_bindings.cpp},
  vllm/_custom_ops.py: keep main's vocab_parallel_embedding op, drop the
  LongCat ngram_compute_n_gram_ids kernel along with its sources.
- docs/models/supported_models.md, vllm/model_executor/models/registry.py:
  keep both sides (LongCat entries plus main's NanbeigeForCausalLM).
- tests/transformers_utils/test_config.py: keep both imports.
- vllm/model_executor/models/transformers/base.py: take main's side. Its
  refactor keys attention by AttentionFuser.layer_index, which already
  handles LongCat's two attention sublayers per decoder layer, so the
  extract_layer_index(prefix, num_attn_module) workaround is superseded.
- vllm/model_executor/models/longcat_flash{,_mtp,_ngram}.py: stay deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
@mergify mergify Bot removed the needs-rebase label Sep 17, 2026
) -> tuple[str, torch.dtype | None, str] | None:
"""Matches `topk(score(linear(x)))`, `score` being `softmax`/`sigmoid`.

Returns the scoring function and the dtype the router computes in."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring should be updated

@mergify

mergify Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @xianbaoqian.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/build documentation Improvements or additions to documentation needs-rebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants