2424
2525from etils import epath
2626from flax import nnx
27+ from flax import struct
2728
2829
2930from flax .training import train_state
3637from maxtext .input_pipeline .multihost_dataloading import MultiHostDataLoadIterator
3738from maxtext .input_pipeline .multihost_dataloading import RemoteIteratorWrapper
3839from maxtext .input_pipeline .synthetic_data_processing import PlaceHolderDataIterator
40+ from maxtext .trainers .diloco .utils import spmd_diloco_checkpointing as diloco_checkpoint_utils
3941from maxtext .utils import elastic_utils
4042from maxtext .utils import exceptions
4143from maxtext .utils import gcs_utils
@@ -186,6 +188,16 @@ def _load_linen_checkpoint_into_nnx(
186188 present, else keep their fresh init value. A genuinely-missing weight raises.
187189 """
188190 max_logging .log (f"Restoring Linen-layout checkpoint into NNX state at { path } " )
191+ if config and getattr (config , "enable_diloco" , False ):
192+ return diloco_checkpoint_utils .restore_diloco_checkpoint (
193+ path ,
194+ abstract_nnx_state ,
195+ checkpoint_storage_concurrent_gb ,
196+ use_ocdbt = use_ocdbt ,
197+ use_zarr3 = use_zarr3 ,
198+ config = config ,
199+ )
200+
189201 linen_abstract = train_state_nnx .to_checkpoint_dict (abstract_nnx_state )
190202 if config and getattr (getattr (config , "lora" , None ), "enable_lora" , False ):
191203 linen_abstract = _filter_lora_trainable_state (linen_abstract )
@@ -536,9 +548,21 @@ def map_to_pspec(data):
536548 )
537549 ocp .type_handlers .register_type_handler (jax .Array , array_handler , override = True )
538550
539- restore_target = (
540- train_state_nnx .to_checkpoint_dict (abstract_unboxed_pre_state ) if is_nnx else abstract_unboxed_pre_state
541- )
551+ is_diloco = bool (maxtext_config and getattr (maxtext_config , "enable_diloco" , False ))
552+
553+ # Map the expected training state to the on-disk checkpoint dictionary layout:
554+ # - DiLoCo: DiLoCoTrainState (wrapping NNX or Linen inner state + outer params + opt state).
555+ # - Standard non-DiLoCo NNX: TrainStateNNX (converted to Linen collection layout for storage).
556+ # - Standard non-DiLoCo Linen: TrainState dataclass (used directly).
557+ if is_diloco :
558+ restore_target = diloco_checkpoint_utils .to_diloco_checkpoint_dict (
559+ abstract_unboxed_pre_state , config = maxtext_config
560+ )
561+ elif is_nnx :
562+ restore_target = train_state_nnx .to_checkpoint_dict (abstract_unboxed_pre_state )
563+ else :
564+ restore_target = abstract_unboxed_pre_state
565+
542566 if maxtext_config and getattr (getattr (maxtext_config , "lora" , None ), "enable_lora" , False ):
543567 restore_target = _filter_lora_trainable_state (restore_target )
544568 restore_args = jax .tree_util .tree_map (map_to_pspec , restore_target )
@@ -560,7 +584,11 @@ def map_to_pspec(data):
560584 ),
561585 ):
562586 restored = checkpoint_manager .restore (step , args = Composite (state = checkpoint_args )).state
563- if is_nnx :
587+ if is_diloco :
588+ restored = diloco_checkpoint_utils .from_diloco_checkpoint_dict (
589+ restored , abstract_unboxed_pre_state , config = maxtext_config
590+ )
591+ elif is_nnx :
564592 restored = _restored_linen_to_nnx (restored , abstract_unboxed_pre_state , config = maxtext_config )
565593 return (
566594 restored ,
@@ -585,15 +613,25 @@ def map_to_pspec(data):
585613 checkpoint_args ,
586614 expansion_factor_real_data ,
587615 )
588- if is_nnx :
616+ if is_diloco :
617+ restored_items = diloco_checkpoint_utils .from_diloco_checkpoint_dict (
618+ restored ["items" ], abstract_unboxed_pre_state , config = maxtext_config
619+ )
620+ restored = {"items" : restored_items }
621+ elif is_nnx :
589622 restored_items = _restored_linen_to_nnx (restored ["items" ], abstract_unboxed_pre_state , config = maxtext_config )
590623 restored = {"items" : restored_items }
591624 return (restored , iterator )
592625 # Case 3: Default/Fallback case.
593626 # This case acts as a wildcard ('_') and matches if none of the preceding cases were met.
594627 case _:
595628 restored = checkpoint_manager .restore (step , args = Composite (items = checkpoint_args ))
596- if is_nnx :
629+ if is_diloco :
630+ restored_items = diloco_checkpoint_utils .from_diloco_checkpoint_dict (
631+ restored ["items" ], abstract_unboxed_pre_state , config = maxtext_config
632+ )
633+ restored = {"items" : restored_items }
634+ elif is_nnx :
597635 restored_items = _restored_linen_to_nnx (restored ["items" ], abstract_unboxed_pre_state , config = maxtext_config )
598636 restored = {"items" : restored_items }
599637 return (restored , None )
@@ -848,7 +886,9 @@ def maybe_save_checkpoint(checkpoint_manager, state, config, data_iterator, step
848886 _handle_post_checkpoint_preemption (checkpoint_manager , actual_step , force_ckpt_save )
849887 return
850888
851- if latest_step (checkpoint_manager ) == actual_step :
889+ # Skip if step directory already exists (e.g. step 0 or prior checkpoints in all_steps())
890+ # to prevent Orbax OCDBT UUID collisions during auto-resume / continuation runs for DiLoCo.
891+ if latest_step (checkpoint_manager ) == actual_step or actual_step in checkpoint_manager .all_steps ():
852892 max_logging .log (f"Checkpoint for step { actual_step } already exists, skipping save." )
853893 return
854894
@@ -903,18 +943,18 @@ def _filter_dict(val, path=()):
903943
904944def save_checkpoint (checkpoint_manager , step , state , config = None , data_iterator = None , force = False ):
905945 """Wrapper for saving checkpoint."""
906- if not isinstance (state , (dict , nnx .State , train_state .TrainState )):
946+ # Allow struct.PyTreeNode so Flax dataclass states (e.g. DiLoCoTrainState) aren't cleared to empty dicts ({})
947+ if not isinstance (state , (dict , nnx .State , train_state .TrainState , struct .PyTreeNode )):
907948 if isinstance (state , train_state_nnx .TrainStateNNX ):
908949 state = nnx .state (state )
909950 elif not isinstance (state , (dict , nnx .State )):
910951 state = {}
911952
912- if config and getattr (config , "pure_nnx" , False ) and isinstance (state , nnx .State ):
953+ if config and getattr (config , "enable_diloco" , False ):
954+ state = diloco_checkpoint_utils .to_diloco_checkpoint_dict (state , config )
955+ elif config and getattr (config , "pure_nnx" , False ):
913956 # Save in the Linen on-disk layout so pure_nnx and Linen checkpoints are interchangeable.
914- if getattr (config , "enable_diloco" , False ):
915- step_value = state .step .get_value () if hasattr (state .step , "get_value" ) else state .step
916- state = train_state_nnx .to_linen_checkpoint_dict ({"model" : state .params , "optimizer" : {"step" : step_value }})
917- else :
957+ if isinstance (state , nnx .State ):
918958 state = train_state_nnx .to_checkpoint_dict (state )
919959
920960 if config and getattr (config , "enable_checkpointing" , False ):
0 commit comments