TRF020

MLA models must isolate the KV LoRA expansion (kv_b_proj) in a dedicated method that forward() calls.

   
Default Enabled
Scope All models
Source mlinter/trf020.py
Show in terminal mlinter --rule TRF020

What it does

In model directories whose configuration declares kv_lora_rank (Multi-head Latent Attention), checks the attention class owning the KV LoRA expansion projection (kv_b_proj, or any nn.Linear(config.kv_lora_rank, ...)): the expansion must live in a dedicated method (e.g. expand_kv) that forward() calls, not inline. In modular files a method inherited from an imported base counts.

Why is this bad?

External backends (vLLM/SGLang) override the expansion to consume the compressed KV cache directly. Inlined in forward() there is nothing to override, so the backend must materialize the full cache – losing the memory savings MLA exists for.

Example

+    def expand_kv(self, k_nope, k_pe):
+        key_shape = (*k_nope.shape[:-1], -1, self.qk_nope_head_dim + self.v_head_dim)
+        k_nope = self.kv_b_proj(k_nope).view(key_shape).transpose(1, 2)
+        k_nope, value_states = torch.split(k_nope, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
+        k_pe = k_pe.expand(*k_nope.shape[:-1], -1)
+        key_states = torch.cat((k_nope, k_pe), dim=-1)
+        return key_states, value_states
+
     def forward(self, hidden_states, ...):
         ...
-        k_nope = self.kv_b_proj(k_pass).view(key_shape).transpose(1, 2)
-        k_nope, value_states = torch.split(k_nope, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
-        k_pe = k_rot.expand(*k_nope.shape[:-1], -1)
-        key_states = torch.cat((k_nope, k_pe), dim=-1)
+        key_states, value_states = self.expand_kv(k_pass, k_rot)

Suppressing this rule

Add a # trf-ignore: TRF020 comment on the flagged line or the line directly above it. See Suppressing rules for whole-file directives and when a suppression is the wrong answer.