# قواعد بنية النموذج

يفرض Transformers مجموعة من القواعد الثابتة على كل ملف من ملفات `modeling_*.py` و`modular_*.py` و`configuration_*.py`. وتوفر حزمة [mlinter](https://github.com/huggingface/transformers-mlinter) محرك الفحص، بينما يحتفظ المستودع بمجموعة القواعد النشطة لديه في `utils/rules.toml`. ويتيح لنا ملف TOML المحلي تمكين القواعد أو تعطيلها أو ضبطها بسرعة دون انتظار إصدار جديد من `transformers-mlinter`.

> [!TIP]
> راجع [توثيق mlinter](https://huggingface.github.io/transformers-mlinter/) للاطلاع على مرجع الفاحص الكامل وتفاصيل الاستخدام.

هذه هي الأعراف المتوقعة للنماذج عند إضافة أو تغيير كود النمذجة. فهي تحافظ على اتساق قاعدة الكود وتضمن التوافق مع ميزات مثل توازلي خط الأنابيب (pipeline parallelism)، وخرائط الأجهزة (device maps)، وربط الأوزان (weight tying).

## تشغيل أداة الفحص

`make typing` يشغّل `mlinter` بالتوازي مع مُتحقّق الأنواع `ty` عبر غلاف المستودع، بحيث يلتقط `utils/rules.toml`. شغّل نفس الغلاف مباشرةً باستخدام الأوامر التالية.

```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
```

يُعدّ العلم `--changed-only` الخيار الأسرع أثناء التطوير. فهو يفحص فقط الملفات التي قمت بتعديلها مقارنةً بفرع main. إذا كنت تستدعي `mlinter` مباشرةً بدلاً من الغلاف، فمرّر `--rules-toml utils/rules.toml` حتى تُطبَّق التOverrides المحلية.

## إصلاح انتهاك

عند اكتشاف انتهاك لقاعدة، يبدو الخطأ على النحو التالي:

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

استخدم معرّف القاعدة للبحث عن الإصلاح في [مرجع القواعد](#rules-reference). يُطلق TRF013 عندما لا يستدعي فرع من [PreTrainedModel](/docs/transformers/main/ar/main_classes/model#transformers.PreTrainedModel) الدالة `self.post_init()`. تُنفّذ هذه الدالة خطوات نهائية أساسية، وإهمالها يؤدي إلى أخطاء وقت التشغيل.

```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()
```

## مرجع القواعد

تُدرج كل قاعدة أدناه ما تفرضه، مع عرض الفرق (diff) الذي يُظهر الإصلاح. شغّل `python utils/check_modeling_structure.py --rule TRF001` لعرض التوثيق المدمج لأي قاعدة ضمن مجموعة القواعد الحالية للمستودع.

### TRF001

يتحقق من أن config_class الخاص بـ PreTrainedModel يُسمّى Config. قد يؤدي عدم التطابق إلى تعطيل التحميل وفئات auto والتوقعات المطوَّرة.

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

### TRF002

يتحقق من أن base_model_prefix هو حرف ثابت غير فارغ وخالي من مسافات. قد تؤدي البادئات غير الصالحة إلى كسر خرائط مفاتيح تحميل الأوزان والوصول إلى النموذج الأساسي.

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

### TRF003

يُشير إلى نمط `if not return_dict: return (x,)` القديم في forward. ففرع return_dict اليدوي مُطوّل وسهل الوقوع في أخطائه. دع @capture_output أو @can_return_tuple يتكفل بذلك.

```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

يتحقق من عدم تعريف أي فئة نموذج لطريقة tie_weights. يؤدي تجاوز tie_weights إلى تعطيل التحميل، وحساب device_map، والحفظ. يجب إعلان الأوزان المرتبطة في خاصية الفئة _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

يتحقق من شكل _no_split_modules عند وجوده. قد تؤدي القيم غير الصالحة إلى تعطيل تقسيم خريطة الأجهزة والتجزئة.

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

### TRF006

يتحقق من أن حُجج الكاش في التوقيع الأمامي (forward) مستخدمة في الجسم. يشير حُجج الكاش غير المستخدمة إلى دعم غير مكتمل للكاش وسلوك غير متسق لواجهة البرمجة (API).

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

### TRF007

يتحقق من إسنادات خاصية self بعد استدعاء self.post_init() في __init__. فإن تعديل بنية النموذج بعد post_init يتجاوز مهام التهيئة والإنهاء الخاصة به.

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

### TRF008

يتحقق من أن add_start_docstrings على فئة النموذج تستقبل حجة غير فارغة. فالحجة الفارغة تنتج توثيقات API مولّدة غير واضحة ومنخفضة الجودة.

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

### TRF009

Flags imports into another model's package from a model's shipped implementation files: modeling_*, configuration_*, processing_*, image_processing_*, video_processing_*, feature_extraction_*, tokenization_* and generation_*.py. Three forms count: package path (`from transformers.models.other.modeling_other import X`), relative path (`from ..other.configuration_other import X`) and public API (`from transformers import OtherModel`) -- for the last the owning directory is inferred from the class name and confirmed against the classes it defines, so an unresolvable name is left alone. Out of scope: `modular_*.py` (building on another model is its purpose, and the converter flattens those imports away), `convert_*.py`, `__init__.py` and files under `auto`. Allowed targets: the model's own directory, `auto` and `timm_wrapper`. One model, one definition: model A's behavior lives in its own files, and a change to model B must not silently change model A -- for every file kind, not just modeling. Reach sub-configs through AutoConfig and CONFIG_MAPPING, and use a modular file to build on another model's code.

```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

يتحقق من أن فئات PreTrainedConfig/PretrainedConfig الفرعية المباشرة في ملفات configuration_*.py وmodular_*.py تحمل @strict(accept_kwargs=True). بدونها، تفقد الإعدادات الجديدة عقد التحقق من الأنواع وقت التشغيل الخاص بالمستودع، وتبتعد عن المعيار المعتمد على dataclass للإعدادات.

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

### TRF011

في forward() لفئات PreTrainedModel الفرعية، لن تكون عمليات الوصول إلى سمات submodule التي لا تتوفر في torch.nn.Identity متاحة: على متغيرات الحلقات فوق self.layers، وself. حيث  ليست سمة قياسية لـnn.Module. قد يستبدل توازي الأنابيب (Pipeline parallelism) أي submodule بـtorch.nn.Identity، لذا فإن قراءة سمة مخصصة (مثل decoder_layer.attention_type) منه تثير AttributeError وقت التشغيل. اقرأ بيانات الوصف (metadata) الخاصة بكل طبقة من 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

Checks that every PreTrainedModel subclass defining __init__ calls self.post_init(). In modular files super().__init__() counts, since it propagates the parent's post_init. post_init does essential finalization (weight init, gradient checkpointing setup, ...); skipping it causes subtle runtime bugs.

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

### TRF014

أعلام `trust_remote_code` المستخدمة أو المُمرَّرة (مثلًا كحجج اسمية) في ملفات دمج النماذج الأصلية. يُحمِّل `trust_remote_code` أكوادًا عشوائية، بما في ذلك الملفات الثنائية — وهي ميزة قوية للمستخدمين، وليست شيئًا يمكن لدمج أصلي أن يعتمد عليه، نظرًا إلى أنه لا يمكن مراجعة الكود البعيد أو صيانته داخل 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

عندما يحدد فرع من PreTrainedModel قيمة غير فارغة للمفتاح _tied_weights_keys، يتحقق من وجود حقل tie_word_embeddings في ملف الإعداد المرافق. وبدون هذا الحقل، لا يستطيع المستخدمون التحكم في ربط الأوزان: إذ يقوم النموذج بالربط بشكل غير مشروط، مما يكسر عمليات التحويل المتبادلة للتسلسل (serialization round-trips) والتدريب الدقيق باستخدام رؤوس غير مرتبطة.

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

### TRF016

عندما تُعلن فئة image_processing_*.py أو video_processing_*.py عن سمات منطقية do_* (مثل do_resize، do_rescale، do_normalize، do_convert_rgb، ...) وتُعيد تعريف preprocess() أو _preprocess()، يتحقق من أن كل علم لا يزال يُستهلك هناك: إما بالإشارة إليه مباشرةً، أو بالتفويض عبر super().preprocess/_preprocess(..., **kwargs)، أو -- لمعالجات الصور فقط -- بالتحويل عبر _preprocess_image_like_inputs/_prepare_image_like_inputs. يُستثنى do_sample_frames: تستهلكه preprocess() الأساسية قبل تشغيل _preprocess(). أي do_X لا تشير إليه إعادة التعريف أبداً يكون معطلاً: فلا يُحدث تعيين do_X=False أي تأثير، وتُنفَّذ العملية على أي حال، وتُكسر تجاوزات كل استدعاء بصمت.

```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

في الفئات التي تحمل كلًا من __@auto_docstring__ و__@dataclass__، يتم التحقق من أن __@auto_docstring__ يأتي أولًا. تُطبَّق المُزيِّنات من الأسفل إلى الأعلى، لذا فإن @dataclass في الأعلى يُنفِّذ @auto_docstring أولًا، على فئة لم يتم توليد init لها بعد: ثم يعدِّل doc الخاص بالفئة الأم بدلًا من فئة الفرع.

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

### TRF018

يتحقق من أن كل فئة فرعية من PreTrainedModel تعيد تعريف `_init_weights(self, module, ...)` تستدعي السلسلة عبر `super()._init_weights(...)`. كما تقبل الملفات المعيارية القيم المرجعية `PreTrainedModel._init_weights(self, module)` و`PreTrainedModel._init_weights(module)` و`raise AttributeError(...)`. لإلغاء التعريف الكامل المتعمد، قم بكبح التحذير بوضع `# trf-ignore: TRF018` فوق الدالة. تغطي الدالة الأساسية `_init_weights` أنواع الوحدات القياسية (Linear، Embedding، LayerNorm، RotaryEmbedding، ...). إذا تم تجاوزها، ستبقى جميع الوحدات الفرعية التي لا تغطيها إعادة التعريف غير مهيأة -- وهو ما يمر عبر الاختبارات ثم يظهر لاحقًا كخطأ دقيق في تهيئة الأوزان (انظر 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

يُصدر تحذيرًا عند وجود `_defaults` غير فارغ في قواميس `*ProcessorKwargs` من نوع TypedDict في ملفات `processing_*.py`. النماذج التي تم إصدارها قبل تاريخ الحد النهائي معفاة من هذا الشرط. إن تثبيت قيم `_defaults` يدويًا يؤدي إلى تشتيت إعدادات المعالج عبر مصادر Python، ويجعل تجاوزها من ملف الإعداد أمرًا غير مريح، كما يُثقل الكود. أما في `processor_config.json` على المنصة، فتنتقل هذه الإعدادات مع نقطة التفتيش ويمكن تحديثها دون الحاجة إلى أي تغيير في الكود.

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

### TRF020

في دلائل النماذج التي تُعلن في ملفها الإعدادي عن `kv_lora_rank` (انتباه كامن متعدد الرؤوس)، يتم فحص فئة الانتباه التي تمتلك إسقاط توسيع KV LoRA (`kv_b_proj`، أو أي `nn.Linear(config.kv_lora_rank, ...)`): يجب أن يكون التوسيع في طريقة مخصصة (مثل `expand_kv`) تستدعيها `forward()`، وليس مضمّناً في الكود. في الملفات المعيارية، تُحتسب الطريقة الموروثة من فئة أساسية مستوردة. الخوادم الخارجية (vLLM/SGLang) تُعيد تعريف التوسيع لاستهلاك ذاكرة KV المضغوطة مباشرةً. إذا كان التوسيع مضمّناً في `forward()`، فلا يوجد ما يُعاد تعريفه، لذا يجب على الخادم إنشاء ذاكرة كاملة -- مما يفقدنا توفير الذاكرة الذي وُجد من أجله MLA.

```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

In modeling_*.py and modular_*.py, flags `torch.tensor(<value>, ..., device=<non-cpu>)` where `<value>` provably resolves to a Python scalar -- from numeric literals and arithmetic on them, torch.finfo/iinfo fields, scalar-returning builtins and math.* calls, locals bound exactly once, self. assigned in the class body, and config fields annotated int/float/bool in the companion configuration file (following attribute_map). Anything that may also be a sequence (`eos_token_id: int | list[int] | None`) or that cannot be resolved is left alone. __init__, _init_weights, __post_init__ and post_init are exempt: they never run inside a capture region. torch.tensor(, device=) materialises the value on the host then copies it to the device; CUDA graph capture forbids that copy, so the model cannot be captured. torch.full((), , dtype=..., device=...) fills the same 0-d tensor on-device with a capturable kernel and no synchronisation.

```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

يتحقق من أن كل سلسلة نصية في قائمة `_no_split_modules` داخل ملف modeling_*.py أو modular_*.py تسمّي فئة معرّفة في ذلك الملف، أو مستوردة إليه، أو معرّفة بواسطة وحدة أخت من نفس دليل النموذج. يكمل هذا الفحص TRF005 الذي يتحقق فقط من شكل القيمة. يطابق `device_map` هذه السلاسل النصية مع `module.__class__.__name__` وقت التشغيل، لذا فإن الاسم القديم أو المكتوب بشكل خاطئ لا يطابق شيئاً ويُتجاهل بصمت -- ما يعني أن الوحدة التي يجب إبقاؤها معاً قد تُقسَّم عبر الأجهزة. احذف العناصر التي تسمّي فئات نموذج آخر بدلاً من تصحيحها: فـ `post_init` يجمع `_no_split_modules` من النماذج الفرعية بالفعل.

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

### TRF023

In configuration_*.py and modular_*.py, flags `*Config` fields named after an upstream paper's abbreviation instead of the canonical name: 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. Fields are read from the class body and from __init__/__post_init__ assignments and defaults. Ambiguous but idiomatic names (num_heads, num_layers, embed_dim, mlp_ratio) are not flagged, and models added before cutoff_date keep theirs. Everything that reads a model's shape -- device_map planning, tensor/pipeline parallel plans, quantization, PEFT, attention-backend selection, `attribute_map` consumers -- looks up the canonical names, so a config spelling the same quantity `d_model` silently opts out of all of them. Map the checkpoint's own spelling in the conversion script.

```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

In modeling_*.py and modular_*.py, flags an integer literal greater than 8 in a dimension argument of a torch.nn constructor (Linear, Embedding, LayerNorm, RMSNorm, GroupNorm, BatchNorm*, InstanceNorm*, Conv*d, ConvTranspose*d, Bilinear, MultiheadAttention), positional or by keyword (in_features, out_features, in_channels, out_channels, num_embeddings, embedding_dim, embed_dim, normalized_shape, num_channels, hidden_size). Operator-shape arguments (kernel_size, stride, padding, num_groups) are ignored; literals up to 8 keep scalar heads, binary classifiers and RGB channel counts clean. Models added before cutoff_date are exempt. A hardcoded width pins the module to one checkpoint size: the same architecture at another scale loads with a shape mismatch, and `from_pretrained` cannot say which value is wrong because no config field points at it. It also splits the source of truth, so editing the config no longer changes the model that gets built.

```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

في modeling_*.py وmodular_*.py، تُستدعى الأعلام (flags) إلى مصنع الأقنعة (mask factory) (نقاط الدخول في masking_utils وهي create_causal_mask، create_bidirectional_mask، create_sliding_window_causal_mask، create_chunked_causal_mask، create_masks_for_generate، وأي مساعد create_*_mask) داخل فئة لا ترث من PreTrainedModel -- كتل nn.Module عادية مثل الطبقات، ووحدات الانتباه، والمشفّرات. بناء القناع هو عمل تعقيدُه O(مربع طول التسلسل) ولا يتغير من طبقة إلى أخرى، لذا فإن بناؤه داخل الطبقة يدفع هذا التكلفة مرة واحدة لكل طبقة، وتصبح كل طبقة مالكةً لقناعها الخاص، فلا يمكن بعد ذلك تسليم واجهات الخلفية (backends) للانتباه قناعًا واحدًا مُجهَّزًا. ابنِ القناع مرة واحدة في النموذج ثم مرّره إلى الأسفل.

```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

في ملفات modeling_*.py وmodular_*.py، يُشير إلى فئة غير PreTrainedModel تعرّف فقط __init__ وforward، وتُعيّن بالضبط خاصية self. واحدة في __init__، وجسم forward فيها هو بالضبط `return self.<attr>(...)` لها (يتم تجاهل سلسلة التوثيق الأولية). أي طريقة أخرى، أو خاصية إضافية، أو بيان قبل الأمر return، أو استدعاء `super()` في forward يعني أن الفئة تؤدي عملها الخاص. يضيف الغلاف مستوى إلى كل اسم وزن، وإلى _no_split_modules، وخطط التوازي، وكل خرائط التحويل، دون حساب أي شيء -- والقراء مضطرون لفتح فئة إضافية واحدة لاكتشاف ذلك. فئات PreTrainedModel الفرعية معفاة: فهي موجودة من أجل from_pretrained والفئات التلقائية حتى عندما يقتصر forward على التحويل فقط.

```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

تُشير إلى أي عبارة `assert` في ملفات modeling_*.py و modular_*.py و configuration_*.py. حيث أن `python -O` يزيل هذه العبارات، مما يعني أن فحصًا للشكل أو الإعداد مكتوبًا كعبارة `assert` يختفي بصمت في التشغيل المُحسَّن. كما أن عبارة `assert` تُنتج خطأ AssertionError عاريًا، بينما يمكن لخطأ ValueError تسمية القيمة المخالفة وتحديد الإجراء المطلوب.

```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

Checks the first 25 lines of modeling_*.py, modular_*.py, configuration_*.py, processing_*.py, image_processing_*.py and video_processing_*.py for a `Licensed under the <name> License` line followed by every clause of the standard warranty paragraph, from `You may obtain a copy of the License at` through `limitations under the License.`. Lines are flattened and lowercased first, so wrapping and comment style do not matter. The license name and copyright line are not checked: they vary per model. A file shipped without the header leaves its provenance ambiguous, and adding it later means touching an already-released file. Matching only `Apache License` would let through the defects that actually occur: a paragraph truncated mid-way, a header that stops after the URL, or one mangled by a bad search-and-replace.

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

### TRF029

In modeling_*.py and modular_*.py, flags an `__init__` taking `config` alongside an argument whose name is unambiguously a config field (hidden_size, num_attention_heads, intermediate_size, head_dim, num_hidden_layers, embed_dim, dropout, eps, patch_size, rope_theta, ...). A parameter optional with a `None` default is exempt: that is an override, not a second source of truth, and it is how one MLP class serves both the dense and the expert width of a MoE model. A hardcoded default such as `hidden_size: int = 1024` is not -- it wins over the config whenever the caller passes nothing. kosmos2 is allowlisted: its doc page is not derivable from the directory name, so the cutoff cannot grandfather it. The same number now has two sources of truth and the caller picks the winner, so editing the config no longer changes the model that gets built. It also pushes architecture knowledge out to every call site, where it does not belong.

```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

في modeling_*.py وmodular_*.py، تُحدَّد سلاسل السمات التي تبدأ من `config` أو `self.config` وتبلغ عمقها ثلاثة مستويات أو أكثر. يُعدّ `config.hidden_size` (قفزة واحدة) و`config.text_config.hidden_size` (قفزتان، وهو الوصول المعتاد إلى الإعداد الفرعي) مقبولين. يُحتسب انتهاك واحد لكل سطر. الوحدة التي تتعامل مع `config.diffusion_config.atom_encoder_config.hidden_size` تكون مرتبطة بالهرمية الكاملة للإعداد بدلاً من شريحتها الخاصة، مما يمنع إعادة استخدامها أو اختبارها أو تزويدها بإعداد فرعي مختلف. مرّر الإعداد الفرعي ذي الصلة إلى الأسفل، فتختصر السلسلة إلى قفزة واحدة.

```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

In modeling_*.py and modular_*.py, flags a top-level `@dataclass` whose bases include nothing ending in `Output`, unless it has two or more mandatory fields -- those are internal argument bundles, which `ModelOutput` rejects at runtime. A plain output dataclass does not index like a tuple, does not survive `return_dict=False`, and is invisible to @auto_docstring, so its fields never reach the generated API docs. ModelOutput gets all three for free.

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

### TRF032

In modeling_*.py and modular_*.py, flags masked_fill, masked_fill_, full, full_like and new_full called with a negated numeric literal of magnitude 1e3 or more. A hardcoded -1e9 overflows to -inf in float16 and is nowhere near the smallest value in float32, so the same mask behaves differently per dtype and can produce NaNs after softmax. `torch.finfo(dtype).min` is the smallest representable value in whatever dtype is actually running.

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

### TRF033

In modeling_*.py and modular_*.py, flags methods whose name starts with `set_`, except the PreTrainedModel contract methods set_input_embeddings, set_output_embeddings, set_decoder, set_encoder, set_attn_implementation and set_default_language. A setter makes behaviour depend on call order: the value is not in the config, so it is not saved, not restored by from_pretrained, and not visible to device-map or parallelism planning. Users have to know to call it, and forgetting is silent.

```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

In modeling_*.py and modular_*.py, flags a locally-defined `*Layer`/`*Block` class instantiated in an `nn.ModuleList(...)` that does not reach `GradientCheckpointingLayer` through its base chain; modular files follow relative imports into sibling models, and unresolved chains are inconclusive. Out of scope: a model that never sets `supports_gradient_checkpointing = True`, which raises from `gradient_checkpointing_enable()` instead of skipping a layer; a layer holding `nn.BatchNorm*`/`nn.InstanceNorm*`, whose statistics would be recomputed twice; and a stack that is not the model's token mixer, shown by an attention, modulation, mixer or SSM module assigned as `self.x = Y(...)`. `gradient_checkpointing_enable()` wraps a layer only if it is a GradientCheckpointingLayer. A plain nn.Module on the trunk is skipped silently, so training looks checkpointed while still allocating full activations, and the OOM surfaces far from the cause. Elsewhere -- a conv backbone, a decode head -- the trade is the author's call, so the rule stays out.

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

### TRF035

تُعلَّم تعليقات `# noqa`، سواءً كانت تحمل رموزًا أم لا، في ملفات modeling_*.py و modular_*.py و configuration_*.py. في ملف modular، تُقبل الرموز `F401` و `F821` و `F822`: فهو مصدر توليد لا يُعرّف عمدًا كل اسم يستخدمه، لذا تُطلق أدوات ruff الخاصة بالأسامي غير المعرّفة على كود صحيح -- إدخالات `__all__` التي يملؤها المحوّل، فئات مقيمة في النموذج الأب، واستيرادات تُحفظ لإعادة التصدير. تُتخطى أي علامة `# noqa` تُسمّي تلك الرموز فقط؛ أما أي علامة تُسمّي أي شيء آخر فتُبلَّغ عن الرموز المتبقية. تُبلَّغ دائمًا عن علامة `# noqa` عارية؛ وفي ملف modular، تطلب الرسالة ذكر الرمز بدلًا من إعادة الكتابة. ملفات النموذج هي كود عادي يخضع لقواعد الفحص في المستودع، لذا فإن أي كبت يعني أن المشكلة الكامنة تُركت في مكانها -- كما أن علامة `# noqa` العارية تخفي أيضًا كل مخالفة مستقبلية على تلك السطر. صحّح الكود بدلًا من ذلك.

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

### TRF036

In modeling_*.py and modular_*.py, flags any `nn.Sequential(...)` construction. Sequential names its children by position, so weights land at `mlp.0.weight` and `mlp.2.weight`: the conversion mapping, _tied_weights_keys and every parallelism plan then reference indices, and inserting a layer renames everything after it. It also hides the forward, so the dtype casts and residuals between steps are invisible where they happen.

```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

في modeling_*.py وmodular_*.py، تُراقب استدعاءات einsum، وتُبلّغ عن المعادلة عندما تكون ثابتة (literal). تكون هذه الميزة معطّلة افتراضيًا: فـeinsum قد تكون في بعض الأحيان الطريقة الأوضح للتعبير عن عملية انكماش (contraction)، لذا فهي اختيارية وليست قاعدة صارمة. تُشفّر معادلة einsum الأشكال في رمز يحتاج القارئ إلى فكّ تشفيره، كما أن المعادلة المبنية ديناميكيًا تُخفي أي عملية انكماش هي التي تُنفَّذ فعليًا. قم بتوسيعها إلى عمليات 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

يتحقق من أن كل ملف من ملفات modeling_*.py، processing_*.py، image_processing_*.py، video_processing_*.py، feature_extraction_*.py، وtokenization_*.py له ملف اختبارات مطابق في tests/models//test_*.py (فمثلاً modeling_acme.py <-> tests/models/acme/test_modeling_acme.py). الملفات المعفاة: configuration_*.py، حيث تغطيها فئة ConfigTester في ملف الاختبار المرافق test_modeling_*.py، وكذلك tokenization_utils*.py، وهي أداة مساعدة تغطيها اختبارات مُجزئتها (tokenizer)؛ كما أن tokenization__fast.py يتوافق مع ملف اختبارات نظيره البطيء. يمكن لملف modular_*.py أن يحدد عدة عائلات في آنٍ واحد، لذا تُوجَّه فئاته بناءً على لاحقة الاسم: XxxModel/XxxPreTrainedModel/XxxFor للنمذجة، XxxImageProcessor(Fast) لمعالجة الصور، XxxProcessor للمعالجة، XxxVideoProcessor لمعالجة الفيديو، XxxFeatureExtractor لاستخراج الميزات، XxxTokenizer(Fast) للتجزئة، وXxxConfig تُتجاهل — ويُبلَّغ عن مخالفة واحدة لكل ملف اختبارات مفقود. لا يُدعم `# trf-ignore: TRF038`؛ استخدم `allowlist_models` حتى تكون الإعفاءات مرئية أثناء المراجعة. الملف المصدر الذي لا يملك ملف اختبارات يفتقر إلى تغطية الانحدار: فقد يُدمج مسار أمامي معطوب، أو خرائط تحويل خاطئة، أو مُجزئ يتجاهل رمزا خاصاً، ويبقى معطوباً إلى أجل غير مسمى. يكفي إعداد وهمي بأوزان عشوائية، أو مفردات صغيرة مكتوبة يدوياً، لاختبار أيٍّ من هذه الحالات.

```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

يكتشف كتل `if is_*_available(): import ...` (بما في ذلك التوليفات مثل `is_vision_available() and is_torch_available()`) ويضع علامة على الاستيراد عندما لا يُشار إلى الاسم في أي مكان آخر من الملف، بما في ذلك داخل تلميحات الأنواع النصية و__all__. فحص الاستيرادات غير المستخدمة في ruff لا ينظّف هذه الكتل: الاستيراد قابل للوصول، لذا تبدو الكتلة سليمة في حد ذاتها. بمجرد أن لم تعد إعادة الهيكلة بحاجة إلى PIL.Image أو torch أو غير ذلك، يبقى الاستيراد المحمي كحمولة ميتة وإشارة مضللة حول التبعيات الفعلية للملف.

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

### TRF040

في modeling_*.py وmodular_*.py، تُوسِّع الأعلام (flags) الطرق المُزيَّنة بكلٍّ من @capture_outputs و@can_return_tuple. يكمل هذا الأمر TRF003، الذي يتناول تفرّع return_dict اليدوي داخل forward(). نظرًا لأن كلا المُزيِّنين (decorators) يُزيلان return_dict، فإن المُزيِّن الخارجي فقط هو الذي يرى القيمة الحقيقية. كما أن @capture_outputs يتعامل بالفعل مع التحويل إلى to_tuple، مما يجعل @can_return_tuple زائدًا عن الحاجة.

```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

In tests/models/*/test_tokenization_*.py, checks the file defines a collected test class inheriting TokenizerTesterMixin. Only classes the runner collects count -- a `TestCase` base, or the `*Test` naming convention when the base is another model's test class -- so helper-only files are skipped, and a helper mixing in the suite does not satisfy the rule for a real test class. Inheritance is followed through bases in the same file and into another model's tokenizer test imported by name; an unresolvable base never counts. Reported on the first test class that does not run the suite. `auto` is allowlisted: test_tokenization_auto.py tests AutoTokenizer resolution, not a tokenizer. TokenizerTesterMixin is where encode/decode round-tripping, padding and truncation, special-token handling, added-token persistence and save/load equivalence are actually checked. A test that only asserts a few hand-written token id lists passes while the tokenizer is broken in every one of them, and still looks tested in review.

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

### TRF043

يُصدر تحذيرًا عند وجود معامل position_ids مُعلَن في توقيع forward للفئات التي تنتهي أسماؤها بـ Attention. يُستهلك position_ids في المراحل اللاحقة بواسطة تدريب flash-attention بدون حشو، ويجب أن يمر عبر **kwargs. تسمية هذا المعامل في التوقيع تستهلكه قبل أن تتمكن واجهة الانتباه من قراءته؛ بينما تمرر معيار llama position_embeddings بالإضافة إلى **kwargs.

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

### TRF044

يُصدر تحذيرًا عند وجود معامل باسم cache_position في أي دالة ضمن ملفات modeling_*.py وmodular_*.py. تم حذف cache_position من جميع النماذج في الإصدار v5. إعادة إدخاله (عادةً عن طريق نسخه من مصادر ما قبل v5) يؤدي إلى تمرير معامل غير مستخدم عبر كل طبقة؛ فحدّث الكاش يتم عبر past_key_values.update(key_states, value_states, self.layer_idx)، دون أي تمرير للموضع.

```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

يُشير إلى المعاملات القديمة output_attentions و output_hidden_states و return_dict في توقيعات الدالة forward داخل ملفات modeling_*.py و modular_*.py. النماذج المُساهَم بها قبل تاريخ الانتهاء معفاة من ذلك. يملك مكدس الزخارف (Decorators) التحكم في المخرجات: حيث يحلّ الزخرفة @capture_outputs أعلام output_* مقابل الإعدادات (config) ويسجّل الموترات (tensors) عبر _can_record_outputs، بينما تتولى الزخرفة @can_return_tuple معالجة return_dict. إعلان هذه المعاملات في التوقيع يعيد إدخال تمرير الأعلام يدويًا، مما يؤدي إلى انحراف عن سلوك الزخارف.

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

### TRF046

تعيين الأعلام إلى سمات self في طرق forward داخل ملفات modeling_*.py و modular_*.py. الحالة المكتوبة أثناء forward تُكسر معالجة الدفع (batching) و torch.compile والاستدلال على الوحدة. تُمرَّر الحالة المنقولة بشكل صريح (كائنات cache، حلقة generate)؛ أما القيم التي تعتمد فقط على config أو الأشكال الثابتة فتُوضع في __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

تعيينات الأعلام إلى سمات self في طرق preprocess و _preprocess و __call__ و post_process* داخل ملفات image_processing_*.py و video_processing_*.py. المعالج الذي يحمل حالة بين الاستدعاءات يكسر معالجة الدفعات من نوع "preprocess-then-postprocess": فعملية preprocess الثانية تكتب فوق الحالة التي تحتاجها عملية postprocess الأولى. أعد القيمة أو مررها عبر سلسلة الطرق.

```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

تُحدِّد قائمة/مجموعة/مجموعة غير مرتبة (list/tuple/set) الحرفية في إعلانات _tied_weights_keys على مستوى الفئة. في الإصدار v5، تغيّر _tied_weights_keys إلى قاموس (dict) يربط كل معامل مستهدف مرتبط (tied target parameter) بمصدره. لم تعد صيغة القائمة (list) تُبيّن أيّ معامل هو المصدر، مما يؤدي إلى سلوك خاطئ صامت في عمليات الربط (tying) وحساب device_map والحفظ.

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

### TRF049

تُستدعى أعلام init في طرق __init__ داخل ملفات modeling_*.py و modular_*.py: بدائل nn.init.* / init.* والمُهيِّئات التي تُعدّل في الموقع على المعاملات الخاصة (self.weight.data.normal_()). تُنشأ النماذج على جهاز meta، لذا تُستبعد قيم المصفوفات المكتوبة في __init__ قبل التحميل: المعامل الذي يُهيَّأ فقط هناك يحتفظ بمحتوى عشوائي عند الضبط الدقيق من الصفر أو بعد إعادة تحميل جهاز meta. خصّص باستخدام torch.empty في __init__، وابدأ التهيئة في _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

تُوجَّه استدعاءات إلى فئة *RotaryEmbedding* في __init__ للفئات التي تنتهي أسماؤها بـ Attention. تمتلك النموذج rotary_emb واحدة، وتبني inv_freq مرة واحدة، وتُمرِّر cos/sin إلى الأسفل على شكل position_embeddings. وجود وحدة rotary واحدة لكل طبقة انتباه يؤدي إلى تكرار المخازن المؤقتة، وإعادة حساب الترددات في كل طبقة، والانحراف عن العقد التي تنص على أن الانتباه يستقبل 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

تقارن الأعلام (Flags) مع خاصية _attn_implementation في ملفات modeling_*.py وmodular_*.py. ينتمي توجيه الخلفية (Backend dispatch) إلى ALL_ATTENTION_FUNCTIONS.get_interface، بينما ينتمي تعديل المصفوفات (Tensor munging) المشروط بالخلفية (مثل الحشو وإعادة التشكيل) إلى الأغلفة المشتركة (shared wrappers) الموجودة تحت integrations/. يؤدي التفرع المضمّن (Inline branching) إلى جعل جسم النموذج واعياً بالنواة (kernel-aware)، مما يؤدي إلى كسر العمل عند تسجيل خلفيات جديدة.

```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

تُشير الأعلام إلى إسنادات على مستوى الوحدة (module-level) لأسماء تنتهي بـ _ATTENTION_CLASSES في ملفات modeling_*.py وmodular_*.py. ففئات الانتباه الخاصة بكل منصة (backend) المختارة من قاموس (dict) هي النمط الشائع قبل اعتماد الواجهة: فالفئات المتطابقة تقريبًا تتباعد عن بعضها البعض، ولا تصل نوى الانتباه (attention kernels) المسجلة في ALL_ATTENTION_FUNCTIONS إليها أبدًا. تستبدل فئة انتباه واحدة تُوجَّه عبر الواجهة هذا القاموس؛ ولا ينبغي أن تُنقل من فئة أم قديمة (legacy).

```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

في ملفات modeling_*.py وmodular_*.py، تُعلَّم إسنادات الأعلام التي تبني shift_logits/shift_labels (والنسخ المُزاحة منها) عبر التقطيع، كما في labels[..., 1:]. استلام تسميات مُزاحة مسبقًا (shift_labels = kwargs.pop("shift_labels", labels)) هو التعبير الصحيح ولا يُعلَّم عليه. يقوم self.loss_function بتزحيز التسميات بنفسه، لذا فإن التزحيز المسبق في كود النموذج يؤدي إلى التدريب على أهداف مُزاحة مرتين، أو يفرض مسار خسارة مخصصًا. تمرّر النماذج ذات المُفكِّك فقط التسميات الخام وتترك لدالة الخسارة مهمة تزحيزها. أما النماذج ذات المُشفِّر-المُفكِّك فهي الحالة المعاكسة: فالتسميات فيها مُزاحة مسبقًا بفضل رمز بداية المُفكِّك المُضاف في البداية، لذا تمرّر shift_labels=labels لمنع دالة الخسارة من تزحيزها مرة أخرى.

```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

أعلام `config = SomeConfig` كخاصية صنف في فئات PreTrainedModel الفرعية داخل ملفات modeling_*.py و modular_*.py. يستنتج `PreTrainedModel.__init_subclass__` قيمة `config_class` من `config` **تسمية**، وذلك عبر `inspect.get_annotations(cls)`. يؤدي إنشاء إسناد إلى خاصية صنف زائدة لا يبلغ عنها الاستدعاء، فيحتفظ الصنف الفرعي بصمت بقيمة `config_class` الخاصة بالصنف الأب. أما التسمية المجردة فلا تحمل قيمة وقت التشغيل، ولا تنشئ أي خاصية، وتُلتقط بشكل صحيح.

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

### TRF056

في modeling_*.py وmodular_*.py، تُستخدم الأعلام `.item()` و`.tolist()` داخل أي `forward`. أما استدعاء `.tolist()` الذي يغذي معامل حجم التقسيم في `split(...)` فهو مستثنى: إذ يحتاج torch.split إلى أرقام صحيحة من نوع Python. كلا الاستدعاءين يقرآن متجهًا (tensor) مرة أخرى إلى المضيف، وهو ما لا يمكن لـ dynamo تتبّعه، مما يؤدي إلى انقطاع الرسم البياني.

```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

يتحقق `@auto_docstring` من فئات `PreTrainedModel` العامة (`<Model>PreTrainedModel`، `<Model>Model`، `<Model>For<Task>`، والعناصر الأساسية)، وفئات `PreTrainedConfig`، وفئات `ModelOutput`، ومعالجات الصور، وفئات `ProcessorMixin`، وكذلك من طرقها العامة `forward`، `get_image_features`، `get_video_features`، `get_audio_features`، `get_text_features`، `preprocess` و`__call__`. يتم فحص `modular_*.py` مقابل الملفات المولّدة منه. وبدونه، تُشحن الفئة دون مقدمة ودون توثيق للمعاملات، والطريقة دون توثيق للحجج، وقسم `Returns`، ومثال على الاستخدام -- وكل ذلك يجب كتابته يدوياً لكل نموذج بدلاً من أن يأتي من `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

في ملفات modeling_*.py و modular_*.py، تُستثنى استدعاءات `register_buffer("<name>", ...)` التي يكون اسم المخزن فيها نصاً حرفياً، بغض النظر عن الكائن المستقبِل (`self`، أو وحدة أخرى مثل `layer.mamba`). أما الاسم المحسوب — أي متغير أو سلسلة f، على سبيل المثال مخزن واحد لكل طبقة داخل حلقة — فلا يوجد له مكافئ من إسناد السمات، لذا فهو مستثنى. نظراً لأن torch>=2.5 `nn.Buffer` يسجّل المخزن عبر إسناد سمة عادي، تماماً مثل `nn.Parameter`. فالمخزن الذي يُنشأ عبر استدعاء دالة لا يوجد إلا كأثر جانبي لتشغيل `__init__`، لذا فإن الملف المعياري الذي يرغب في تعديل مخزن واحد يجب أن يعيد تعريف `__init__` بالكامل. أما إذا أُسند كسمة، فيمكن توريثه وتجاوزه بشكل مستقل.

```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

بالنسبة إلى مديريات النماذج التي يخصص مخطط التوازي فيها للتensors `moe_tp_experts`، يتحقق من أن فئات `*Experts` الموجَّهة تستقبل حالات المخفية، والفهارس الأعلى k، وأوزان التوجيه الأعلى k كأول ثلاثة حواجز موضعية في `forward`. تُقبل الأسماء المستعارة مثل `selected_experts` و`routing_weights`. يطبّق `MoeExpertsParallel` تحويلًا على التدرج للحاجز الموضعي الثالث. إذا كانت التوقيع مختلفًا، فسيُطبَّق ذلك على tensor خاطئ بصمت أو يُتخطى.

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

## كبت الانتهاكات

إذا كنت بحاجة إلى كبح انتهاك قاعدة، فاستخدم أحد الخيارين أدناه.

### القمع المضمّن

أضِف تعليق `# trf-ignore: RULE_ID` على السطر المخالف. تضمّن شرحًا حتى يفهم المراجعون سبب مبرر الكبت.

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

لا تستخدم `trf-ignore` لإسكات المخالفات التي يجب إصلاحها في الكود.

### `allowlist_models`

بالنسبة إلى النماذج التي تحتوي على أكواد قديمة لا يمكن إصلاحها على الفور، أضف اسم مجلد النموذج إلى قائمة `allowlist_models` في القاعدة ذات الصلة داخل [mlinter rules.toml](https://github.com/huggingface/transformers-mlinter/blob/main/mlinter/rules.toml).

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

