@@ -1064,6 +1064,10 @@ class HardwareAndMesh(BaseModel):
10641064 CustomRule .DEFAULT ,
10651065 description = "Customized mesh and logical rules for granularity." ,
10661066 )
1067+ custom_mesh_and_rule_for_eval : CustomRule = Field (
1068+ CustomRule .DEFAULT ,
1069+ description = "Customized mesh and logical rules for evaluation." ,
1070+ )
10671071 allow_split_physical_axes : bool = Field (False , description = "Allow splitting physical axes for device mesh creation." )
10681072 enable_nnx : bool = Field (True , description = "Whether to use NNX for model definition." )
10691073 optimize_mesh_for_tpu_v6e : bool = Field (False , description = "Apply transformations to the mesh for TPU v6e." )
@@ -1080,6 +1084,9 @@ class LayoutAndSharding(BaseModel):
10801084 """Configuration for data and model sharding rules."""
10811085
10821086 logical_axis_rules : Any = Field ([], description = "Rules for mapping logical axes to physical mesh axes." )
1087+ logical_axis_rules_for_eval : Any = Field (
1088+ [], description = "Rules for mapping logical axes to physical mesh axes during evaluation."
1089+ )
10831090 data_sharding : Any = Field ([], description = "Sharding for input data." )
10841091 context_sharding : str = Field ("context" , description = "Physical axis name for context parallelism." )
10851092 input_data_sharding_logical_axes : list [str ] = Field (
@@ -2697,31 +2704,46 @@ def validate_num_moe_emb_chunks(self):
26972704 f"Got use_gmm_v2={ self .use_gmm_v2 } , use_ring_of_experts={ self .use_ring_of_experts } ."
26982705 )
26992706
2707+ @staticmethod
2708+ def _load_mesh_config_from_yaml (rule_value : str ) -> dict :
2709+ """Helper to load and parse custom mesh YAML configurations."""
2710+ custom_mesh_path = os .path .join (
2711+ os .path .dirname (os .path .abspath (__file__ )),
2712+ "custom_mesh_and_rule" ,
2713+ f"{ rule_value } .yml" ,
2714+ )
2715+
2716+ if not os .path .exists (custom_mesh_path ):
2717+ # ValueError is more semantically correct for validation errors than NotImplementedError
2718+ raise ValueError (f"Custom mesh config file not found at { custom_mesh_path } " )
2719+
2720+ # Explicitly setting encoding removes the need for the pylint disable comment
2721+ with open (custom_mesh_path , "r" , encoding = "utf-8" ) as f :
2722+ return yaml .safe_load (f ) or {}
2723+
27002724 @model_validator (mode = "after" )
27012725 def set_derived_and_validate_values (self ) -> "MaxTextConfig" :
27022726 """
27032727 Computes all derived values and runs all cross-field validations after initial parsing.
27042728 This logic is ported from the legacy pyconfig_deprecated.py system and adapted for Pydantic.
27052729 """
2730+ # Handle primary custom mesh and rule
27062731 if self .custom_mesh_and_rule is not CustomRule .DEFAULT :
2707- custom_mesh_path = os .path .join (
2708- os .path .dirname (os .path .abspath (__file__ )),
2709- "custom_mesh_and_rule" ,
2710- f"{ self .custom_mesh_and_rule .value } .yml" ,
2711- )
2712- if os .path .exists (custom_mesh_path ):
2713- with open (custom_mesh_path , "r" ) as f : # pylint: disable=unspecified-encoding
2714- custom_mesh_config = yaml .safe_load (f )
2715- if "mesh_axes" in custom_mesh_config :
2716- self .mesh_axes = custom_mesh_config ["mesh_axes" ]
2717- if "logical_axis_rules" in custom_mesh_config :
2718- self .logical_axis_rules = custom_mesh_config ["logical_axis_rules" ]
2719- if "data_sharding" in custom_mesh_config :
2720- self .data_sharding = custom_mesh_config ["data_sharding" ]
2721- if "context_sharding" in custom_mesh_config :
2722- self .context_sharding = custom_mesh_config ["context_sharding" ]
2723- else :
2724- raise NotImplementedError (f"Custom mesh config file not found at { custom_mesh_path } " )
2732+ mesh_config = self ._load_mesh_config_from_yaml (self .custom_mesh_and_rule .value )
2733+
2734+ # Use setattr to dynamically apply attributes, keeping code compact
2735+ for field in ("mesh_axes" , "logical_axis_rules" , "data_sharding" , "context_sharding" ):
2736+ if field in mesh_config :
2737+ setattr (self , field , mesh_config [field ])
2738+
2739+ # Handle eval custom mesh and rule
2740+ if self .custom_mesh_and_rule_for_eval is CustomRule .DEFAULT :
2741+ # Fallback to primary rule if eval is DEFAULT
2742+ self .custom_mesh_and_rule_for_eval = self .custom_mesh_and_rule
2743+ self .logical_axis_rules_for_eval = self .logical_axis_rules
2744+ else :
2745+ eval_config = self ._load_mesh_config_from_yaml (self .custom_mesh_and_rule_for_eval .value )
2746+ self .logical_axis_rules_for_eval = eval_config .get ("logical_axis_rules" , self .logical_axis_rules )
27252747
27262748 # A. SET RUN NAME AND PATHS
27272749 # If run_name is not set, generate one from the JOBSET_NAME environment variable (if available)
0 commit comments