@@ -1897,17 +1897,17 @@ def build_conversion_tasks(
18971897 hf_pretrained : HFPreTrained ,
18981898 megatron_model : List [MegatronModel ],
18991899 weight_dtype : Optional [torch .dtype ] = None ,
1900- ) -> List [None | WeightConversionTask ]:
1900+ ) -> List [WeightConversionTask ]:
19011901 """Construct the conversion tasks between HF and megatron.
19021902
19031903 Args:
19041904 weight_dtype: Export dtype recorded on each task. Overrides must forward it.
19051905
19061906 The algorithm walks over every parameter of every destination model,
19071907 asks the :class:`MegatronMappingRegistry` whether it has a mapping for that
1908- parameter, and – if the corresponding HF weights actually exist – yields
1909- an :class:`_HFLoadTask` describing exactly how that parameter will be
1910- populated .
1908+ parameter and returns a concrete task describing exactly how that
1909+ parameter will be populated. Missing mappings or source weights are
1910+ conversion errors, not empty task slots .
19111911 """
19121912
19131913 has_hf_state = hasattr (hf_pretrained , "state" ) and hasattr (hf_pretrained .state , "source" )
@@ -1934,9 +1934,15 @@ def build_conversion_tasks(
19341934 name for name in sorted_global_param_names_all_pp_ranks if "output_layer" not in name
19351935 ]
19361936
1937+ mappings_by_global_name = self ._validate_conversion_mappings (
1938+ mapping_registry ,
1939+ sorted_global_param_names_all_pp_ranks ,
1940+ hf_keys ,
1941+ )
1942+
19371943 global_names_index_dict = {name : idx for idx , name in enumerate (sorted_global_param_names_all_pp_ranks )}
19381944
1939- tasks = [None ] * len (sorted_global_param_names_all_pp_ranks )
1945+ pending_tasks : list [ WeightConversionTask | None ] = [None ] * len (sorted_global_param_names_all_pp_ranks )
19401946 for vp_stage , model in enumerate (megatron_model ):
19411947 # persistent buffers are part of the model's state_dict, but not the named_parameters, so we must include them here separately
19421948 for local_name , _ in itertools .chain (model .named_parameters (), persistent_buffers (model )):
@@ -1950,34 +1956,15 @@ def build_conversion_tasks(
19501956 print_rank_0 (f"WARNING: { global_name } not in global_names_index_dict" )
19511957 continue
19521958 global_name_idx = global_names_index_dict [global_name ]
1953- mapping = mapping_registry .megatron_to_hf_lookup (self ._get_lora_unwrapped_name (global_name ))
1954-
1955- if not mapping :
1956- logger .warning (f"WARNING: No mapping found for megatron_param: { global_name } " )
1957- continue
1958- # Ensure hf weights exist (skip for config-only export where hf_keys is None)
1959- if hf_keys is not None and not mapping .allow_hf_name_mismatch :
1960- if isinstance (mapping .hf_param , str ):
1961- if mapping .hf_param not in hf_keys :
1962- logger .warning (f"WARNING: Can't find { mapping .hf_param } in hf_keys" )
1963- continue
1964- else :
1965- missing_params = [
1966- hf_param for hf_param in mapping .hf_param .values () if hf_param not in hf_keys
1967- ]
1968- if missing_params :
1969- logger .warning (
1970- f"WARNING: Can't find the following HF parameters in hf_keys: { missing_params } "
1971- )
1972- continue
1959+ mapping = mappings_by_global_name [global_name ]
19731960
19741961 local_module , local_weights = get_module_and_param_from_name (megatron_model , local_name , vp_stage )
19751962 if local_module is not None and not hasattr (local_module , "config" ):
19761963 # If module is not a MegatronModule (e.g. torch.nn.Conv1d or a module list) we need
19771964 # to get the config from the model
19781965 setattr (local_module , "config" , model_config )
19791966
1980- tasks [global_name_idx ] = WeightConversionTask (
1967+ pending_tasks [global_name_idx ] = WeightConversionTask (
19811968 pp_rank = pp_rank ,
19821969 vp_stage = vp_stage ,
19831970 param_name = local_name ,
@@ -1990,15 +1977,12 @@ def build_conversion_tasks(
19901977
19911978 # Fill the remaining ones for pp communications
19921979 for idx , global_name in enumerate (sorted_global_param_names_all_pp_ranks ):
1993- if tasks [idx ] is None :
1994- mapping = mapping_registry .megatron_to_hf_lookup (self ._get_lora_unwrapped_name (global_name ))
1995- # Skip tasks with no mapping found
1996- if mapping is None :
1997- continue
1980+ if pending_tasks [idx ] is None :
1981+ mapping = mappings_by_global_name [global_name ]
19981982 # This is an exception here we pass in global name
19991983 # we are not using global_name to extract module and weights
20001984 # only use it for param mapping auto dispatch checks
2001- tasks [idx ] = WeightConversionTask (
1985+ pending_tasks [idx ] = WeightConversionTask (
20021986 pp_rank = pp_rank ,
20031987 vp_stage = None ,
20041988 param_name = global_name ,
@@ -2009,6 +1993,68 @@ def build_conversion_tasks(
20091993 weight_dtype = weight_dtype ,
20101994 )
20111995
1996+ return self ._require_concrete_tasks (pending_tasks )
1997+
1998+ def _validate_conversion_mappings (
1999+ self ,
2000+ mapping_registry : MegatronMappingRegistry ,
2001+ global_param_names : Iterable [str ],
2002+ hf_keys : Iterable [str ] | None = None ,
2003+ ) -> dict [str , MegatronParamMapping ]:
2004+ """Resolve and validate mappings for the full cross-PP parameter list."""
2005+ mappings_by_global_name : dict [str , MegatronParamMapping ] = {}
2006+ missing_mappings : list [str ] = []
2007+ missing_hf_weights : list [tuple [str , str ]] = []
2008+ hf_key_set = set (hf_keys ) if hf_keys is not None else None
2009+
2010+ for global_name in global_param_names :
2011+ mapping = mapping_registry .megatron_to_hf_lookup (self ._get_lora_unwrapped_name (global_name ))
2012+ if mapping is None :
2013+ missing_mappings .append (global_name )
2014+ continue
2015+
2016+ mappings_by_global_name [global_name ] = mapping
2017+ if hf_key_set is None or mapping .allow_hf_name_mismatch :
2018+ continue
2019+
2020+ expected_hf_names = (
2021+ [mapping .hf_param ] if isinstance (mapping .hf_param , str ) else list (mapping .hf_param .values ())
2022+ )
2023+ missing_hf_weights .extend (
2024+ (global_name , hf_name ) for hf_name in expected_hf_names if hf_name not in hf_key_set
2025+ )
2026+
2027+ if missing_mappings :
2028+ missing_names = "\n " .join (missing_mappings )
2029+ raise ValueError (
2030+ "No mapping found for the following Megatron parameter(s):\n "
2031+ f" { missing_names } \n "
2032+ "Every global Megatron parameter must have a concrete mapping so import and export remain strict."
2033+ )
2034+
2035+ if missing_hf_weights :
2036+ missing_names = "\n " .join (f"{ global_name } -> { hf_name } " for global_name , hf_name in missing_hf_weights )
2037+ raise ValueError (
2038+ "Hugging Face checkpoint is missing mapped parameter(s):\n "
2039+ f" { missing_names } \n "
2040+ "If the HF config determines whether the weight exists, register the mapping "
2041+ "conditionally on that config instead. If it does not, and the name is synthesized "
2042+ "or the weight is absent on only some layers, set allow_hf_name_mismatch on the "
2043+ "mapping."
2044+ )
2045+
2046+ return mappings_by_global_name
2047+
2048+ @staticmethod
2049+ def _require_concrete_tasks (
2050+ pending_tasks : Iterable [WeightConversionTask | None ],
2051+ ) -> list [WeightConversionTask ]:
2052+ """Return tasks after enforcing the internal no-empty-slot invariant."""
2053+ tasks : list [WeightConversionTask ] = []
2054+ for task in pending_tasks :
2055+ if task is None :
2056+ raise RuntimeError ("Internal error: conversion task construction left an empty slot" )
2057+ tasks .append (task )
20122058 return tasks
20132059
20142060 def _detect_fp8_params (
@@ -2103,7 +2149,7 @@ def build_export_fp8_tasks(
21032149 * ,
21042150 scale_inv_suffix : str = "_scale_inv" ,
21052151 fp8_scale_inv_attr : str = "_rowwise_scale_inv" ,
2106- ) -> List [None | WeightConversionTask ]:
2152+ ) -> List [WeightConversionTask ]:
21072153 """
21082154 Build Megatron→(export) conversion tasks, inserting extra *scale_inv* tasks for blockwise FP8 params.
21092155 """
@@ -2128,6 +2174,11 @@ def build_export_fp8_tasks(
21282174 name for name in sorted_global_param_names_all_pp_ranks if "output_layer" not in name
21292175 ]
21302176
2177+ mappings_by_global_name = self ._validate_conversion_mappings (
2178+ mapping_registry ,
2179+ sorted_global_param_names_all_pp_ranks ,
2180+ )
2181+
21312182 # 1) Determine which global params are blockwise FP8 and gather flags across PP ranks
21322183 global_fp8_flags = self ._detect_fp8_params (
21332184 megatron_model ,
@@ -2160,10 +2211,7 @@ def build_export_fp8_tasks(
21602211 if global_name not in global_names_index_dict :
21612212 continue
21622213
2163- mapping = mapping_registry .megatron_to_hf_lookup (self ._get_lora_unwrapped_name (global_name ))
2164- if not mapping :
2165- logger .warning (f"WARNING: No mapping found for megatron_param: { global_name } " )
2166- continue
2214+ mapping = mappings_by_global_name [global_name ]
21672215 local_module , local_weights = get_module_and_param_from_name (megatron_model , local_name , vp_stage )
21682216 if local_module is not None and not hasattr (local_module , "config" ):
21692217 setattr (local_module , "config" , model_config )
@@ -2221,22 +2269,16 @@ def build_export_fp8_tasks(
22212269 # For scale_inv entries, reuse the base param's mapping type.
22222270 if global_name .endswith (scale_inv_suffix ):
22232271 base_global_name = global_name [: - len (scale_inv_suffix )]
2224- base_mapping = mapping_registry .megatron_to_hf_lookup (self ._get_lora_unwrapped_name (base_global_name ))
2225- if base_mapping is not None :
2226- # clone mapping instance to avoid sharing state across tasks.
2227- base_mapping_for_scale = mapping_registry .resolve_mapping (base_mapping , ())
2228- mapping = _HFNameSuffixMapping (
2229- base_mapping_for_scale ,
2230- scale_inv_suffix ,
2231- self ._fp8_scale_block_size (global_fp8_flags .get (base_global_name )),
2232- )
2233- else :
2234- mapping = None
2272+ base_mapping = mappings_by_global_name [base_global_name ]
2273+ # clone mapping instance to avoid sharing state across tasks.
2274+ base_mapping_for_scale = mapping_registry .resolve_mapping (base_mapping , ())
2275+ mapping = _HFNameSuffixMapping (
2276+ base_mapping_for_scale ,
2277+ scale_inv_suffix ,
2278+ self ._fp8_scale_block_size (global_fp8_flags .get (base_global_name )),
2279+ )
22352280 else :
2236- mapping = mapping_registry .megatron_to_hf_lookup (self ._get_lora_unwrapped_name (global_name ))
2237- if mapping is None :
2238- logger .warning (f"No mapping found for global_name: { global_name } " )
2239- continue
2281+ mapping = mappings_by_global_name [global_name ]
22402282
22412283 tasks [idx ] = WeightConversionTask (
22422284 pp_rank = pp_rank ,
@@ -2248,7 +2290,7 @@ def build_export_fp8_tasks(
22482290 mapping = mapping ,
22492291 )
22502292
2251- return tasks
2293+ return self . _require_concrete_tasks ( tasks )
22522294
22532295 @staticmethod
22542296 def _fp8_scale_block_size (fp8_flag : bool | int | None ) -> int | None :
0 commit comments