3131
3232from maxtext .utils import max_logging
3333from maxtext .utils import maxtext_utils
34- # Reuse MaxText's native checkpointing logic.
35- from maxtext .common .checkpointing import GrainCheckpointHandler , GrainCheckpointSave , GrainCheckpointRestore
34+ from maxtext .common import checkpointing
3635from tunix .sft import checkpoint_manager as tunix_checkpoint_manager
3736from tunix .sft import peft_trainer
3837
@@ -651,8 +650,9 @@ def create_labels(self, targets, targets_segmentation=None, **kwargs):
651650class MaxTextCheckpointManager (tunix_checkpoint_manager .CheckpointManager ):
652651 """Custom CheckpointManager that uses MaxText's native handlers.
653652
654- This manager extends Tunix to support saving/restoring the MaxText input pipeline
655- (Grain) alongside the model and optimizer.
653+ Model and optimizer are delegated to Tunix's v1 ``Checkpointer`` unchanged.
654+ The Grain input pipeline is added as an extra ``"iter"`` checkpointable via
655+ ``GrainCheckpointable``, which wraps MaxText's ``GrainCheckpointHandler``.
656656 """
657657
658658 def __init__ (
@@ -666,32 +666,6 @@ def __init__(
666666 self .student_config = student_config
667667 self ._iterator = raw_iterator
668668
669- # Re-initialize internal Orbax manager with MaxText's Grain handler
670- # pylint: disable=access-member-before-definition
671- # pytype: disable=attribute-error
672- if self ._checkpoint_manager is not None :
673- root_directory = self ._checkpoint_manager .directory
674-
675- if options is None :
676- options = getattr (self ._checkpoint_manager , "options" , None )
677-
678- item_handlers = {
679- "model_params" : checkpoint .PyTreeCheckpointHandler (),
680- "optimizer_state" : checkpoint .PyTreeCheckpointHandler (),
681- "custom_metadata" : checkpoint .JsonCheckpointHandler (),
682- # Use MaxText's handler for the iterator
683- "iter" : GrainCheckpointHandler (),
684- }
685-
686- self ._checkpoint_manager .close ()
687- self ._checkpoint_manager = checkpoint .CheckpointManager (
688- root_directory ,
689- item_handlers = item_handlers ,
690- options = options ,
691- )
692- # pytype: enable=attribute-error
693- # pylint: enable=access-member-before-definition
694-
695669 def save (
696670 self ,
697671 step ,
@@ -701,10 +675,8 @@ def save(
701675 force = False ,
702676 custom_metadata = None ,
703677 ):
704- """Saves the checkpoint including the input pipeline state (if available)."""
705- if self ._checkpoint_manager is None :
706- return False
707- if not force and not self ._checkpoint_manager .should_save (step ):
678+ """Saves model, optimizer and the Grain input pipeline state."""
679+ if self ._checkpointer is None :
708680 return False
709681
710682 # Standard Tunix Logic for Model/Optimizer.
@@ -715,21 +687,12 @@ def save(
715687 else :
716688 params = nnx .state (target_model )
717689
718- # Define standard SaveArgs once to reuse
719- default_save_args = checkpoint .SaveArgs ()
720- cp_save_args = {
721- "model_params" : checkpoint .args .PyTreeSave (
722- item = params , save_args = jax .tree .map (lambda _ : default_save_args , params )
723- ),
724- }
725- # Exclude optimizer state if the flag is set OR if learn_to_init_mode is active.
690+ checkpointables : dict [str , Any ] = {"model_params" : params }
691+ # Exclude optimizer state when learn_to_init_mode is active.
726692 exclude_opt = self .student_config .learn_to_init_mode
727693
728694 if optimizer is not None and not exclude_opt :
729- optimizer_state = nnx .state (optimizer , nnx .optimizer .OptState )
730- cp_save_args ["optimizer_state" ] = checkpoint .args .PyTreeSave (
731- item = optimizer_state , save_args = jax .tree .map (lambda _ : default_save_args , optimizer_state )
732- )
695+ checkpointables ["optimizer_state" ] = nnx .state (optimizer , nnx .optimizer .OptState )
733696
734697 if self ._iterator is not None :
735698 # Follow MaxText's logic to handle multi-process saving
@@ -747,15 +710,11 @@ def save(
747710 local_iter = data_iter .local_iterator if hasattr (data_iter , "local_iterator" ) else data_iter
748711 grain_iters_to_save .append ((local_iter , process_index , process_count_total ))
749712
750- # Use GrainCheckpointSave wrapper
751- cp_save_args ["iter" ] = GrainCheckpointSave (item = grain_iters_to_save ) # pyrefly: ignore[bad-assignment]
713+ checkpointables ["iter" ] = checkpointing .GrainCheckpointable (
714+ save_args = checkpointing .GrainCheckpointSave (item = grain_iters_to_save ) # pyrefly: ignore[bad-assignment]
715+ )
752716
753- return self ._checkpoint_manager .save (
754- step ,
755- args = checkpoint .args .Composite (** cp_save_args ),
756- custom_metadata = custom_metadata or {},
757- force = force ,
758- )
717+ return self ._save_checkpointables (step , checkpointables , force , custom_metadata )
759718
760719 def maybe_restore ( # pyrefly: ignore[bad-override]
761720 self ,
@@ -770,12 +729,12 @@ def maybe_restore( # pyrefly: ignore[bad-override]
770729 Returns:
771730 (restored step, custom_metadata dict). Step is 0 if no checkpoint exists.
772731 """
773- if self ._checkpoint_manager is None :
732+ if self ._checkpointer is None :
774733 return 0 , {}
775734
776735 target_model = getattr (model , "student_model" , model )
777736
778- step , _ = super ().maybe_restore (
737+ step , custom_metadata = super ().maybe_restore (
779738 model = target_model , # pyrefly: ignore[bad-argument-type]
780739 optimizer = optimizer ,
781740 restore_only_lora_params = restore_only_lora_params ,
@@ -785,20 +744,14 @@ def maybe_restore( # pyrefly: ignore[bad-override]
785744
786745 max_logging .log (f"Restored from checkpoint step { step } ." )
787746
788- metadata = self ._checkpoint_manager .metadata (step )
789- if metadata and hasattr (metadata , "custom_metadata" ) and metadata .custom_metadata is not None :
790- custom_metadata = metadata .custom_metadata
791- else :
792- custom_metadata = {}
793-
794- return step , dict (custom_metadata )
747+ return step , dict (custom_metadata or {})
795748
796749 def restore_iterator (self ):
797750 """Restores the iterator using MaxText's logic."""
798- if self ._checkpoint_manager is None or self ._iterator is None :
751+ if self ._checkpointer is None or self ._iterator is None :
799752 return None
800753
801- step = self ._checkpoint_manager . latest_step ()
754+ step = self .latest_step ()
802755 if step is None :
803756 return None
804757
@@ -808,9 +761,10 @@ def restore_iterator(self):
808761 data_iter = self ._iterator
809762 local_iter = data_iter .local_iterator if hasattr (data_iter , "local_iterator" ) else data_iter
810763
811- restore_args = GrainCheckpointRestore (item = local_iter )
812-
813- self ._checkpoint_manager .restore (step , args = checkpoint .args .Composite (iter = restore_args ))
764+ self ._checkpointer .load_checkpointables (
765+ step ,
766+ {"iter" : checkpointing .GrainCheckpointable (restore_args = checkpointing .GrainCheckpointRestore (item = local_iter ))},
767+ )
814768 # Since Grain restores in-place via set_state(), we return the original object
815769 return self ._iterator
816770
@@ -820,5 +774,5 @@ def restore_iterator(self):
820774
821775 def wait_until_finished (self ):
822776 """Blocks until all outstanding checkpoint operations are complete."""
823- if self ._checkpoint_manager is not None :
824- self ._checkpoint_manager . wait_until_finished ()
777+ if self ._checkpointer is not None :
778+ self ._checkpointer . wait ()
0 commit comments