# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project from typing import Any import torch import torch.nn as nn from vllm.config import VllmConfig from vllm.models.deepseek_v4_1.common.engram import ( Engram, EngramDiskStager, NgramHashState, ) from vllm.triton_utils import tl, triton from vllm.v1.worker.gpu.input_batch import InputBatch from vllm.v1.worker.gpu.mm.encoder_cache import EncoderCache from vllm.v1.worker.gpu.model_states.default import DefaultModelState from vllm.v1.worker.gpu.states import RequestState @triton.jit def _gather_lookback_kernel( lookback_ptr, idx_mapping_ptr, num_computed_tokens_ptr, all_token_ids_ptr, all_token_ids_stride, num_reqs, DEPTH: tl.constexpr, BLOCK_DEPTH: tl.constexpr, ): # One program per lookback row; rows past the batch are filled with -1. batch_idx = tl.program_id(0) in_batch = batch_idx < num_reqs req_state_idx = tl.load(idx_mapping_ptr + batch_idx, mask=in_batch, other=0) num_computed = tl.load(num_computed_tokens_ptr + req_state_idx) offs = tl.arange(0, BLOCK_DEPTH) pos = num_computed - 1 - offs valid = in_batch & (offs < DEPTH) & (pos >= 0) ids = tl.load( all_token_ids_ptr + req_state_idx * all_token_ids_stride + pos, mask=valid, other=-1, ) tl.store(lookback_ptr + batch_idx * DEPTH + offs, ids, mask=offs < DEPTH) class DeepseekV41ModelState(DefaultModelState): """DefaultModelState plus the engram lookback window. The engram n-gram hash needs the ids of the ``depth`` tokens preceding each request's chunk start (see ``common/engram.py``). The runner keeps the full token history on device, so the window is gathered there every step: exact for prompt and generated tokens alike, whatever instance produced their KV. """ def __init__( self, vllm_config: VllmConfig, model: nn.Module, encoder_cache: EncoderCache | None, device: torch.device, ): super().__init__(vllm_config, model, encoder_cache, device) depth = model.token_lookback_depth self.lookback_token_ids: torch.Tensor | None = None if depth > 0: # Persistent so a captured graph can read it on replay. self.lookback_token_ids = torch.full( (self.max_num_reqs, depth), -1, dtype=torch.int32, device=device ) # Tech2Wild/Kai 2026-09-10 (CUDA graphs): DISK-backed Engram rows are # staged in prepare_inputs, outside the (possibly captured) forward. self.engram_stager: EngramDiskStager | None = None engrams = [m for m in model.modules() if isinstance(m, Engram) and m.prestage] if engrams: hash_states = [m for m in model.modules() if isinstance(m, NgramHashState)] assert len(hash_states) == 1, ( f"expected one NgramHashState, found {len(hash_states)}" ) self.engram_stager = EngramDiskStager(hash_states[0], engrams) def prepare_inputs( self, input_batch: InputBatch, req_states: RequestState ) -> dict[str, torch.Tensor | None]: model_inputs = super().prepare_inputs(input_batch, req_states) window = self.lookback_token_ids if window is None: return model_inputs all_token_ids = req_states.all_token_ids.gpu depth = window.shape[1] _gather_lookback_kernel[(window.shape[0],)]( window, input_batch.idx_mapping, req_states.num_computed_tokens.gpu, all_token_ids, all_token_ids.stride(0), input_batch.idx_mapping.shape[0], DEPTH=depth, BLOCK_DEPTH=triton.next_power_of_2(depth), ) model_inputs["lookback_token_ids"] = window if self.engram_stager is not None and input_batch.input_ids is not None: # After the runner wrote this step's ids/positions/query_start_loc # and the lookback window above; before the forward / graph replay. positions = model_inputs.get("positions") self.engram_stager.stage( input_batch.input_ids, positions if positions is not None else input_batch.positions, input_batch.query_start_loc[: input_batch.num_reqs + 1], window, input_batch.num_tokens, input_batch=input_batch, req_states=req_states, ) return model_inputs def prepare_dummy_inputs(self, num_reqs: int, num_tokens: int) -> dict[str, Any]: model_inputs = super().prepare_dummy_inputs(num_reqs, num_tokens) if self.lookback_token_ids is not None: # The captured graph reads this buffer; replays refill it in place. self.lookback_token_ids.fill_(-1) model_inputs["lookback_token_ids"] = self.lookback_token_ids return model_inputs