[Agent] Add NPU main2main skill (#2858)

Signed-off-by: gcanlin <canlinguosdu@gmail.com>
This commit is contained in:
Canlin Guo authored and GitHub committed 2026-04-18 21:57:39 +08:00
1 parent 9cf1fe71f4
commit 9313f37056
4 files changed
+1231

No files matched your search

@@ -0,0 +1,300 @@
---
name: vllm-omni-npu-model-runner-upgrade
description: "Upgrade vllm-omni NPU model runners (OmniNPUModelRunner, NPUARModelRunner, NPUGenerationModelRunner) to align with the latest vllm-ascend NPUModelRunner while preserving omni-specific logic."
---
# vLLM-Omni NPU Model Runner Upgrade Skill
## Overview
This skill guides the process of upgrading vllm-omni's NPU model runners to align with the latest vllm-ascend codebase while preserving omni-specific enhancements. The NPU runners are designed to run omni multimodal models (like Qwen3-Omni, Bagel, MiMoAudio) on Ascend NPUs.
## File Structure
### NPU Model Runner Files
```
vllm-omni/vllm_omni/platforms/npu/worker/
├── __init__.py
├── npu_model_runner.py # OmniNPUModelRunner (base class)
├── npu_ar_model_runner.py # NPUARModelRunner (autoregressive)
├── npu_ar_worker.py # AR worker
├── npu_generation_model_runner.py # NPUGenerationModelRunner (diffusion/non-AR)
└── npu_generation_worker.py # Generation worker
```
### GPU Reference Files (for omni-specific logic sync)
```
vllm-omni/vllm_omni/worker/
├── __init__.py
├── gpu_model_runner.py # OmniGPUModelRunner
├── gpu_ar_model_runner.py # GPUARModelRunner
├── gpu_ar_worker.py
├── gpu_generation_model_runner.py
├── gpu_generation_worker.py
├── mixins.py
├── base.py
└── gpu_memory_utils.py
```
### vllm-ascend Reference Files
```
vllm-ascend/vllm_ascend/worker/
├── model_runner_v1.py # NPUModelRunner (base class to copy from)
├── npu_input_batch.py
├── block_table.py
├── pcp_utils.py
└── worker.py
```
## Inheritance Hierarchy
```
GPUModelRunner (vllm)
|
+----------------+----------------+
| |
OmniGPUModelRunner NPUModelRunner (vllm-ascend)
(vllm_omni/worker) (vllm_ascend/worker)
| |
+----------- OmniNPUModelRunner --+
(multiple inheritance)
|
+---------------+---------------+
| |
NPUARModelRunner NPUGenerationModelRunner
(autoregressive) (non-autoregressive/diffusion)
```
## Omni-Specific Comment Markers
Omni-specific logic is marked with comment blocks:
```python
# -------------------------------------- Omni-new -------------------------------------------------
# ... omni-specific code ...
# -------------------------------------- Omni-new -------------------------------------------------
```
Or simpler variations:
```python
# -------------------------------------- Omni-new -------------------------------------------------
# ------------------------------------------------------------------------------------------------
```
**Important**:
- Always preserve and add these markers when modifying code.
- **The reference documents (`references/omni-specific-blocks.md`) may not be up-to-date.** Always grep for `Omni-new` in the GPU implementations to find the authoritative list of omni-specific blocks.
- When you discover new omni-specific code that is not documented in the references, please update the reference files.
## Key Methods Requiring Attention
### OmniNPUModelRunner (npu_model_runner.py)
| Method | Description | Omni-Specific Logic |
|--------|-------------|---------------------|
| `load_model` | Load model and initialize talker_mtp | Uses `ACLGraphWrapper` instead of `CUDAGraphWrapper`, initializes talker buffers |
| `_dummy_run` | Warmup/profiling run | talker_mtp dummy forward, `extract_multimodal_outputs` |
| `_model_forward` | Forward pass wrapper | Injects `model_kwargs_extra`, wraps with `OmniOutput`, NPU-specific graph updates |
| `_talker_mtp_forward` | Talker MTP forward for Qwen3-Omni | Uses `set_ascend_forward_context` |
### NPUARModelRunner (npu_ar_model_runner.py)
| Method | Description | Omni-Specific Logic |
|--------|-------------|---------------------|
| `__init__` | Initialize with KV transfer manager | `OmniKVTransferManager` setup |
| `execute_model` | Main inference entry | KV transfer handling, `_update_states` override, `extract_multimodal_outputs` |
| `sample_tokens` | Token sampling | Hidden states extraction, multimodal outputs processing, `OmniModelRunnerOutput` |
| `_resolve_global_request_id` | Request ID resolution | For disaggregated inference |
### NPUGenerationModelRunner (npu_generation_model_runner.py)
| Method | Description | Omni-Specific Logic |
|--------|-------------|---------------------|
| `_update_request_states` | Update request states for async chunk | async_chunk handling |
| `execute_model` | Generation forward | async_chunk, `seq_token_counts`, `_run_generation_model` |
| `sample_tokens` | Output processing | multimodal output packaging to `OmniModelRunnerOutput` |
| `_dummy_run` | Dummy run override | model_kwargs initialization, multimodal extraction |
| `_run_generation_model` | Run generation model | Calls `_model_forward` with sampler |
## Upgrade Workflow
### Step 1: Preparation
1. **Identify target versions**(Use gh cli to check):
- We're using vllm-omni main branch
- Check the last release of vllm-omni
- Target vllm-ascend version(Just directly use the local latest vllm-ascend code)
2. **Check GPU-side changes** (since last release):
```bash
cd /root/vllm-workspace/vllm-omni
git log --oneline --since="<last-release-date>" -- vllm_omni/worker/
```
3. **Read latest vllm-ascend code**:
- We don't track vllm-ascend changes - just directly use the latest code from `/root/vllm-workspace/vllm-ascend/vllm_ascend/worker/model_runner_v1.py`
- Copy the relevant methods and re-insert omni-specific blocks
### Step 2: Analyze Omni-Specific Logic
For each NPU model runner file:
1. **Extract existing omni-specific blocks**:
```bash
grep -n "Omni-new" vllm_omni/platforms/npu/worker/npu_model_runner.py
```
2. **Document each omni block**:
- Which method it belongs to
- What functionality it provides
- Dependencies on other omni code
### Step 3: Update Base Class (OmniNPUModelRunner)
**Note**: Always check the GPU implementation `gpu_model_runner.py` for any new omni logic not yet documented in references.
1. **Read the latest vllm-ascend `NPUModelRunner.load_model`**
2. **Copy the method, keeping the structure**
3. **Re-insert omni-specific logic** (check GPU `gpu_model_runner.py` for authoritative list):
- Replace `CUDAGraphWrapper` with `ACLGraphWrapper`
- Keep talker_mtp initialization
- Preserve buffer allocations for talker
- Check for any new omni blocks added since last sync
4. **Update `_dummy_run`**:
- Copy from vllm-ascend
- Compare with GPU `_dummy_run` for omni-specific blocks
- Re-insert all `Omni-new` marked code from GPU version
5. **Update `_model_forward`**:
- Keep the omni wrapper logic
- Update NPU-specific parts (graph params, SP all-gather)
- Check GPU version for any new omni logic
### Step 4: Update AR Model Runner
1. **Compare with GPU `gpu_ar_model_runner.py`** for any new omni features
2. **Copy `execute_model` from vllm-ascend**
3. **Re-insert omni blocks** (reference `references/omni-specific-blocks.md`, but note it may be incomplete):
- **IMPORTANT**: Always check the GPU implementation `gpu_ar_model_runner.py` for all `Omni-new` marked code blocks
- The reference doc may not include newly added omni logic - treat it as a starting point, not exhaustive
- When discovering new omni code blocks, please update `references/omni-specific-blocks.md`
- Common omni blocks include but are not limited to: KV transfer, multimodal outputs, sampling_metadata handling, etc.
4. **Update `sample_tokens`** (also compare with GPU implementation):
- Compare with `gpu_ar_model_runner.py`'s `sample_tokens` method
- Identify all `Omni-new` marked code blocks
- Ensure NPU version includes all omni-specific logic
### Step 5: Update Generation Model Runner
**Note**: Generation model runner may have unique omni logic for diffusion/non-AR models.
1. **Compare with GPU `gpu_generation_model_runner.py`** - grep for all `Omni-new` blocks
2. **Update `execute_model`**:
- Check GPU version for all omni-specific blocks
- Keep async_chunk handling
- Keep `seq_token_counts` injection
- Update forward/context setup from vllm-ascend
- Look for any new omni logic not documented in references
3. **Update `_dummy_run`**:
- Copy from vllm-ascend base
- Compare with GPU `_dummy_run` if exists
- Re-insert all omni-specific logic
### Step 6: Update Imports
Check and update imports at the top of each file:
```python
# Common vllm-ascend imports
from vllm_ascend.ascend_forward_context import get_forward_context, set_ascend_forward_context
from vllm_ascend.attention.attention_v1 import AscendAttentionState
from vllm_ascend.attention.utils import using_paged_attention
from vllm_ascend.compilation.acl_graph import ACLGraphWrapper, update_full_graph_params
from vllm_ascend.ops.rotary_embedding import update_cos_sin
from vllm_ascend.utils import enable_sp, lmhead_tp_enable
from vllm_ascend.worker.model_runner_v1 import SEQ_LEN_WITH_MAX_PA_WORKSPACE, NPUModelRunner
# Omni-specific imports
from vllm_omni.model_executor.models.output_templates import OmniOutput
from vllm_omni.worker.gpu_model_runner import OmniGPUModelRunner
from vllm_omni.outputs import OmniModelRunnerOutput
from vllm_omni.distributed.omni_connectors.kv_transfer_manager import OmniKVTransferManager
```
### Step 7: Sync GPU-Side Omni Changes
1. **Check recent GPU worker changes**:
```bash
git diff <from-tag>..<to-tag> -- vllm_omni/worker/gpu_model_runner.py
git diff <from-tag>..<to-tag> -- vllm_omni/worker/gpu_ar_model_runner.py
```
2. **Identify new omni features** that need to be ported to NPU
3. **Apply corresponding changes** to NPU runners
### Step 8: Validation
1. **Run type checking**:
```bash
cd /root/vllm-workspace/vllm-omni
python -m py_compile vllm_omni/platforms/npu/worker/npu_model_runner.py
python -m py_compile vllm_omni/platforms/npu/worker/npu_ar_model_runner.py
python -m py_compile vllm_omni/platforms/npu/worker/npu_generation_model_runner.py
```
2. **Run import test**:
```bash
python -c "from vllm_omni.platforms.npu.worker import *"
```
3. **Run model serving test** (if hardware available):
```bash
vllm serve <model-path> --trust-remote-code
```
## Common Pitfalls
### 1. Forward Context Differences
- GPU uses `set_forward_context`
- NPU uses `set_ascend_forward_context`
- Parameters may differ slightly
### 2. Graph Wrapper Differences
- GPU: `CUDAGraphWrapper`
- NPU: `ACLGraphWrapper`
- Constructor parameters may differ
### 3. Buffer Creation
- GPU: `_make_buffer` returns different structure
- NPU: May need numpy=True/False parameter
### 4. Attention Metadata
- GPU: Uses vllm attention metadata builders
- NPU: Uses `AscendCommonAttentionMetadata`
### 5. Sampling
- GPU: Uses vllm sampler
- NPU: Uses `AscendSampler`
## Checklist Before Commit
- [ ] All omni-specific comment markers preserved
- [ ] New omni logic from GPU side synced
- [ ] Imports updated to latest vllm-ascend
- [ ] No `CUDAGraphWrapper` references in NPU code
- [ ] `set_ascend_forward_context` used instead of `set_forward_context`
- [ ] `ACLGraphWrapper` used for talker_mtp wrapping
- [ ] Type hints match vllm-ascend signatures
- [ ] No duplicate code blocks
- [ ] Python syntax valid (py_compile passes)
## Reference Files for Comparison
When upgrading, keep these files open for reference:
1. **vllm-ascend NPUModelRunner**: `/root/vllm-workspace/vllm-ascend/vllm_ascend/worker/model_runner_v1.py`
2. **vllm GPUModelRunner**: `/root/vllm-workspace/vllm/vllm/v1/worker/gpu_model_runner.py`
3. **vllm-omni OmniGPUModelRunner**: `/root/vllm-workspace/vllm-omni/vllm_omni/worker/gpu_model_runner.py`
@@ -0,0 +1,335 @@
# GPU to NPU Translation Patterns
This document provides a quick reference for translating GPU code patterns to NPU equivalents when porting omni-specific logic.
## Import Translations
### Forward Context
```python
# GPU
from vllm.forward_context import set_forward_context
# NPU
from vllm_ascend.ascend_forward_context import set_ascend_forward_context
```
### Graph Wrapper
```python
# GPU
from vllm.compilation.cuda_graph import CUDAGraphWrapper
# NPU
from vllm_ascend.compilation.acl_graph import ACLGraphWrapper
```
### Attention State
```python
# GPU (no equivalent - uses FlashAttention states directly)
# NPU
from vllm_ascend.attention.attention_v1 import AscendAttentionState
```
### Utilities
```python
# GPU
# (directly use torch.cuda functions)
# NPU
from vllm_ascend.utils import enable_sp, lmhead_tp_enable
from vllm_ascend.ops.rotary_embedding import update_cos_sin
```
## Context Manager Translations
### Forward Context Setup
```python
# GPU
with set_forward_context(
attn_metadata,
self.vllm_config,
num_tokens=num_tokens_padded,
num_tokens_across_dp=num_tokens_across_dp,
cudagraph_runtime_mode=cudagraph_mode,
batch_descriptor=batch_desc,
):
# forward pass
# NPU
with set_ascend_forward_context(
attn_metadata,
self.vllm_config,
num_tokens=num_tokens_padded,
num_tokens_across_dp=num_tokens_across_dp,
aclgraph_runtime_mode=cudagraph_mode, # Note: 'aclgraph' not 'cudagraph'
batch_descriptor=batch_desc,
num_actual_tokens=scheduler_output.total_num_scheduled_tokens,
model_instance=self.model,
):
# forward pass
```
### Graph Capture Context
```python
# GPU
from vllm.compilation.cuda_graph import graph_capture as cuda_graph_capture
with cuda_graph_capture(self.device):
# capture
# NPU
from vllm_ascend.worker.model_runner_v1 import graph_capture
with graph_capture(self.device):
# capture
```
## Graph Wrapper Usage
### Creating Graph Wrapper
```python
# GPU
if cudagraph_mode.has_full_cudagraphs() and has_separate_talker:
self.talker_mtp = CUDAGraphWrapper(
talker_mtp,
self.vllm_config,
runtime_mode=CUDAGraphMode.FULL
)
# NPU
if cudagraph_mode.has_full_cudagraphs() and has_separate_talker:
self.talker_mtp = ACLGraphWrapper(
talker_mtp,
self.vllm_config,
runtime_mode=CUDAGraphMode.FULL
)
```
### Checking Graph Wrapper Type
```python
# GPU
if not isinstance(self.talker_mtp, CUDAGraphWrapper):
_cudagraph_mode = CUDAGraphMode.NONE
# NPU
if not isinstance(self.talker_mtp, ACLGraphWrapper):
_cudagraph_mode = CUDAGraphMode.NONE
```
## Device Operations
### Synchronization
```python
# GPU
torch.cuda.synchronize()
# NPU
torch.npu.synchronize()
```
### Stream Operations
```python
# GPU
stream = torch.cuda.Stream(device=device)
torch.cuda.current_stream()
# NPU
stream = torch.npu.Stream(device=device)
torch.npu.current_stream()
```
## Attention Metadata
### State Setting (NPU-specific)
```python
# GPU - handled internally by attention backends
# NPU - explicit state setting required
self.attn_state = AscendAttentionState.DecodeOnly
if self.speculative_config and self.speculative_config.method == "mtp":
if self.vllm_config.model_config.use_mla:
self.attn_state = AscendAttentionState.SpecDecoding
else:
self.attn_state = AscendAttentionState.ChunkedPrefill
```
### Building Attention Metadata
```python
# GPU - uses vllm attention builders
# NPU - may need additional parameters
(attn_metadata, spec_decode_common_attn_metadata) = self._build_attention_metadata(
num_tokens=num_tokens_unpadded,
num_tokens_padded=num_tokens_padded,
num_reqs=num_reqs,
num_reqs_padded=num_reqs_padded,
max_query_len=max_num_scheduled_tokens,
ubatch_slices=ubatch_slices_attn,
logits_indices=logits_indices,
use_spec_decode=use_spec_decode,
num_scheduled_tokens=scheduler_output.num_scheduled_tokens,
num_scheduled_tokens_np=num_scheduled_tokens_np,
cascade_attn_prefix_lens=cascade_attn_prefix_lens,
)
```
## Rotary Embedding
### Update Cos/Sin Cache
```python
# GPU - typically handled inside attention
# NPU - explicit update required before forward
from vllm_ascend.ops.rotary_embedding import update_cos_sin
update_cos_sin(positions)
```
## Sequence Parallelism
### Enable SP Check
```python
# GPU - use vllm distributed utilities
# NPU - use vllm-ascend wrapper
from vllm_ascend.utils import enable_sp
if enable_sp():
# sequence parallelism enabled
```
## Sampler
### Sampler Type
```python
# GPU - uses vllm sampler
self.sampler = Sampler()
# NPU - uses AscendSampler
from vllm_ascend.sample.sampler import AscendSampler
self.sampler = AscendSampler()
```
## Input Batch
### Batch Class
```python
# GPU
from vllm.v1.worker.gpu_input_batch import InputBatch
# NPU
from vllm_ascend.worker.npu_input_batch import NPUInputBatch
```
## Graph Parameter Updates
### Full Graph Params Update (NPU-specific)
```python
# GPU - not needed
# NPU - required for FULL graph mode
from vllm_ascend.compilation.acl_graph import update_full_graph_params
forward_context = get_forward_context()
if (
forward_context.cudagraph_runtime_mode == CUDAGraphMode.FULL
and not forward_context.capturing
and not self.use_sparse
):
update_full_graph_params(
self.attn_backend,
self.update_stream,
forward_context,
num_tokens_padded,
self.vllm_config,
self.speculative_config,
positions.shape[0],
)
```
## Paged Attention Check
```python
# GPU - not typically needed
# NPU
from vllm_ascend.attention.utils import using_paged_attention
if is_graph_capturing and using_paged_attention(num_tokens, self.vllm_config):
seq_lens = SEQ_LEN_WITH_MAX_PA_WORKSPACE
```
## Common Method Signature Differences
### _dummy_run Parameters
```python
# GPU (v0.17.0)
def _dummy_run(
self,
num_tokens: int,
cudagraph_runtime_mode: CUDAGraphMode | None = None,
force_attention: bool = False,
uniform_decode: bool = False,
allow_microbatching: bool = True,
skip_eplb: bool = False,
is_profile: bool = False,
create_mixed_batch: bool = False,
remove_lora: bool = True,
is_graph_capturing: bool = False,
num_active_loras: int = 0,
) -> tuple[torch.Tensor, torch.Tensor]:
# NPU (v0.17.0) - adds with_prefill, activate_lora
def _dummy_run(
self,
num_tokens: int,
with_prefill: bool = False,
cudagraph_runtime_mode: CUDAGraphMode | None = None,
force_attention: bool = False,
uniform_decode: bool = False,
is_profile: bool = False,
create_mixed_batch: bool = False,
allow_microbatching: bool = True,
skip_eplb: bool = False,
remove_lora: bool = True,
activate_lora: bool = False,
is_graph_capturing: bool = False,
num_active_loras: int = 0,
) -> tuple[torch.Tensor, torch.Tensor]:
```
### _model_forward Parameters
```python
# GPU - no num_tokens_padded
def _model_forward(
self,
input_ids: torch.Tensor | None = None,
positions: torch.Tensor | None = None,
intermediate_tensors: IntermediateTensors | None = None,
inputs_embeds: torch.Tensor | None = None,
**model_kwargs: dict[str, Any],
):
# NPU - has num_tokens_padded as first parameter
def _model_forward(
self,
num_tokens_padded: int,
input_ids: torch.Tensor | None = None,
positions: torch.Tensor | None = None,
intermediate_tensors: IntermediateTensors | None = None,
inputs_embeds: torch.Tensor | None = None,
**model_kwargs: dict[str, Any],
):
```
## Quick Reference Table
| Feature | GPU | NPU |
|---------|-----|-----|
| Graph wrapper | `CUDAGraphWrapper` | `ACLGraphWrapper` |
| Forward context | `set_forward_context` | `set_ascend_forward_context` |
| Runtime mode param | `cudagraph_runtime_mode` | `aclgraph_runtime_mode` |
| Device sync | `torch.cuda.synchronize()` | `torch.npu.synchronize()` |
| Stream | `torch.cuda.Stream` | `torch.npu.Stream` |
| Current stream | `torch.cuda.current_stream()` | `torch.npu.current_stream()` |
| Input batch | `InputBatch` | `NPUInputBatch` |
| Sampler | `Sampler` | `AscendSampler` |
| Attention state | N/A | `AscendAttentionState` |
| RoPE update | N/A | `update_cos_sin()` |
@@ -0,0 +1,374 @@
# Omni-Specific Code Blocks Reference
This document catalogs omni-specific code blocks in the NPU model runners, making it easier to identify what needs to be preserved during upgrades.
> **IMPORTANT**: This document may not be complete or up-to-date!
>
> - Always grep for `Omni-new` in the GPU implementations (`vllm_omni/worker/`) to find the authoritative list
> - New omni features may be added that are not yet documented here
> - When you discover new omni-specific blocks during an upgrade, please update this document
> - Last verified: Check git history for this file
## OmniNPUModelRunner (npu_model_runner.py)
### load_model - Talker MTP Initialization
```python
def load_model(self, *args, **kwargs) -> None:
NPUModelRunner.load_model(self, *args, **kwargs)
# Initialize enable_sp cache to avoid get_current_vllm_config() error
# in _pad_for_sequence_parallelism during execute_model.
# This is a workaround for vllm-ascend not passing vllm_config to enable_sp().
enable_sp(self.vllm_config)
# TODO move this model specific logic to a separate class
# TTS model IS the talker (no .talker sub-attr); use getattr to support both Omni and TTS.
talker_mtp = getattr(self.model, "talker_mtp", None)
if talker_mtp is not None:
self.talker_mtp = talker_mtp # type: ignore[assignment]
cudagraph_mode = self.compilation_config.cudagraph_mode
assert cudagraph_mode is not None
# Only wrap talker_mtp in CUDAGraphWrapper for Omni models that
# have a separate .talker sub-module. TTS models' code predictor
# has internal AR loops / torch.multinomial — not graph-safe.
has_separate_talker = getattr(self.model, "talker", None) is not None
if cudagraph_mode.has_full_cudagraphs() and has_separate_talker:
# NOTE: Use ACLGraphWrapper on NPU, not CUDAGraphWrapper
self.talker_mtp = ACLGraphWrapper(talker_mtp, self.vllm_config, runtime_mode=CUDAGraphMode.FULL)
# TTS exposes mtp_hidden_size; Omni uses hf_text_config.hidden_size.
hidden_size = int(
getattr(self.model, "mtp_hidden_size", 0) or getattr(self.model_config.hf_text_config, "hidden_size")
)
max_batch_size = max(self.max_num_reqs, self.compilation_config.max_cudagraph_capture_size)
self.talker_mtp_input_ids = self._make_buffer(max_batch_size, dtype=torch.int32)
self.talker_mtp_inputs_embeds = self._make_buffer(
max_batch_size, hidden_size, dtype=self.dtype, numpy=False
)
self.last_talker_hidden = self._make_buffer(max_batch_size, hidden_size, dtype=self.dtype, numpy=False)
self.text_step = self._make_buffer(max_batch_size, hidden_size, dtype=self.dtype, numpy=False)
```
### _dummy_run - Talker MTP Dummy Forward
Location: Inside `set_ascend_forward_context` block, before main model forward
```python
# ---------------------------------------Omni-new----------------------------------------------
if getattr(self.model, "talker", None) is not None and hasattr(self.model, "talker_mtp"):
num_tokens_padded_talker_mtp = num_tokens_padded
if num_tokens_padded_talker_mtp == self.max_num_tokens:
num_tokens_padded_talker_mtp = self.talker_mtp_input_ids.gpu.shape[0]
outputs = self.talker_mtp(
self.talker_mtp_input_ids.gpu[:num_tokens_padded_talker_mtp],
self.talker_mtp_inputs_embeds.gpu[:num_tokens_padded_talker_mtp],
self.last_talker_hidden.gpu[:num_tokens_padded_talker_mtp],
self.text_step.gpu[:num_tokens_padded_talker_mtp],
)
self.compilation_config.cache_dir = None
# ---------------------------------------Omni-new----------------------------------------------
```
### _dummy_run - Extract Multimodal Outputs
Location: After model forward, before dummy_compute_logits
```python
# ---------------------------------------Omni-new----------------------------------------------
hidden_states, multimodal_outputs = self.extract_multimodal_outputs(hidden_states)
# ---------------------------------------Omni-new----------------------------------------------
```
### _model_forward - Omni Output Wrapping
```python
def _model_forward(
self,
num_tokens_padded: int,
input_ids: torch.Tensor | None = None,
positions: torch.Tensor | None = None,
intermediate_tensors: IntermediateTensors | None = None,
inputs_embeds: torch.Tensor | None = None,
**model_kwargs: dict[str, Any],
):
"""Override to combine NPUModelRunner's signature with OmniGPUModelRunner's logic."""
# Omni-specific: build and inject extra model kwargs
model_kwargs_extra = self._build_model_kwargs_extra()
# Call the model forward (same as NPUModelRunner)
assert self.model is not None
model_output = self.model(
input_ids=input_ids,
positions=positions,
intermediate_tensors=intermediate_tensors,
inputs_embeds=inputs_embeds,
**model_kwargs,
**model_kwargs_extra,
)
# Omni-specific: wrap output if needed
if not isinstance(model_output, OmniOutput) and hasattr(self.model, "make_omni_output"):
model_output = self.model.make_omni_output(model_output, **model_kwargs_extra)
# Omni-specific: cache model output for later sample_tokens
self._omni_last_model_output = model_output
# NPU-specific: update full graph params (keep from vllm-ascend)
forward_context = get_forward_context()
# ... NPU graph update logic ...
# NPU-specific: all-gather for sequence parallelism (keep from vllm-ascend)
if get_forward_context().sp_enabled and not isinstance(model_output, IntermediateTensors):
model_output = self._all_gather_hidden_states_and_aux(model_output)
return model_output
```
---
## NPUARModelRunner (npu_ar_model_runner.py)
### __init__ - KV Transfer Manager
```python
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.input_ids = self._make_buffer(self.max_num_tokens, dtype=torch.int32)
# each model stage has their own hidden size
self.hidden_size = self.model_config.hf_text_config.hidden_size
self.inputs_embeds = self._make_buffer(self.max_num_tokens, self.hidden_size, dtype=self.dtype, numpy=False)
# Initialize KV cache manager (preserve vllm_config fallback behavior)
self.kv_transfer_manager = OmniKVTransferManager.from_vllm_config(self.vllm_config, self.model_config)
```
### execute_model - KV Transfer Before Update States
Location: At the very beginning of execute_model
```python
# -------------------------------------- Omni-new -------------------------------------------------
# [Omni] Handle KV transfer BEFORE updating states (which removes finished requests)
self.kv_extracted_req_ids = self.kv_transfer_manager.handle_finished_requests_kv_transfer(
finished_reqs=getattr(scheduler_output, "finished_requests_needing_kv_transfer", {}),
kv_caches=self.kv_caches,
block_size=self.cache_config.block_size,
cache_dtype=str(self.cache_config.cache_dtype),
request_id_resolver=self._resolve_global_request_id,
)
# -------------------------------------- Omni-new -------------------------------------------------
```
### execute_model - Custom _update_states Call
Location: Inside synchronize_input_prep context
```python
# -------------------------------------- Omni-new -------------------------------------------------
self._update_states(scheduler_output)
# ------------------------------------------------------------------------------------------------
```
### execute_model - Extract Multimodal Outputs
Location: In post process section, after hidden_states assignment
```python
# -------------------------------------- Omni-new -------------------------------------------------
hidden_states, multimodal_outputs = self.extract_multimodal_outputs(hidden_states)
if multimodal_outputs is not None:
keys_or_type = (
list(multimodal_outputs.keys())
if isinstance(multimodal_outputs, dict)
else type(multimodal_outputs)
)
logger.debug(f"[AR] execute_model: multimodal_outputs keys = {keys_or_type}")
else:
logger.debug("[AR] execute_model: multimodal_outputs is None")
# -------------------------------------- Omni-new -------------------------------------------------
```
### execute_model - Compute Logits with sampling_metadata
Location: In both broadcast_pp_output True and False branches
```python
# -------------------------------------- Omni-new -------------------------------------------------
# Try with sampling_metadata first; fall back to without for models that don't support it
try:
logits = self.model.compute_logits(
sample_hidden_states, sampling_metadata=self.input_batch.sampling_metadata
)
except TypeError:
logits = self.model.compute_logits(sample_hidden_states)
# -------------------------------------- Omni-new -------------------------------------------------
```
### sample_tokens - KV Extracted Req IDs
Location: At the beginning of sample_tokens
```python
# -------------------------------------- Omni-new -------------------------------------------------
kv_extracted_req_ids = getattr(self, "kv_extracted_req_ids", None)
self.kv_extracted_req_ids = None
# -------------------------------------- Omni-new -------------------------------------------------
```
### sample_tokens - Process Additional Information and Build Output
Location: After bookkeeping sync, replacing the original output construction
```python
# -------------------------------------- Omni-new -------------------------------------------------
hidden_states_cpu = hidden_states.detach().to("cpu").contiguous()
num_scheduled_tokens_np = getattr(self, "_omni_num_scheduled_tokens_np", None)
if num_scheduled_tokens_np is None:
req_ids = self.input_batch.req_ids
num_scheduled_tokens_np = np.array(
[scheduler_output.num_scheduled_tokens[rid] for rid in req_ids],
dtype=np.int32,
)
self._process_additional_information_updates(
hidden_states, multimodal_outputs, num_scheduled_tokens_np, scheduler_output
)
pooler_output: list[dict[str, object]] = []
for rid in req_ids_output_copy:
idx = req_id_to_index_output_copy[rid]
start = int(self.query_start_loc.cpu[idx])
sched = int(num_scheduled_tokens_np[idx])
end = start + sched
hidden_slice = hidden_states_cpu[start:end]
payload: dict[str, object] = {"hidden": hidden_slice}
if isinstance(multimodal_outputs, dict) and multimodal_outputs:
# ... multimodal output slicing logic ...
pooler_output.append(payload)
model_runner_output = OmniModelRunnerOutput(
req_ids=req_ids_output_copy,
req_id_to_index=req_id_to_index_output_copy,
sampled_token_ids=valid_sampled_token_ids,
logprobs=logprobs_lists,
prompt_logprobs_dict=prompt_logprobs_dict,
pooler_output=(pooler_output if self.vllm_config.model_config.engine_output_type != "text" else None),
kv_connector_output=kv_connector_output,
)
model_runner_output.kv_extracted_req_ids = kv_extracted_req_ids
# -------------------------------------- Omni-new -------------------------------------------------
```
---
## NPUGenerationModelRunner (npu_generation_model_runner.py)
### execute_model - Async Chunk Update
Location: Inside prepare input section, before synchronize_input_prep
```python
# -------------------------------------- Omni-new -------------------------------------------------
if self.model_config.async_chunk and num_scheduled_tokens:
self._update_request_states(scheduler_output)
# -------------------------------------- Omni-new -------------------------------------------------
```
### execute_model - Seq Token Counts
Location: After _preprocess call
```python
# [Omni] Pass token counts per request for code2wav output slicing
model_kwargs["seq_token_counts"] = tokens
```
### execute_model - Run Generation Model
Location: Inside forward context
```python
# -------------------------------------- Omni-new -------------------------------------------------
outputs = self._run_generation_model(
num_tokens_padded=num_tokens_padded,
input_ids=input_ids,
positions=positions,
intermediate_tensors=intermediate_tensors,
inputs_embeds=inputs_embeds,
model_kwargs=model_kwargs,
logits_indices=logits_indices,
)
_, multimodal_outputs = self.extract_multimodal_outputs(outputs)
# -------------------------------------- Omni-new -------------------------------------------------
```
### sample_tokens - Multimodal Output Processing
The entire sample_tokens method body is omni-specific for generation models:
```python
# -------------------------------------- Omni-new -------------------------------------------------
pooler_output: list[object] = []
if isinstance(multimodal_outputs, torch.Tensor):
# ... tensor handling ...
elif isinstance(multimodal_outputs, list):
# ... list handling ...
elif isinstance(multimodal_outputs, dict):
# ... dict handling per request ...
else:
raise RuntimeError("Unsupported diffusion output type")
# [Omni] Copy req_id mappings to avoid async scheduling mutation.
req_ids_output_copy = self.input_batch.req_ids.copy()
req_id_to_index_output_copy = self.input_batch.req_id_to_index.copy()
output = OmniModelRunnerOutput(
req_ids=req_ids_output_copy,
req_id_to_index=req_id_to_index_output_copy,
sampled_token_ids=[],
logprobs=None,
prompt_logprobs_dict={},
pooler_output=pooler_output,
kv_connector_output=kv_connector_output,
num_nans_in_logits={},
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
)
# -------------------------------------- Omni-new -------------------------------------------------
```
### _dummy_run - Model Kwargs Init and Multimodal Extract
Location: Before model forward and after
```python
model_kwargs = self._init_model_kwargs() # Before forward
# ... forward ...
# -------------------------------------- Omni-new -------------------------------------------------
hidden_states, _ = self.extract_multimodal_outputs(hidden_states)
# -------------------------------------------------------------------------------------------------
```
---
## ExecuteModelState Extension
The `ExecuteModelState` NamedTuple is extended for omni:
```python
class ExecuteModelState(NamedTuple):
"""Ephemeral cached state transferred between execute_model() and
sample_tokens(), after execute_model() returns None."""
scheduler_output: SchedulerOutput
logits: torch.Tensor
spec_decode_metadata: SpecDecodeMetadata | None
spec_decode_common_attn_metadata: AscendCommonAttentionMetadata | None
hidden_states: torch.Tensor
sample_hidden_states: torch.Tensor
aux_hidden_states: list[torch.Tensor] | None
attn_metadata: PerLayerAttnMetadata
positions: torch.Tensor
ec_connector_output: ECConnectorOutput | None
cudagraph_stats: CUDAGraphStat | None
multimodal_outputs: Any # <-- Omni extension
```
This extended state must be imported from `npu_ar_model_runner` in `npu_generation_model_runner`.
@@ -0,0 +1,222 @@
# NPU Model Runner Upgrade Workflow Checklist
> **Note**: Reference documents (`omni-specific-blocks.md`) may not be complete. Always grep for `Omni-new` in GPU implementations to find all omni-specific code blocks. Update the reference docs when discovering new blocks.
## Pre-Upgrade Preparation
### 1. Version Information
- [ ] Identify current vllm-omni version: `_________`
- [ ] Identify target vllm-ascend version: `_________`
- [ ] Identify target vllm version: `_________`
- [ ] Last release date for GPU worker changes: `_________`
### 2. Gather Git History
```bash
# GPU-side omni changes since last release
cd /root/vllm-workspace/vllm-omni
git log --oneline --since="YYYY-MM-DD" -- vllm_omni/worker/
# vllm-ascend NPUModelRunner changes
cd /root/vllm-workspace/vllm-ascend
git log --oneline <from-tag>..<to-tag> -- vllm_ascend/worker/model_runner_v1.py
```
### 3. Backup Current Files
- [ ] Create backup of current NPU runners:
```bash
cp -r vllm_omni/platforms/npu/worker vllm_omni/platforms/npu/worker.backup
```
---
## OmniNPUModelRunner (npu_model_runner.py)
### Read and Understand
- [ ] Read current `npu_model_runner.py`
- [ ] Read latest `vllm_ascend/worker/model_runner_v1.py`
- [ ] Read latest `vllm_omni/worker/gpu_model_runner.py`
### Method: load_model
- [ ] Document existing omni-specific logic
- [ ] Copy latest NPUModelRunner.load_model structure
- [ ] Re-insert: `enable_sp(self.vllm_config)` call
- [ ] Re-insert: talker_mtp detection and setup
- [ ] Replace: `CUDAGraphWrapper` → `ACLGraphWrapper`
- [ ] Re-insert: Buffer allocations (talker_mtp_input_ids, etc.)
### Method: _dummy_run
- [ ] Document existing omni-specific logic locations
- [ ] Copy latest NPUModelRunner._dummy_run
- [ ] Re-insert: talker_mtp dummy forward block (inside context)
- [ ] Re-insert: `extract_multimodal_outputs` call
- [ ] Verify: Comment markers are present
### Method: _model_forward
- [ ] Copy latest NPUModelRunner._model_forward structure
- [ ] Re-insert: `_build_model_kwargs_extra()` call
- [ ] Re-insert: OmniOutput wrapping logic
- [ ] Re-insert: `_omni_last_model_output` caching
- [ ] Keep: NPU graph params update
- [ ] Keep: SP all-gather logic
### Method: _talker_mtp_forward
- [ ] Verify: Uses `set_ascend_forward_context`
- [ ] Verify: Uses `ACLGraphWrapper` check
- [ ] Sync any changes from GPU `_talker_mtp_forward`
### Imports
- [ ] Update vllm-ascend imports to latest paths
- [ ] Verify all omni imports are present
- [ ] Remove any deprecated imports
---
## NPUARModelRunner (npu_ar_model_runner.py)
### Read and Understand
- [ ] Read current `npu_ar_model_runner.py`
- [ ] Read latest `vllm_ascend/worker/model_runner_v1.py` execute_model
- [ ] Read latest `vllm_omni/worker/gpu_ar_model_runner.py`
### Method: __init__
- [ ] Sync any new initialization from GPU side
- [ ] Keep: `OmniKVTransferManager` setup
- [ ] Keep: Custom buffer allocations
### Method: execute_model
- [ ] Document all omni blocks with line numbers
- [ ] Copy latest NPUModelRunner.execute_model structure
- [ ] Re-insert: KV transfer handling (beginning)
- [ ] Re-insert: Custom `_update_states` call
- [ ] Re-insert: `extract_multimodal_outputs`
- [ ] Re-insert: `compute_logits` with sampling_metadata try/except
- [ ] Update: ExecuteModelState to include multimodal_outputs
### Method: sample_tokens
- [ ] Document all omni blocks
- [ ] Copy latest NPUModelRunner.sample_tokens structure
- [ ] Re-insert: `kv_extracted_req_ids` handling
- [ ] Re-insert: Hidden states CPU copy
- [ ] Re-insert: `_process_additional_information_updates`
- [ ] Re-insert: `OmniModelRunnerOutput` construction
### ExecuteModelState
- [ ] Verify: `multimodal_outputs` field is present
- [ ] Verify: Imported/used correctly in execute_model
### Imports
- [ ] Update all vllm-ascend imports
- [ ] Keep omni-specific imports
---
## NPUGenerationModelRunner (npu_generation_model_runner.py)
### Read and Understand
- [ ] Read current `npu_generation_model_runner.py`
- [ ] Read latest GPU `gpu_generation_model_runner.py`
### Method: _update_request_states
- [ ] Verify: async_chunk handling is correct
- [ ] Sync any changes from GPU side
### Method: execute_model
- [ ] Document all omni blocks
- [ ] Copy latest NPUModelRunner.execute_model base structure
- [ ] Re-insert: async_chunk update logic
- [ ] Re-insert: `seq_token_counts` injection
- [ ] Re-insert: `_run_generation_model` call
- [ ] Re-insert: `extract_multimodal_outputs`
- [ ] Use: ExecuteModelState from npu_ar_model_runner
### Method: sample_tokens
- [ ] Keep: Entire omni multimodal output processing
- [ ] Update: Any new output fields needed
- [ ] Keep: `OmniModelRunnerOutput` construction
### Method: _run_generation_model
- [ ] Sync any changes from GPU side
- [ ] Keep: `_model_forward` call with sampler
### Method: _dummy_run
- [ ] Copy latest NPUModelRunner._dummy_run
- [ ] Re-insert: `model_kwargs = self._init_model_kwargs()`
- [ ] Re-insert: `extract_multimodal_outputs` at end
### Imports
- [ ] Import ExecuteModelState from npu_ar_model_runner
- [ ] Update vllm-ascend imports
---
## Post-Upgrade Validation
### Syntax Validation
- [ ] `python -m py_compile vllm_omni/platforms/npu/worker/npu_model_runner.py`
- [ ] `python -m py_compile vllm_omni/platforms/npu/worker/npu_ar_model_runner.py`
- [ ] `python -m py_compile vllm_omni/platforms/npu/worker/npu_generation_model_runner.py`
### Import Validation
- [ ] `python -c "from vllm_omni.platforms.npu.worker.npu_model_runner import OmniNPUModelRunner"`
- [ ] `python -c "from vllm_omni.platforms.npu.worker.npu_ar_model_runner import NPUARModelRunner"`
- [ ] `python -c "from vllm_omni.platforms.npu.worker.npu_generation_model_runner import NPUGenerationModelRunner"`
### Comment Markers
- [ ] Grep for "Omni-new" in all three files
- [ ] Verify all omni blocks have closing markers
### Code Review
- [ ] No `CUDAGraphWrapper` references
- [ ] All `set_forward_context` replaced with `set_ascend_forward_context`
- [ ] Parameter names correct (`aclgraph_runtime_mode` not `cudagraph_runtime_mode`)
- [ ] No duplicate code blocks
- [ ] No missing imports
---
## Git Commit
### Commit Message Template
```
[NPU] Upgrade model runners to align with vllm-ascend vX.Y.Z
- Update OmniNPUModelRunner with latest NPUModelRunner base
- Update NPUARModelRunner execute_model and sample_tokens
- Update NPUGenerationModelRunner for async_chunk changes
- Sync GPU-side omni changes from vX.Y.Z release
- Preserve all omni-specific logic (marked with Omni-new comments)
Changes from vllm-ascend:
- <list key changes>
Changes synced from GPU:
- <list key GPU-side omni changes>
```
### Files to Stage
- [ ] `vllm_omni/platforms/npu/worker/npu_model_runner.py`
- [ ] `vllm_omni/platforms/npu/worker/npu_ar_model_runner.py`
- [ ] `vllm_omni/platforms/npu/worker/npu_generation_model_runner.py`
- [ ] Any other modified files
---
## Troubleshooting
### Import Errors
- Check if vllm-ascend module paths have changed
- Verify PYTHONPATH includes both vllm-ascend and vllm-omni
### Type Errors
- Check method signatures match between GPU and NPU
- Verify NamedTuple fields match expected structure
### Runtime Errors
- Enable debug logging: `export VLLM_LOGGING_LEVEL=DEBUG`
- Check graph capture issues: try `--enforce-eager`
- Check attention issues: verify AscendAttentionState usage
### Performance Regression
- Compare with previous version on same model
- Check if graph capture is working: look for ACLGraph logs
- Verify SP/EP configurations are correct