2626
2727log = setup_logger ()
2828
29+
30+ # Older remote model files sometimes store `_tied_weights_keys` as a plain list
31+ # like `["lm_head.weight"]`. transformers 5.x now expects `{target: source}`,
32+ # and otherwise later save/load helpers fail with `'list' object has no attribute 'keys'`.
33+ def _resolve_legacy_tied_weights_mapping (model : PreTrainedModel , tied_mapping ) -> dict [str , str ]:
34+ if not isinstance (tied_mapping , (list , tuple , set )):
35+ return {}
36+
37+ if not getattr (model .config , "tie_word_embeddings" , False ):
38+ return {}
39+
40+ input_embeddings = model .get_input_embeddings ()
41+ input_weight = getattr (input_embeddings , "weight" , None )
42+ if input_weight is None :
43+ return {}
44+
45+ source_name = None
46+ for name , param in model .named_parameters (remove_duplicate = False ):
47+ if param is input_weight :
48+ source_name = name
49+ break
50+
51+ if source_name is None :
52+ return {}
53+
54+ return {
55+ # Legacy list entries only name the tied target, so resolve them back
56+ # to the input embedding weight name expected by transformers 5.x.
57+ target_name : source_name
58+ for target_name in tied_mapping
59+ if isinstance (target_name , str ) and target_name != source_name
60+ }
61+
62+
63+ # Rewrite legacy list-based `_tied_weights_keys` in-place so transformers 5.x
64+ # save/load code stops crashing on older trust_remote_code models that still use
65+ # the pre-5.x list format.
66+ def _normalize_legacy_tied_weights_keys (model : PreTrainedModel ) -> None :
67+ for _name , submodule in model .named_modules (remove_duplicate = False ):
68+ tied_mapping = getattr (submodule , "_tied_weights_keys" , None )
69+ if not isinstance (tied_mapping , (list , tuple , set )):
70+ continue
71+
72+ if isinstance (submodule , PreTrainedModel ):
73+ submodule ._tied_weights_keys = _resolve_legacy_tied_weights_mapping (submodule , tied_mapping )
74+ else :
75+ submodule ._tied_weights_keys = {}
76+
77+
78+ # Bridge a few transformers 5.x API changes so older trust_remote_code model
79+ # files still import and initialize without editing the cached remote source.
80+ def _patch_transformers_remote_code_compat () -> None :
81+ try :
82+ from transformers .utils import import_utils
83+ except Exception :
84+ return
85+
86+ if not hasattr (import_utils , "is_torch_fx_available" ):
87+ # transformers 5.x removed `import_utils.is_torch_fx_available`, but
88+ # older remote model files still import it during module import.
89+ def is_torch_fx_available () -> bool :
90+ return hasattr (torch , "fx" )
91+
92+ import_utils .is_torch_fx_available = is_torch_fx_available
93+
94+ if not getattr (PreTrainedModel , "_gptqmodel_legacy_tied_weights_patch" , False ):
95+ original_get_expanded_tied_weights_keys = PreTrainedModel .get_expanded_tied_weights_keys
96+
97+ def get_expanded_tied_weights_keys (self , all_submodels : bool = False ) -> dict :
98+ # transformers 5.x expects `_tied_weights_keys` to be a dict, while
99+ # older trust_remote_code models still declare `["lm_head.weight"]`.
100+ # Handle the legacy form here so HF tied-weight expansion still works.
101+ tied_mapping = getattr (self , "_tied_weights_keys" , None )
102+ if not isinstance (tied_mapping , (list , tuple , set )):
103+ return original_get_expanded_tied_weights_keys (self , all_submodels = all_submodels )
104+
105+ if all_submodels :
106+ expanded_tied_weights = {}
107+ for prefix , submodule in self .named_modules (remove_duplicate = False ):
108+ if isinstance (submodule , PreTrainedModel ):
109+ submodel_tied_weights = submodule .get_expanded_tied_weights_keys (all_submodels = False )
110+ if prefix != "" :
111+ submodel_tied_weights = {
112+ f"{ prefix } .{ k } " : f"{ prefix } .{ v } " for k , v in submodel_tied_weights .items ()
113+ }
114+ expanded_tied_weights .update (submodel_tied_weights )
115+ return expanded_tied_weights
116+
117+ if not getattr (self .config , "tie_word_embeddings" , False ):
118+ return {}
119+
120+ return _resolve_legacy_tied_weights_mapping (self , tied_mapping )
121+
122+ PreTrainedModel .get_expanded_tied_weights_keys = get_expanded_tied_weights_keys
123+ PreTrainedModel ._gptqmodel_legacy_tied_weights_patch = True
124+
125+
126+ # Restore the pre-transformers-5 RoPE config shape expected by older MiniCPM
127+ # remote code before HF instantiates the architecture from config.
128+ def _normalize_remote_code_config_compat (config : Any ) -> None :
129+ # transformers 5.x normalizes RoPE config to `rope_type`, but older
130+ # MiniCPM remote code still reads `rope_scaling["type"]` or expects `None`.
131+ rope_scaling = getattr (config , "rope_scaling" , None )
132+ if not isinstance (rope_scaling , dict ) or "type" in rope_scaling :
133+ return
134+
135+ rope_type = rope_scaling .get ("rope_type" )
136+ factor = rope_scaling .get ("factor" )
137+
138+ if rope_type in (None , "default" ) and factor is None :
139+ config .rope_scaling = None
140+ return
141+
142+ config .rope_scaling = dict (rope_scaling )
143+ config .rope_scaling ["type" ] = rope_type
144+
29145def _sanitize_generation_config (cfg : GenerationConfig , * , drop_sampling_fields : bool = False ) -> bool :
30146 changed = False
31147 if cfg is None :
@@ -154,6 +270,10 @@ def build_shell_model(
154270 del init_kwargs ["_fast_init" ]
155271 # All nn.Parameters and buffers are created
156272
273+ if trust_remote_code :
274+ _patch_transformers_remote_code_compat ()
275+ _normalize_remote_code_config_compat (config )
276+
157277 # All nn.Parameters and buffers are created on 'meta' and initializers are skipped.
158278 pb = log .spinner (title = "Model loading..." , interval = 0.1 )
159279 try :
@@ -167,4 +287,7 @@ def build_shell_model(
167287 finally :
168288 pb .close ()
169289
290+ if trust_remote_code and isinstance (shell , PreTrainedModel ):
291+ _normalize_legacy_tied_weights_keys (shell )
292+
170293 return shell
0 commit comments