Fix LoRA adapter silently failing on Pixtral/Ministral-3 models#34964
Fix LoRA adapter silently failing on Pixtral/Ministral-3 models#34964timon0305 wants to merge 1 commit intovllm-project:mainfrom
Conversation
…or LoRA support Signed-off-by: timon0305 <timon0305@outlook.com>
There was a problem hiding this comment.
Code Review
The pull request correctly identifies and addresses the silent failure of LoRA adapters on Pixtral models by adding a WeightsMapper and defensive warnings. However, the mapping prefixes in pixtral.py appear to mismatch the actual attribute names used in the model implementation, which will likely prevent the fix from working as intended. Additionally, a redundant and potentially inefficient check was added to the worker manager.
| model_module_names = {name for name, _ in model.named_modules()} | ||
| matched = any( | ||
| module_name in model_module_names for module_name in lora.loras | ||
| ) |
There was a problem hiding this comment.
This check is redundant because a more precise check is performed in LoRAModelManager.activate_adapter (which is called immediately after loading in add_adapter). Furthermore, calling model.named_modules() to build a set on every adapter load is inefficient for large models like Pixtral (12B). If an early check is desired, it should be performed against self._adapter_manager.modules, which contains the actual LoRA-eligible module names.
| hf_to_vllm_mapper = WeightsMapper( | ||
| orig_to_new_prefix={ | ||
| "model.language_model.": "language_model.model.", | ||
| "model.vision_tower.": "vision_tower.", | ||
| "model.multi_modal_projector.": "multi_modal_projector.", | ||
| "lm_head.": "language_model.lm_head.", | ||
| } | ||
| ) |
There was a problem hiding this comment.
The target prefixes in the WeightsMapper do not match the actual attribute names defined in PixtralForConditionalGeneration. Specifically, vision_tower should be vision_encoder and multi_modal_projector should be vision_language_adapter. Additionally, based on the PR description, mapping model.language_model. to language_model.model. will result in an extra model. prefix (e.g., language_model.model.model.layers...), as the internal vLLM path is language_model.model.layers....
| hf_to_vllm_mapper = WeightsMapper( | |
| orig_to_new_prefix={ | |
| "model.language_model.": "language_model.model.", | |
| "model.vision_tower.": "vision_tower.", | |
| "model.multi_modal_projector.": "multi_modal_projector.", | |
| "lm_head.": "language_model.lm_head.", | |
| } | |
| ) | |
| hf_to_vllm_mapper = WeightsMapper( | |
| orig_to_new_prefix={ | |
| "model.language_model.": "language_model.", | |
| "model.vision_tower.": "vision_encoder.", | |
| "model.multi_modal_projector.": "vision_language_adapter.", | |
| "lm_head.": "language_model.lm_head.", | |
| } | |
| ) |
| "no effect. This usually means the model class is missing " | ||
| "an hf_to_vllm_mapper attribute.", | ||
| lora_model.id, | ||
| ) |
There was a problem hiding this comment.
These warnings may be misleading, please delete them
|
Thank you for your contribution! Since #36963 is a superset of this PR, I'll go ahead and close this one. |
Purpose
Fix LoRA adapters silently having no effect when loaded on Pixtral-based models
(e.g.
mistralai/Ministral-3-8B-Instruct-2512).The root cause is that
PixtralForConditionalGenerationis missing thehf_to_vllm_mapperattribute that other multimodal models (likeMistral3ForConditionalGeneration) define. Without this mapper, LoRA weightnames from HuggingFace checkpoints (e.g.
model.language_model.model.layers.…)are not translated to vLLM's internal names (e.g.
language_model.model.layers.…),so no LoRA modules match and the adapter is silently ignored.
Changes:
hf_to_vllm_mappertoPixtralForConditionalGeneration(same mapping asMistral3ForConditionalGeneration)LoRAModelManager.activate_adapter()when none of the adapterweights match any LoRA-eligible module
WorkerLoRAManager._load_adapter()when loaded LoRA modulenames don't match any model module
Fixes #34591
Test Plan
Mistral3ForConditionalGenerationinHuggingFace format correctly apply when served via
PixtralForConditionalGenerationmatch (e.g. by intentionally removing the mapper)
Test Result
Pre-commit hooks all pass (ruff check, ruff format, mypy, typos, etc.)