# Regras de estrutura do modelo

O Transformers impõe um conjunto de regras estáticas a cada arquivo `modeling_*.py`, `modular_*.py` e `configuration_*.py`. O pacote [mlinter](https://github.com/huggingface/transformers-mlinter) fornece o mecanismo de verificação, e o repositório mantém seu conjunto de regras ativo em `utils/rules.toml`. Esse arquivo TOML local nos permite habilitar, desabilitar ou ajustar regras rapidamente, sem esperar por um novo lançamento do `transformers-mlinter`.

> [!TIP]
> Consulte a [documentação do mlinter](https://huggingface.github.io/transformers-mlinter/) para a referência completa do verificador e detalhes de uso.

Estas são as convenções de modelo esperadas para adicionar ou alterar o código de modelagem. Elas mantêm a base de código consistente e garantem a compatibilidade com recursos como paralelismo de pipeline, mapas de dispositivo e amarração de pesos.

## Executando o verificador

`make typing` executa o `mlinter` em conjunto com o verificador de tipos `ty` por meio do wrapper do repositório, de modo que ele detecta o `utils/rules.toml`. Execute o mesmo wrapper diretamente com os comandos a seguir.

```bash
python utils/check_modeling_structure.py                 # check all modeling files
python utils/check_modeling_structure.py --changed-only  # check only files changed vs origin/main
python utils/check_modeling_structure.py --list-rules    # list all rules and their enabled status
python utils/check_modeling_structure.py --rule TRF001   # show built-in docs for a specific rule
```

A flag `--changed-only` é a opção mais rápida durante o desenvolvimento. Ela verifica apenas os arquivos que você modificou em relação à branch principal. Se você invocar o `mlinter` diretamente, em vez do wrapper, passe `--rules-toml utils/rules.toml` para que as sobrescritas locais sejam aplicadas.

## Corrigindo uma violação

Quando uma violação de regra é detectada, o erro tem o seguinte formato:

```
src/transformers/models/acme/modeling_acme.py:18: TRF013: AcmeModel.__init__ does not call self.post_init().
```

Use o ID da regra para localizar a correção na [referência de regras](#rules-reference). TRF013 é acionado quando uma subclasse de [PreTrainedModel](/docs/transformers/main/pt/main_classes/model#transformers.PreTrainedModel) não chama `self.post_init()`. Esse método executa etapas essenciais de finalização, e omiti-lo causa bugs em tempo de execução.

```diff
 class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
         self.layers = nn.ModuleList(
             [AcmeDecoderLayer(config) for _ in range(config.num_hidden_layers)]
         )
+        self.post_init()
```

## Referência de regras

Cada regra abaixo lista o que ela aplica e um diff mostrando a correção. Execute `python utils/check_modeling_structure.py --rule TRF001` para ver a documentação embutida de qualquer regra com o conjunto de regras atual do repositório.

### TRF001

Verifica se o config_class de PreTrainedModel é nomeado Config. Uma incompatibilidade pode quebrar o carregamento, as classes automáticas e as expectativas dos desenvolvedores.

```diff
class AcmePreTrainedModel(PreTrainedModel):
-    config_class = WileConfig
+    config_class = AcmeConfig
```

### TRF002

Verifica se base_model_prefix é uma string literal não vazia e sem espaços em branco. Prefixos inválidos podem quebrar o mapeamento de chaves para carregamento de pesos e o acesso ao modelo base.

```diff
class AcmePreTrainedModel(PreTrainedModel):
-    base_model_prefix = ""
+    base_model_prefix = "model"
```

### TRF003

Sinaliza o padrão antigo `if not return_dict: return (x,)` em forward. A ramificação manual de return_dict é verbosa e fácil de errar. Deixe que @capture_output ou @can_return_tuple façam isso.

```diff
-def forward(self, x, return_dict=None):
-    if not return_dict:
-        return (x,)
-    return AcmeModelOutput(last_hidden_state=x)
+@can_return_tuple
+def forward(self, x):
+    return AcmeModelOutput(last_hidden_state=x)
```

### TRF004

Verifica que nenhuma classe de modelo define um método tie_weights. Sobrescrever tie_weights quebra o carregamento, o cálculo de device_map e o salvamento. Declare pesos vinculados no atributo de classe _tied_weights_keys.

```diff
-def tie_weights(self):
-    self.lm_head.weight = self.emb.weight
+class AcmeForCausalLM(AcmePreTrainedModel):
+    _tied_weights_keys = ["lm_head.weight"]
```

### TRF005

Verifica a forma de _no_split_modules quando presente. Valores malformados podem quebrar a particionamento e o sharding do device-map.

```diff
-_no_split_modules = [SomeLayerClass, ""]
+_no_split_modules = ["AcmeDecoderLayer", "AcmeAttention"]
```

### TRF006

Verifica se os argumentos de cache na assinatura de um método forward são utilizados no corpo do método. Um argumento de cache não utilizado sugere suporte incompleto a cache e comportamento de API inconsistente.

```diff
def forward(self, x, past_key_values=None, use_cache=False):
+    if use_cache:
+        ...
     return x
```

### TRF007

Verifica atribuições ao atributo self após self.post_init() em __init__. Modificar a estrutura do modelo após post_init ignora o trabalho de inicialização e finalização.

```diff
def __init__(self, config):
     ...
-    self.post_init()
-    self.proj = nn.Linear(...)
+    self.proj = nn.Linear(...)
+    self.post_init()
```

### TRF008

Verifica que add_start_docstrings em uma classe de modelo recebe um argumento não vazio. Um argumento vazio gera documentação de API de baixa qualidade e pouco clara.

```diff
-@add_start_docstrings("")
+@add_start_docstrings("The Acme model.")
 class AcmeModel(AcmePreTrainedModel):
     ...
```

### TRF009

Sinaliza importações do pacote de outro modelo a partir dos arquivos de implementação enviados de um modelo: modeling_*, configuration_*, processing_*, image_processing_*, video_processing_*, feature_extraction_*, tokenization_* e generation_*.py. Três formas são consideradas: caminho de pacote (`from transformers.models.other.modeling_other import X`), caminho relativo (`from ..other.configuration_other import X`) e API pública (`from transformers import OtherModel`) -- para a última, o diretório proprietário é inferido a partir do nome da classe e confirmado em relação às classes que ela define, de modo que um nome não resolvido é deixado como está. Fora do escopo: `modular_*.py` (construir sobre outro modelo é seu propósito, e o conversor aplaina essas importações), `convert_*.py`, `__init__.py` e arquivos sob `auto`. Alvos permitidos: o próprio diretório do modelo, `auto` e `timm_wrapper`. Um modelo, uma definição: o comportamento do modelo A está em seus próprios arquivos, e uma alteração no modelo B não deve alterar silenciosamente o modelo A -- para todos os tipos de arquivo, não apenas modeling. Acesse sub-configurações por meio de AutoConfig e CONFIG_MAPPING, e use um arquivo modular para construir sobre o código de outro modelo.

```diff
-from transformers.models.llama.modeling_llama import LlamaAttention
-from transformers import CLIPTextModelWithProjection
+# Keep implementation local to this model's own files.
+# To build on another model, write modular_acme.py; to reuse a snippet,
+# copy it with a # Copied from comment.
```

### TRF010

Verifica que os subclasse diretos de PreTrainedConfig/PretrainedConfig em configuration_*.py e modular_*.py carregam @strict(accept_kwargs=True). Sem isso, um novo config perde o contrato de validação de tipo em tempo de execução do repositório e se desvia do padrão de config baseado em dataclass.

```diff
+@strict(accept_kwargs=True)
 class AcmeConfig(PreTrainedConfig):
     ...
```

### TRF011

Em forward() de subclasses de PreTrainedModel, acessos a atributos de submódulos que torch.nn.Identity não teria: em variáveis de laço sobre self.layers, e self. onde  não é um atributo padrão de nn.Module. A paralelização de pipeline pode substituir qualquer submódulo por torch.nn.Identity, portanto, ler um atributo personalizado (por exemplo, decoder_layer.attention_type) dele levanta AttributeError em tempo de execução. Leia metadados por camada de self.config.

```diff
def forward(self, ...):
-    for decoder_layer in self.layers:
+    for i, decoder_layer in enumerate(self.layers):
         hidden_states = decoder_layer(
             hidden_states,
-            attention_mask=causal_mask_mapping[decoder_layer.attention_type],
+            attention_mask=causal_mask_mapping[self.config.layer_types[i]],
         )
```

### TRF012

Flags in-place ops (.normal_(), .zero_(), ...) on module weights inside _init_weights. Parameters carry internal flags tracking whether they still need re-initialization; in-place ops bypass them. Use the `init` primitives.

```diff
+from transformers import initialization as init
+
 def _init_weights(self, module):
-    module.weight.normal_(mean=0.0, std=0.02)
+    init.normal_(module.weight, mean=0.0, std=0.02)
```

### TRF013

Verifica que toda subclasse de PreTrainedModel que define __init__ chama self.post_init(). Em arquivos modulares, super().__init__() conta, pois propaga o post_init do pai. post_init realiza finalizações essenciais (inicialização de pesos, configuração de gradient checkpointing, ...); ignorá-lo causa bugs sutis em tempo de execução.

```diff
class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
         self.layers = nn.ModuleList(...)
+        self.post_init()
```

### TRF014

Flags `trust_remote_code` usadas ou passadas (por exemplo, como kwarg) em arquivos de integração de modelos nativos. `trust_remote_code` carrega código arbitrário, incluindo binários -- um recurso poderoso para usuários, mas não algo em que uma integração nativa pode depender, já que o código remoto não pode ser revisado nem mantido no transformers.

```diff
class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
-        self.model = AutoModel.from_pretrained(..., trust_remote_code=True)
+        self.model = AutoModel.from_pretrained(...)
```

### TRF015

Quando uma subclasse de PreTrainedModel define _tied_weights_keys não vazio, verifica o arquivo de configuração correspondente em busca de um campo tie_word_embeddings. Sem ele, os usuários não conseguem controlar o vínculo de pesos: o modelo vincula incondicionalmente, quebrando os ciclos de serialização e o ajuste fino com cabeças não vinculadas.

```diff
# configuration_foo.py
 @strict(accept_kwargs=True)
 class FooConfig(PreTrainedConfig):
     hidden_size: int = 768
+    tie_word_embeddings: bool = True
```

### TRF016

Quando uma classe image_processing_*.py ou video_processing_*.py declara atributos booleanos do_* (do_resize, do_rescale, do_normalize, do_convert_rgb, ...) e sobrescreve preprocess() ou _preprocess(), verifique se cada flag ainda é consumida lá: referenciada diretamente, delegada via super().preprocess/_preprocess(..., **kwargs), ou -- apenas processadores de imagem -- encaminhada através de _preprocess_image_like_inputs/_prepare_image_like_inputs. do_sample_frames está isento: o preprocess() base o consome antes de _preprocess() ser executado. Um do_X que a sobrescrita nunca referencia é inútil: definir do_X=False não tem efeito, a operação é executada mesmo assim e as sobrescritas por chamada quebram silenciosamente.

```diff
class AcmeVideoProcessor(BaseVideoProcessor):
     do_resize = True
     do_normalize = True

     def _preprocess(
         self,
         videos,
+        do_resize: bool,
+        do_normalize: bool,
         size,
         image_mean,
         image_std,
         **kwargs,
     ):
         for video in videos:
-            video = self.resize(video, size=size)
-            video = self.normalize(video, image_mean, image_std)
+            if do_resize:
+                video = self.resize(video, size=size)
+            if do_normalize:
+                video = self.normalize(video, image_mean, image_std)
```

### TRF017

Em classes que possuem tanto @auto_docstring quanto @dataclass, verifica-se se @auto_docstring vem primeiro. Os decoradores são aplicados de baixo para cima, portanto, @dataclass no topo executa @auto_docstring primeiro, em uma classe que ainda não possui __init__ sintetizado: ele então modifica o __init__.__doc__ da classe pai em vez do da subclasse.

```diff
-@dataclass
 @auto_docstring(
     custom_intro="""
     Output type of `AcmeForPreTraining`.
     """
 )
+@dataclass
 class AcmeForPreTrainingOutput(ModelOutput):
     ...
```

### TRF018

Verifica que toda subclasse de PreTrainedModel que sobrescreve `_init_weights(self, module, ...)` encadeia a chamada via `super()._init_weights(...)`. Arquivos modulares também aceitam os sentinels `PreTrainedModel._init_weights(self, module)`, `PreTrainedModel._init_weights(module)` e `raise AttributeError(...)`. Para uma sobrescrita completa intencional, suprima com `# trf-ignore: TRF018` acima do método. O método base `_init_weights` cobre os tipos padrão de módulos (Linear, Embedding, LayerNorm, RotaryEmbedding, ...). Pular essa chamada deixa todos os submódulos que a sobrescrita não cobre sem inicialização -- o que passa nos testes e surge muito depois como um bug sutil de inicialização de pesos (cf. https://github.com/huggingface/transformers/pull/45597).

```diff
from ... import initialization as init

 def _init_weights(self, module):
+    super()._init_weights(module)
     if isinstance(module, AcmeCustomLayer):
-        module.gate.data.zero_()
+        init.zeros_(module.gate)
```

### TRF019

Sinaliza um `_defaults` não vazio em TypedDicts `*ProcessorKwargs` em `processing_*.py`. Modelos lançados antes da data limite são isentos. `_defaults` codificados em linha dispersam a configuração do processador pelo código-fonte Python, são difíceis de sobrescrever a partir da configuração e aumentam o tamanho do código. Em `processor_config.json` no hub, eles viajam com o checkpoint e podem ser atualizados sem alteração de código.

```diff
class Gemma4ProcessorKwargs(ProcessingKwargs, total=False):
-    _defaults = {
-        "text_kwargs": {"padding": False},
-        "images_kwargs": {"return_tensors": "pt"},
-    }
     images_kwargs: Gemma4ImageProcessorKwargs
```

### TRF020

Em diretórios de modelos cuja configuração declara `kv_lora_rank` (Multi-head Latent Attention), verifica a classe de atenção que possui a projeção de expansão do KV LoRA (`kv_b_proj`, ou qualquer `nn.Linear(config.kv_lora_rank, ...)`): a expansão deve estar em um método dedicado (por exemplo, `expand_kv`) que o `forward()` chama, e não de forma inline. Em arquivos modulares, um método herdado de uma base importada é considerado válido. Backends externos (vLLM/SGLang) substituem a expansão para consumir o cache KV comprimido diretamente. Se estiver inline no `forward()`, não há nada para substituir, então o backend precisa materializar o cache completo -- perdendo as economias de memória para as quais o MLA existe.

```diff
+    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)
```

### TRF021

Em modeling_*.py e modular_*.py, as flags `torch.tensor(<value>, ..., device=<non-cpu>)` em que `<value>` se resolve comprovadamente para um escalar Python -- a partir de literais numéricos e aritmética sobre eles, campos de torch.finfo/iinfo, builtins que retornam escalares e chamadas de math.*, locais vinculados exatamente uma vez, self. atribuídos no corpo da classe e campos de config anotados como int/float/bool no arquivo de configuração correspondente (seguindo attribute_map). Qualquer coisa que também possa ser uma sequência (`eos_token_id: int | list[int] | None`) ou que não possa ser resolvida é deixada como está. __init__, _init_weights, __post_init__ e post_init estão isentos: eles nunca são executados dentro de uma região de captura. torch.tensor(, device=) materializa o valor no host e então o copia para o dispositivo; a captura de grafo CUDA proíbe essa cópia, de modo que o modelo não pode ser capturado. torch.full((), , dtype=..., device=...) preenche o mesmo tensor 0-d no dispositivo com um kernel capturável e sem sincronização.

```diff
def get_placeholder_mask(self, input_ids, inputs_embeds):
     special_image_mask = (
         inputs_embeds
         == self.get_input_embeddings()(
-            torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+            torch.full((), self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
         )
     ).all(-1)
```

### TRF022

Verifica se cada string em uma lista `_no_split_modules` em modeling_*.py ou modular_*.py nomeia uma classe definida nesse arquivo, importada nele ou definida por um módulo irmão do mesmo diretório de modelo. Complementa o TRF005, que apenas verifica a forma do valor. `device_map` compara essas strings com `module.__class__.__name__` em tempo de execução, portanto um nome desatualizado ou com erro de digitação não corresponde a nada e é ignorado silenciosamente -- o módulo que deveria ser mantido junto ainda pode ser dividido entre dispositivos. Exclua entradas que nomeiam classes de outro modelo em vez de corrigi-las: `post_init` já coleta `_no_split_modules` dos submodelos filhos.

```diff
class VideoLlavaPreTrainedModel(PreTrainedModel):
-    _no_split_modules = ["VideoLlavaVisionAttention"]
```

### TRF023

Em configuration_*.py e modular_*.py, as flags `*Config` sinalizam campos nomeados com a abreviatura de um paper upstream em vez do nome canônico: d_model/n_embd -> hidden_size, d_ff/d_inner/ffn_dim/ffn_hidden_size/expansion_ratio -> intermediate_size, d_head -> head_dim, n_head/n_heads -> num_attention_heads, n_layer/n_layers/num_blocks -> num_hidden_layers. Os campos são lidos do corpo da classe e das atribuições e padrões em __init__/__post_init__. Nomes ambíguos, porém idiomáticos (num_heads, num_layers, embed_dim, mlp_ratio) não são sinalizados, e os modelos adicionados antes de cutoff_date mantêm os seus. Tudo que lê a forma de um modelo -- planejamento de device_map, planos de paralelismo de tensor/pipeline, quantização, PEFT, seleção de backend de atenção, consumidores de `attribute_map` -- consulta os nomes canônicos, de modo que uma config que escreva a mesma quantidade como `d_model` fica silenciosamente fora de todos eles. Mapeie a grafia própria do checkpoint no script de conversão.

```diff
@strict(accept_kwargs=True)
 class AcmeConfig(PreTrainedConfig):
-    d_model: int = 1024
-    d_ff: int = 4096
-    n_heads: int = 16
-    n_layers: int = 24
+    hidden_size: int = 1024
+    intermediate_size: int = 4096
+    num_attention_heads: int = 16
+    num_hidden_layers: int = 24
```

### TRF024

Em modeling_*.py e modular_*.py, sinaliza um literal inteiro maior que 8 em um argumento de dimensão de um construtor do torch.nn (Linear, Embedding, LayerNorm, RMSNorm, GroupNorm, BatchNorm*, InstanceNorm*, Conv*d, ConvTranspose*d, Bilinear, MultiheadAttention), seja posicional ou por palavra-chave (in_features, out_features, in_channels, out_channels, num_embeddings, embedding_dim, embed_dim, normalized_shape, num_channels, hidden_size). Argumentos de forma do operador (kernel_size, stride, padding, num_groups) são ignorados; literais até 8 mantêm limpos os cabeçalhos escalares, classificadores binários e contagens de canais RGB. Modelos adicionados antes de cutoff_date estão isentos. Uma largura fixada em código fixa o módulo em um único tamanho de checkpoint: a mesma arquitetura em outra escala é carregada com incompatibilidade de forma, e `from_pretrained` não pode indicar qual valor está incorreto porque nenhum campo de configuração aponta para ele. Isso também divide a fonte de verdade, de modo que editar a configuração não altera mais o modelo que é construído.

```diff
class AcmeAtomEmbedding(nn.Module):
     def __init__(self, config):
         super().__init__()
-        self.proj = nn.Linear(768, 3072, bias=False)
-        self.norm = nn.LayerNorm(3072)
+        self.proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
+        self.norm = nn.LayerNorm(config.intermediate_size)
```

### TRF025

Em modeling_*.py e modular_*.py, chamadas a uma fábrica de máscaras (os pontos de entrada de masking_utils create_causal_mask, create_bidirectional_mask, create_sliding_window_causal_mask, create_chunked_causal_mask, create_masks_for_generate e qualquer helper create_*_mask) dentro de uma classe que não herda de PreTrainedModel -- blocos nn.Module simples, como camadas, módulos de atenção e codificadores. A construção da máscara é um trabalho de complexidade O(quadrado do comprimento da sequência) que não varia por camada, portanto, construí-la na camada faz com que esse custo seja pago uma vez por camada, e cada camada passa a possuir sua própria máscara, de modo que os backends de atenção não podem mais receber uma única máscara preparada. Construa-a uma vez no modelo e passe-a para baixo.

```diff
class AcmeLayer(nn.Module):
     def forward(self, hidden_states, attention_mask=None, **kwargs):
-        attention_mask = create_causal_mask(
-            config=self.config, input_embeds=hidden_states, attention_mask=attention_mask, ...
-        )
         return self.self_attn(hidden_states, attention_mask, **kwargs)

 class AcmeModel(AcmePreTrainedModel):
     def forward(self, input_ids=None, attention_mask=None, **kwargs):
+        causal_mask = create_causal_mask(
+            config=self.config, input_embeds=inputs_embeds, attention_mask=attention_mask, ...
+        )
         for layer in self.layers:
-            hidden_states = layer(hidden_states, attention_mask, **kwargs)
+            hidden_states = layer(hidden_states, causal_mask, **kwargs)
```

### TRF026

Em modeling_*.py e modular_*.py, sinaliza uma classe que não é PreTrainedModel e que define apenas __init__ e forward, atribui exatamente um self. em __init__ e cujo corpo de forward é exatamente `return self.<attr>(...)` para ele (uma docstring inicial é ignorada). Qualquer outro método, atributo extra, instrução antes do return ou chamada de `super()` em forward significa que a classe realiza trabalho próprio. O wrapper adiciona um nível a cada nome de peso, a _no_split_modules, aos planos de paralelismo e a todos os mapeamentos de conversão, sem calcular nada — e os leitores precisam abrir mais uma classe para descobrir isso. Subclasses de PreTrainedModel estão isentas: elas existem para from_pretrained e as classes auto, mesmo quando o forward apenas delega.

```diff
-class AcmeAtomTransformer(nn.Module):
-    def __init__(self, config):
-        super().__init__()
-        self.encoder = AcmeEncoder(config)
-
-    def forward(self, hidden_states, **kwargs):
-        return self.encoder(hidden_states, **kwargs)
-
 class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
-        self.atom_transformer = AcmeAtomTransformer(config)
+        self.encoder = AcmeEncoder(config)
```

### TRF027

Sinaliza qualquer declaração `assert` em modeling_*.py, modular_*.py e configuration_*.py. `python -O` remove asserts, portanto, uma verificação de forma ou configuração escrita como um deles desaparece silenciosamente em execuções otimizadas. Um assert também gera um AssertionError genérico, enquanto um ValueError pode nomear o valor problemático e indicar o que fazer a respeito.

```diff
def forward(self, hidden_states):
-    assert hidden_states.dim() == 3
+    if hidden_states.dim() != 3:
+        raise ValueError(f"Expected a 3D tensor, got shape {tuple(hidden_states.shape)}.")
```

### TRF028

Verifica as primeiras 25 linhas de modeling_*.py, modular_*.py, configuration_*.py, processing_*.py, image_processing_*.py e video_processing_*.py em busca de uma linha `Licensed under the <name> License` seguida por todas as cláusulas do parágrafo padrão de garantia, de `You may obtain a copy of the License at` até `limitations under the License.`. As linhas são achatadas e convertidas para minúsculas primeiro, de modo que a quebra de linha e o estilo de comentário não importam. O nome da licença e a linha de direitos autorais não são verificados: eles variam por modelo. Um arquivo distribuído sem o cabeçalho deixa sua origem ambígua, e adicioná-lo depois significa modificar um arquivo já lançado. Correspondência apenas com `Apache License` deixaria passar os defeitos que realmente ocorrem: um parágrafo truncado no meio, um cabeçalho que para após a URL, ou um corrompido por uma substituição incorreta.

```diff
+# Copyright 2026 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# ...
 """PyTorch Acme model."""
```

### TRF029

Em modeling_*.py e modular_*.py, sinaliza um `__init__` que recebe `config` junto com um argumento cujo nome é inequivocamente um campo de config (hidden_size, num_attention_heads, intermediate_size, head_dim, num_hidden_layers, embed_dim, dropout, eps, patch_size, rope_theta, ...). Um parâmetro opcional com padrão `None` está isento: isso é uma sobrescrita, não uma segunda fonte de verdade, e é assim que uma classe de MLP atende tanto a largura densa quanto a largura dos especialistas de um modelo MoE. Um padrão codificado, como `hidden_size: int = 1024`, não está isento — ele prevalece sobre o config sempre que o chamador não passar nada. O kosmos2 está na lista de permissões: sua página de documentação não é derivável do nome do diretório, então o limite não pode isentá-lo. O mesmo número agora tem duas fontes de verdade e o chamador escolhe o vencedor, de modo que editar o config não altera mais o modelo que é construído. Isso também empurra o conhecimento da arquitetura para todos os pontos de chamada, onde ele não pertence.

```diff
class AcmeAttention(nn.Module):
-    def __init__(self, config, embed_dim, num_heads, dropout):
+    def __init__(self, config, layer_idx=None):
         super().__init__()
-        self.embed_dim = embed_dim
-        self.num_heads = num_heads
+        self.embed_dim = config.hidden_size
+        self.num_heads = config.num_attention_heads

 class AcmeMLP(nn.Module):
     # an optional override is fine: omitting it reads the config
     def __init__(self, config, intermediate_size=None):
         super().__init__()
         self.intermediate_size = intermediate_size or config.intermediate_size
```

### TRF030

Em modeling_*.py e modular_*.py, cadeias de atributos de flags enraizadas em `config` ou `self.config` com três ou mais níveis de profundidade. `config.hidden_size` (um salto) e `config.text_config.hidden_size` (dois saltos, o acesso normal a sub-configurações) estão corretos. Uma violação por linha. Um módulo que percorre `config.diffusion_config.atom_encoder_config.hidden_size` está acoplado à hierarquia inteira de configurações em vez de sua própria fatia, portanto não pode ser reutilizado, testado ou receber uma sub-configuração diferente. Passe a sub-configuração relevante para baixo e a cadeia se reduz a um salto.

```diff
class AcmeAtomEncoder(nn.Module):
     def __init__(self, config):
         super().__init__()
-        self.norm = AcmeLayerNorm(config.diffusion_config.atom_encoder_config.hidden_size)
+        self.norm = AcmeLayerNorm(config.hidden_size)
```

### TRF031

Em modeling_*.py e modular_*.py, sinaliza um `@dataclass` de nível superior cujas bases não incluem nada terminando em `Output`, a menos que ele tenha dois ou mais campos obrigatórios — esses são bundles de argumentos internos, que o `ModelOutput` rejeita em tempo de execução. Um dataclass de saída simples não é indexado como uma tupla, não sobrevive a `return_dict=False` e é invisível para @auto_docstring, de modo que seus campos nunca chegam à documentação da API gerada. O ModelOutput obtém os três gratuitamente.

```diff
@auto_docstring
 @dataclass
-class AcmeStructureOutput:
+class AcmeStructureOutput(ModelOutput):
     positions: torch.Tensor
     confidence: Optional[torch.Tensor] = None
```

### TRF032

Em modeling_*.py e modular_*.py, as flags masked_fill, masked_fill_, full, full_like e new_full chamadas com um literal numérico negado de magnitude 1e3 ou maior. Um -1e9 hardcoded transborda para -inf em float16 e está longe de ser o menor valor em float32, de modo que a mesma máscara se comporta de forma diferente para cada dtype e pode gerar NaNs após o softmax. `torch.finfo(dtype).min` é o menor valor representável no dtype que está sendo executado.

```diff
-attention_scores = attention_scores.masked_fill(~mask, -1e9)
+attention_scores = attention_scores.masked_fill(~mask, torch.finfo(attention_scores.dtype).min)
```

### TRF033

Em modeling_*.py e modular_*.py, marque os métodos cujo nome começa com `set_`, exceto os métodos do contrato PreTrainedModel set_input_embeddings, set_output_embeddings, set_decoder, set_encoder, set_attn_implementation e set_default_language. Um setter faz o comportamento depender da ordem das chamadas: o valor não está no config, portanto não é salvo, não é restaurado por from_pretrained e não é visível para o device-map ou para o planejamento de paralelismo. Os usuários precisam saber que devem chamá-lo, e esquecê-lo é silencioso.

```diff
class AcmeTriangleAttention(nn.Module):
-    def set_chunk_size(self, chunk_size):
-        self.chunk_size = chunk_size
+    def __init__(self, config):
+        super().__init__()
+        self.chunk_size = config.chunk_size
```

### TRF034

Em modeling_*.py e modular_*.py, sinaliza uma classe `*Layer`/`*Block` definida localmente e instanciada em um `nn.ModuleList(...)` que não alcança `GradientCheckpointingLayer` por meio de sua cadeia de bases; os arquivos modulares seguem imports relativos para modelos irmãos, e cadeias não resolvidas são inconclusivas. Fora do escopo: um modelo que nunca define `supports_gradient_checkpointing = True`, o que levanta uma exceção de `gradient_checkpointing_enable()` em vez de ignorar uma camada; uma camada que mantém `nn.BatchNorm*`/`nn.InstanceNorm*`, cujas estatísticas seriam recalculadas duas vezes; e uma pilha que não é o misturador de tokens do modelo, evidenciado por um módulo de atenção, modulação, misturador ou SSM atribuído como `self.x = Y(...)`. `gradient_checkpointing_enable()` envolve uma camada apenas se ela for um GradientCheckpointingLayer. Um nn.Module simples no tronco é ignorado silenciosamente, de modo que o treinamento parece estar com checkpointing, mas ainda aloca ativações completas, e o OOM aparece longe da causa. Em outros lugares -- um backbone de convolução, uma cabeça de decodificação -- a decisão é do autor, então a regra permanece fora.

```diff
-class AcmeDecoderLayer(nn.Module):
+class AcmeDecoderLayer(GradientCheckpointingLayer):
     def __init__(self, config, layer_idx):
         super().__init__()
```

### TRF035

Sinaliza comentários `# noqa`, com ou sem códigos, em modeling_*.py, modular_*.py e configuration_*.py. Em um arquivo modular, `F401`, `F821` e `F822` são aceitos: trata-se de uma fonte de geração que deliberadamente não define todos os nomes que utiliza, de modo que a família de regras de nomes indefinidos do ruff é acionada em código correto -- entradas de `__all__` preenchidas pelo conversor, classes que residem no modelo pai, imports mantidos para serem reexportados. Um `# noqa` que nomeia apenas esses códigos é ignorado; um que nomeia qualquer outra coisa é reportado nos códigos que restam. Um `# noqa` sem código é sempre reportado; em um arquivo modular, a mensagem pede o código em vez de uma reescrita. Os arquivos de modelo são código comum sujeito às regras de lint do repositório, portanto uma supressão significa que o problema subjacente foi deixado no lugar -- e um `# noqa` sem código também oculta todas as violações futuras naquela linha. Corrija o código em vez disso.

```diff
-from ...modeling_utils import PreTrainedModel  # noqa: F401
+from ...modeling_utils import PreTrainedModel
```

### TRF036

Em modeling_*.py e modular_*.py, sinaliza qualquer construção de `nn.Sequential(...)`. O Sequential nomeia seus filhos por posição, então os pesos ficam em `mlp.0.weight` e `mlp.2.weight`: o mapeamento de conversão, _tied_weights_keys e todos os planos de paralelismo passam a referenciar índices, e inserir uma camada renomeia tudo o que vem depois. Ele também oculta o forward, de modo que os casts de dtype e os resíduos entre etapas ficam invisíveis no local em que ocorrem.

```diff
-        self.mlp = nn.Sequential(
-            nn.Linear(config.hidden_size, config.intermediate_size),
-            nn.GELU(),
-            nn.Linear(config.intermediate_size, config.hidden_size),
-        )
+        self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
+        self.act = ACT2FN[config.hidden_act]
+        self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
```

### TRF037

Em modeling_*.py e modular_*.py, sinaliza chamadas a einsum, reportando a equação quando esta é um literal. Desativado por padrão: einsum é ocasionalmente a maneira mais clara de expressar uma contração, portanto, esta é uma opção ativa em vez de uma convenção rígida. Uma equação einsum codifica as formas em uma notação que o leitor precisa decodificar, e uma equação construída dinamicamente oculta qual contração é executada. Expanda-a em operações explícitas de matmul/transpose.

```diff
-        pair_bias = torch.einsum("bqhc,bkhc->bhqk", query_states, key_states)
+        pair_bias = query_states.permute(0, 2, 1, 3) @ key_states.permute(0, 2, 3, 1)
```

### TRF038

Verifica que cada arquivo modeling_*.py, processing_*.py, image_processing_*.py, video_processing_*.py, feature_extraction_*.py e tokenization_*.py possui um arquivo de teste correspondente em tests/models//test_*.py (modeling_acme.py -> tests/models/acme/test_modeling_acme.py). Isentos: configuration_*.py, coberto pelo ConfigTester no arquivo de teste correspondente test_modeling_*.py, e tokenization_utils*.py, um auxiliar coberto por meio de seu tokenizer; tokenization__fast.py é mapeado para o arquivo de teste do seu equivalente lento. Um arquivo modular_*.py pode definir várias famílias de uma vez, portanto suas classes são roteadas pelo sufixo do nome -- XxxModel/XxxPreTrainedModel/XxxFor para modeling, XxxImageProcessor(Fast) para image processing, XxxProcessor para processing, XxxVideoProcessor para video processing, XxxFeatureExtractor para feature extraction, XxxTokenizer(Fast) para tokenization, XxxConfig ignorado -- e uma violação é reportada para cada arquivo de teste ausente. `# trf-ignore: TRF038` não é suportado; use `allowlist_models` para que uma isenção seja visível na revisão. Um arquivo de origem sem arquivo de teste não tem cobertura de regressão: uma passagem forward quebrada, um mapeamento de conversão incorreto ou um tokenizer que descarta um token especial podem ser incorporados e permanecer quebrados indefinidamente. Uma configuração dummy com pesos aleatórios, ou um vocabulário pequeno escrito manualmente, é suficiente para exercitar qualquer um deles.

```diff
src/transformers/models/acme/modeling_acme.py
+tests/models/acme/test_modeling_acme.py
 src/transformers/models/acme/tokenization_acme.py
+tests/models/acme/test_tokenization_acme.py
```

### TRF039

Encontra blocos `if is_*_available(): import ...` (incluindo combinações como `is_vision_available() and is_torch_available()`) e sinaliza a importação quando o nome não é referenciado em nenhum outro lugar do arquivo, incluindo dentro de dicas de tipo em strings e __all__. A verificação de importação não utilizada do ruff não limpa esses casos: a importação é alcançável, então o bloco parece estar correto por si só. Quando uma refatoração não precisa mais de PIL.Image, torch, etc., a importação protegida permanece como peso morto e um sinal enganoso sobre as dependências reais do arquivo.

```diff
if is_vision_available():
-    from PIL import Image
```

### TRF040

Em modeling_*.py e modular_*.py, marque os métodos decorados com ambos @capture_outputs e @can_return_tuple. Complementa o TRF003, que cobre o ramificação manual de return_dict em forward(). Ambos os decoradores removem return_dict, de modo que apenas o mais externo vê o valor verdadeiro. @capture_outputs já trata a conversão para to_tuple, tornando @can_return_tuple redundante.

```diff
-@can_return_tuple
 @merge_with_config_defaults
 @capture_outputs
 @auto_docstring
 def forward(self, x):
     return AcmeModelOutput(last_hidden_state=x)
```

### TRF041

In modeling_*.py and modular_*.py, flags every `if`/`elif` and conditional expression whose condition reads a `config.*` or `self.config.*` attribute without a `# CODEPATH:` comment, accepted on the branch line or in the contiguous comment block directly above it. Any config attribute counts, not just boolean flags. Exempt by structure: `X if X is not None else fallback`, where the tested field is itself one of the results -- `getattr(config, x, default)` spelled long, which cannot fork the graph; and a guard, an `if` with no `else` whose body only raises or only warns/logs, since one side aborts and nothing diverges past it. Merely mentioning None does not qualify: `config.vision_config is not None` still owes a note. Exempt by field: framework plumbing that gates no checkpoint divergence -- `problem_type` selecting a loss, `hidden_act` looking up an activation, `num_labels`, `use_cache`, `is_decoder`, the special token ids, the `summary_*` head settings; the full list is `DEFAULT_EXEMPT_ATTRIBUTES` in `mlinter/trf041.py`, extended per project by `ignored_attributes = [...]` on the rule table. A model exempts one of its own fields file-wide with a module-level `# trf-ignore: TRF041 config.scale_embedding, config.auxiliary_loss` at column 0 (`self.config.x`, `config.x` and `x` are the same field). It must name at least one field -- a bare `# trf-ignore: TRF041` keeps its per-line meaning -- and a condition is skipped only when every field it reads is exempt. A config-gated branch is a second architecture in the same file, and the code cannot say whether both halves are still reachable -- which is how dead experimental branches survive for releases. The rule does not forbid the branch: like Rust's `// SAFETY:`, it asks for the checkpoints taking each side to be written down next to it. A branch nobody can name one for is a branch to delete.

```diff
+        # CODEPATH: ESMC-6B ships pre-normalised embeddings, the 300M/600M checkpoints do not.
         if config.use_embedding_norm:
             hidden_states = self.embedding_norm(hidden_states)

-        if config.msa_encoder_enabled:
-            hidden_states = self.msa_encoder(hidden_states)
+        # no released checkpoint sets msa_encoder_enabled -> branch removed
```

### TRF042

Em tests/models/*/test_tokenization_*.py, verifica se o arquivo define uma classe de teste coletada que herda de TokenizerTesterMixin. Somente as classes que o runner coleta contam — uma base `TestCase`, ou a convenção de nomenclatura `*Test` quando a base é uma classe de teste de outro modelo —, portanto, arquivos apenas com helpers são ignorados, e um helper que mistura a suíte não atende à regra para uma classe de teste real. A herança é rastreada através das bases no mesmo arquivo e para o teste de tokenizer de outro modelo importado por nome; uma base não resolúvel nunca conta. Reportado na primeira classe de teste que não executa a suíte. `auto` está na lista de permissões: test_tokenization_auto.py testa a resolução do AutoTokenizer, não um tokenizer. TokenizerTesterMixin é onde o round-trip de encode/decode, padding e truncamento, o tratamento de tokens especiais, a persistência de tokens adicionados e a equivalência de save/load são efetivamente verificados. Um teste que apenas afirma algumas listas de ids de tokens escritos à mão passa enquanto o tokenizer está quebrado em todos eles, e ainda parece testado na revisão.

```diff
-class AcmeTokenizationTest(unittest.TestCase):
+class AcmeTokenizationTest(TokenizerTesterMixin, unittest.TestCase):
+    tokenizer_class = AcmeTokenizer
+    test_slow_tokenizer = True
```

### TRF043

Sinaliza um parâmetro position_ids declarado na assinatura forward de classes cujo nome termina em Attention. position_ids é consumido a jusante pelo treinamento sem padding do flash-attention e deve fluir por meio de **kwargs. Nomeá-lo na assinatura o absorve antes que a interface de atenção possa lê-lo; o padrão llama passa position_embeddings além de **kwargs.

```diff
class AcmeAttention(nn.Module):
     def forward(
         self,
         hidden_states,
         position_embeddings,
         attention_mask=None,
-        position_ids=None,
         **kwargs: Unpack[TransformersKwargs],
     ):
```

### TRF044

Sinaliza um parâmetro chamado cache_position em qualquer função em modeling_*.py e modular_*.py. cache_position foi removido de todos os modelos na v5. Reintroduzi-lo (geralmente copiado de fontes anteriores à v5) propaga um argumento obsoleto por todas as camadas; a atualização do cache é past_key_values.update(key_states, value_states, self.layer_idx), sem propagação de posição.

```diff
def forward(
     self,
     hidden_states,
     past_key_values=None,
-    cache_position=None,
     **kwargs,
 ):
-    key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx, cache_position)
+    key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
```

### TRF045

Marca os parâmetros legados output_attentions, output_hidden_states e return_dict nas assinaturas de forward em modeling_*.py e modular_*.py. Modelos contribuídos antes da data limite estão isentos. A pilha de decoradores controla a saída: @capture_outputs resolve as flags output_* em relação à config e registra tensores via _can_record_outputs, e @can_return_tuple trata return_dict. Declará-los na assinatura reintroduz o encadeamento manual de flags que diverge dos decoradores.

```diff
+@capture_outputs
 def forward(
     self,
     input_ids,
-    output_attentions=None,
-    output_hidden_states=None,
-    return_dict=None,
     **kwargs: Unpack[TransformersKwargs],
 ):
```

### TRF046

Atribuições de flags a atributos de self em métodos forward em modeling_*.py e modular_*.py. O estado escrito durante o forward quebra o batching, o torch.compile e o raciocínio sobre o módulo. O estado transportado é passado explicitamente (objetos de cache, o loop de generate); valores que dependem apenas do config ou de formas estáticas pertencem a __init__.

```diff
def forward(self, hidden_states):
-    self.sequence_length = hidden_states.shape[1]
-    embeddings = self.compute_embeddings(self.sequence_length)
+    embeddings = self.compute_embeddings(hidden_states.shape[1])
```

### TRF047

Atribuições de flags a atributos de self nos métodos preprocess, _preprocess, __call__ e post_process* em image_processing_*.py e video_processing_*.py. Um processor que carrega estado entre chamadas quebra o loteamento de preprocess-many-then-postprocess: o segundo preprocess sobrescreve o estado de que o primeiro postprocess precisa. Retorne o valor ou passe-o pela cadeia de métodos.

```diff
def _preprocess(self, images, **kwargs):
-    self.original_sizes = [image.shape[-2:] for image in images]
+    original_sizes = [image.shape[-2:] for image in images]
     ...
+    return BatchFeature(data={"pixel_values": pixel_values, "original_sizes": original_sizes})
```

### TRF048

Sinaliza literais de lista/tupla/conjunto em declarações de nível de classe de _tied_weights_keys. A v5 alterou _tied_weights_keys para um dicionário que mapeia cada parâmetro de destino vinculado à sua origem. O formato de lista não indica mais qual parâmetro é a origem, fazendo com que o vínculo, o cálculo de device_map e o salvamento falhem silenciosamente.

```diff
class AcmeForCausalLM(AcmePreTrainedModel):
-    _tied_weights_keys = ["lm_head.weight"]
+    _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
```

### TRF049

As chamadas de flags em métodos __init__ em modeling_*.py e modular_*.py: primitivas nn.init.* / init.* e inicializadores in-place em parâmetros próprios (self.weight.data.normal_()). Os modelos são instanciados no dispositivo meta, portanto, os valores de tensor escritos em __init__ são descartados antes do carregamento: um parâmetro inicializado apenas ali contém conteúdo aleatório ao fazer fine-tuning do zero ou após um recarregamento do dispositivo meta. Alocar com torch.empty em __init__ e inicializar em _init_weights.

```diff
class AcmeEmbeddings(nn.Module):
     def __init__(self, config):
         super().__init__()
         self.position_embedding = nn.Parameter(torch.empty(config.num_positions, config.hidden_size))
-        nn.init.trunc_normal_(self.position_embedding, std=config.initializer_range)

 class AcmePreTrainedModel(PreTrainedModel):
     def _init_weights(self, module):
         super()._init_weights(module)
+        if isinstance(module, AcmeEmbeddings):
+            init.trunc_normal_(module.position_embedding, std=self.config.initializer_range)
```

### TRF050

Define flags para chamadas a uma classe *RotaryEmbedding* no __init__ de classes cujo nome termina em Attention. O Model possui um único rotary_emb, constrói inv_freq uma vez e passa cos/sin para baixo como position_embeddings. Um módulo de rotação por camada de atenção duplica buffers, recalcula frequências por camada e diverge do contrato de que a atenção recebe position_embeddings.

```diff
class AcmeAttention(nn.Module):
     def __init__(self, config, layer_idx):
         super().__init__()
-        self.rotary_emb = AcmeRotaryEmbedding(config)

 class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
+        self.rotary_emb = AcmeRotaryEmbedding(config)
```

### TRF051

Comparações de flags contra um atributo _attn_implementation em modeling_*.py e modular_*.py. O despacho de backends pertence a ALL_ATTENTION_FUNCTIONS.get_interface, e a manipulação condicional de tensores por backend (padding, reshaping) pertence aos wrappers compartilhados sob integrations/. A ramificação inline mantém o corpo do modelo ciente do kernel e quebra quando novos backends são registrados.

```diff
-if self.config._attn_implementation == "flash_attention_2":
-    attn_output = flash_path(query_states, key_states, value_states)
-else:
-    attn_output = eager_path(query_states, key_states, value_states)
+attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward)
+attn_output, attn_weights = attention_interface(self, query_states, key_states, value_states, ...)
```

### TRF052

Sinaliza atribuições em nível de módulo para nomes que terminam em _ATTENTION_CLASSES em modeling_*.py e modular_*.py. As classes de atenção por backend selecionadas de um dicionário são o idiom pré-interface: classes quase idênticas divergem entre si, e os kernels de atenção do hub registrados em ALL_ATTENTION_FUNCTIONS nunca as alcançam. Uma única classe de atenção que faz despacho através da interface substitui o dicionário; não a propague de um pai legado.

```diff
-ACME_ATTENTION_CLASSES = {
-    "eager": AcmeAttention,
-    "flash_attention_2": AcmeFlashAttention2,
-    "sdpa": AcmeSdpaAttention,
-}
+class AcmeAttention(nn.Module):
+    def forward(self, ...):
+        attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward)
```

### TRF053

Em modeling_*.py e modular_*.py, as atribuições de flags que constroem shift_logits/shift_labels (e variantes shifted_) por fatiamento, como em labels[..., 1:], são sinalizadas. Receber rótulos já deslocados (shift_labels = kwargs.pop("shift_labels", labels)) é o idiom correto e não é sinalizado. self.loss_function desloca os rótulos por conta própria, portanto, pré-deslocar no código de modeling treina em alvos duplamente deslocados ou força um caminho de perda personalizado. Modelos apenas de decodificador passam rótulos brutos e deixam a função de perda deslocá-los. Modelos codificador-decodificador são o caso espelhado: seus rótulos já estão deslocados pelo token inicial do decodificador pré-adicionado, portanto, eles passam shift_labels=labels para impedir que a perda os desloque novamente.

```diff
if labels is not None:
-    shift_logits = logits[..., :-1, :].contiguous()
-    shift_labels = labels[..., 1:].contiguous()
-    loss = nn.functional.cross_entropy(shift_logits.view(-1, self.config.vocab_size), shift_labels.view(-1))
+    # decoder-only: labels are unshifted, the loss shifts them
+    loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size)
+    # encoder-decoder: labels are already shifted, hand them over as shift_labels
+    loss = self.loss_function(logits=logits, labels=labels, shift_labels=labels, vocab_size=self.config.vocab_size)
```

### TRF055

Flags `config = SomeConfig` como atributo de classe em subclasses de PreTrainedModel em modeling_*.py e modular_*.py. `PreTrainedModel.__init_subclass__` deriva `config_class` de uma **anotação** de `config`, via `inspect.get_annotations(cls)`. Uma atribuição cria um atributo de classe órfão que a chamada não relata, de modo que a subclasse mantém silenciosamente o `config_class` do pai. Uma anotação pura não tem valor em tempo de execução, não cria nenhum atributo e é capturada corretamente.

```diff
class Gemma4VisionModel(Gemma4PreTrainedModel):
     """The Gemma 4 Vision Encoder."""
-    config = Gemma4VisionConfig
+    config: Gemma4VisionConfig
```

### TRF056

Em modeling_*.py e modular_*.py, as chamadas `.item()` e `.tolist()` dentro de qualquer `forward` são sinalizadas. Uma chamada `.tolist()` que alimenta o argumento de tamanho da divisão de `split(...)` está isenta: o torch.split precisa de ints do Python. Ambas as chamadas leem um tensor de volta para o host, o que o dynamo não consegue rastrear, causando a quebra do grafo.

```diff
-        for grid, item in zip(grid_thw.tolist(), split_items):
-            _, height, width = grid
-            merged.append(self.patch_merger(item, size=(height, width)))
+        for grid, item in zip(grid_thw, split_items):
+            merged.append(self.patch_merger(item, size=(grid[1], grid[2])))
```

### TRF057

Verifica `@auto_docstring` em subclasses públicas de `PreTrainedModel` (`<Model>PreTrainedModel`, `<Model>Model`, `<Model>For<Task>`, backbones), subclasses de `PreTrainedConfig`, subclasses de `ModelOutput`, processadores de imagem e subclasses de `ProcessorMixin`, e em seus métodos públicos `forward`, `get_image_features`, `get_video_features`, `get_audio_features`, `get_text_features`, `preprocess` e `__call__`. Um `modular_*.py` é verificado em relação aos arquivos gerados a partir dele. Sem ele, uma classe é distribuída sem introdução e sem documentação de parâmetros, e um método sem documentação de argumentos, sem a seção `Returns` e sem exemplo de uso -- tudo isso que então precisa ser escrito manualmente por modelo, em vez de vir do `auto_docstring.py`.

```diff
+@auto_docstring
 @dataclass
 class AcmeModelOutputWithPast(ModelOutput):
     logits: torch.Tensor | None = None

+@auto_docstring
 class AcmeForConditionalGeneration(AcmePreTrainedModel):
+    @auto_docstring
     def forward(self, input_ids, pixel_values=None, **kwargs):
         ...
```

### TRF058

Em modeling_*.py e modular_*.py, as chamadas `register_buffer("<name>", ...)` cujo nome do buffer é um literal de string, em qualquer receptor (`self`, ou outro módulo como `layer.mamba`). Um nome calculado -- uma variável ou f-string, por exemplo, um buffer por camada dentro de um loop -- não tem equivalente de atribuição de atributo e está isento. Desde o torch>=2.5, `nn.Buffer` registra um buffer por meio de atribuição de atributo simples, como `nn.Parameter`. Um buffer criado por uma chamada de método só existe como efeito colateral da execução de `__init__`, portanto um arquivo modular que deseja ajustar um deles precisa redefinir todo o `__init__`. Atribuído como um atributo, ele pode ser herdado e sobrescrito de forma independente.

```diff
-        self.register_buffer("inv_freq", inv_freq, persistent=False)
-        self.register_buffer(
-            "position_ids", torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False
-        )
+        self.inv_freq = nn.Buffer(inv_freq, persistent=False)
+        self.position_ids = nn.Buffer(torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False)
```

### TRF059

Para diretórios de modelos cujo plano de paralelismo de tensores atribui `moe_tp_experts`, verifica-se que as classes roteadas `*Experts` recebem os estados ocultos, os índices top-k e os pesos de roteamento top-k como os três primeiros argumentos posicionais de `forward`. Aliases como `selected_experts` e `routing_weights` são aceitos. `MoeExpertsParallel` aplica uma transformação de gradiente ao argumento posicional 3. Uma assinatura diferente aplica silenciosamente a transformação ao tensor errado ou a ignora.

```diff
class AcmeExperts(nn.Module):
-    def forward(self, hidden_states):
+    def forward(self, hidden_states, top_k_index, top_k_weights):
         ...
```

## Suprimindo violações

Se você precisar suprimir uma violação de regra, use uma das duas opções abaixo.

### Supressão inline

Adicione um comentário `# trf-ignore: RULE_ID` na linha que viola a regra. Inclua uma explicação para que os revisores entendam por que a supressão é justificada.

```py
# trf-ignore: TRF011 — mask is derived from self.config, not the layer
hidden_states = layer(hidden_states, attention_mask=mask_from_config)
```

Não use `trf-ignore` para silenciar violações que devem ser corrigidas no código.

### `allowlist_models`

Para modelos com código legado que não pode ser corrigido imediatamente, adicione o nome do diretório do modelo à lista `allowlist_models` da regra relevante no [mlinter rules.toml](https://github.com/huggingface/transformers-mlinter/blob/main/mlinter/rules.toml).

```toml
[rules.TRF004]
allowlist_models = ["existing_model", "your_model_name"]
```

