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
@@ -508,7 +507,9 @@ def compute_loss(
508507 log_t_p_T_sparse = jax .nn .log_softmax (t_logits / temperature , axis = - 1 )
509508
510509 # 2. Gather Student unnormalized logits at the Teacher's exact Top-K indices
511- s_logits_sparse = jnp .take_along_axis (s_logits , teacher_output .top_k_indices , axis = - 1 ) # pyrefly: ignore[bad-argument-type]
510+ s_logits_sparse = jnp .take_along_axis (
511+ s_logits , teacher_output .top_k_indices , axis = - 1
512+ ) # pyrefly: ignore[bad-argument-type]
512513
513514 # 3. Normalize Student probabilities only over the exact same Top-K subset
514515 log_s_T_sparse = jax .nn .log_softmax (s_logits_sparse / temperature , axis = - 1 )
@@ -558,7 +559,9 @@ def compute_loss(
558559 s_features_sliced = s_features_sliced .astype (jnp .float32 )
559560 t_features_sliced = t_features_sliced .astype (jnp .float32 )
560561
561- feature_loss = beta_feature * self .feature_loss_fn (s_features_sliced , t_features_sliced , mask ) # pyrefly: ignore[not-callable]
562+ feature_loss = beta_feature * self .feature_loss_fn (
563+ s_features_sliced , t_features_sliced , mask
564+ ) # pyrefly: ignore[not-callable]
562565
563566 total_loss = base_logit_loss + feature_loss
564567
@@ -647,8 +650,9 @@ def create_labels(self, targets, targets_segmentation=None, **kwargs):
647650class MaxTextCheckpointManager (tunix_checkpoint_manager .CheckpointManager ):
648651 """Custom CheckpointManager that uses MaxText's native handlers.
649652
650- This manager extends Tunix to support saving/restoring the MaxText input pipeline
651- (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``.
652656 """
653657
654658 def __init__ (
@@ -662,32 +666,6 @@ def __init__(
662666 self .student_config = student_config
663667 self ._iterator = raw_iterator
664668
665- # Re-initialize internal Orbax manager with MaxText's Grain handler
666- # pylint: disable=access-member-before-definition
667- # pytype: disable=attribute-error
668- if self ._checkpoint_manager is not None :
669- root_directory = self ._checkpoint_manager .directory
670-
671- if options is None :
672- options = getattr (self ._checkpoint_manager , "options" , None )
673-
674- item_handlers = {
675- "model_params" : checkpoint .PyTreeCheckpointHandler (),
676- "optimizer_state" : checkpoint .PyTreeCheckpointHandler (),
677- "custom_metadata" : checkpoint .JsonCheckpointHandler (),
678- # Use MaxText's handler for the iterator
679- "iter" : GrainCheckpointHandler (),
680- }
681-
682- self ._checkpoint_manager .close ()
683- self ._checkpoint_manager = checkpoint .CheckpointManager (
684- root_directory ,
685- item_handlers = item_handlers ,
686- options = options ,
687- )
688- # pytype: enable=attribute-error
689- # pylint: enable=access-member-before-definition
690-
691669 def save (
692670 self ,
693671 step ,
@@ -697,10 +675,8 @@ def save(
697675 force = False ,
698676 custom_metadata = None ,
699677 ):
700- """Saves the checkpoint including the input pipeline state (if available)."""
701- if self ._checkpoint_manager is None :
702- return False
703- 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 :
704680 return False
705681
706682 # Standard Tunix Logic for Model/Optimizer.
@@ -711,21 +687,12 @@ def save(
711687 else :
712688 params = nnx .state (target_model )
713689
714- # Define standard SaveArgs once to reuse
715- default_save_args = checkpoint .SaveArgs ()
716- cp_save_args = {
717- "model_params" : checkpoint .args .PyTreeSave (
718- item = params , save_args = jax .tree .map (lambda _ : default_save_args , params )
719- ),
720- }
721- # 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.
722692 exclude_opt = self .student_config .learn_to_init_mode
723693
724694 if optimizer is not None and not exclude_opt :
725- optimizer_state = nnx .state (optimizer , nnx .optimizer .OptState )
726- cp_save_args ["optimizer_state" ] = checkpoint .args .PyTreeSave (
727- item = optimizer_state , save_args = jax .tree .map (lambda _ : default_save_args , optimizer_state )
728- )
695+ checkpointables ["optimizer_state" ] = nnx .state (optimizer , nnx .optimizer .OptState )
729696
730697 if self ._iterator is not None :
731698 # Follow MaxText's logic to handle multi-process saving
@@ -743,15 +710,11 @@ def save(
743710 local_iter = data_iter .local_iterator if hasattr (data_iter , "local_iterator" ) else data_iter
744711 grain_iters_to_save .append ((local_iter , process_index , process_count_total ))
745712
746- # Use GrainCheckpointSave wrapper
747- 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+ )
748716
749- return self ._checkpoint_manager .save (
750- step ,
751- args = checkpoint .args .Composite (** cp_save_args ),
752- custom_metadata = custom_metadata or {},
753- force = force ,
754- )
717+ return self ._save_checkpointables (step , checkpointables , force , custom_metadata )
755718
756719 def maybe_restore ( # pyrefly: ignore[bad-override]
757720 self ,
@@ -766,12 +729,12 @@ def maybe_restore( # pyrefly: ignore[bad-override]
766729 Returns:
767730 (restored step, custom_metadata dict). Step is 0 if no checkpoint exists.
768731 """
769- if self ._checkpoint_manager is None :
732+ if self ._checkpointer is None :
770733 return 0 , {}
771734
772735 target_model = getattr (model , "student_model" , model )
773736
774- step , _ = super ().maybe_restore (
737+ step , custom_metadata = super ().maybe_restore (
775738 model = target_model , # pyrefly: ignore[bad-argument-type]
776739 optimizer = optimizer ,
777740 restore_only_lora_params = restore_only_lora_params ,
@@ -781,20 +744,14 @@ def maybe_restore( # pyrefly: ignore[bad-override]
781744
782745 max_logging .log (f"Restored from checkpoint step { step } ." )
783746
784- metadata = self ._checkpoint_manager .metadata (step )
785- if metadata and hasattr (metadata , "custom_metadata" ) and metadata .custom_metadata is not None :
786- custom_metadata = metadata .custom_metadata
787- else :
788- custom_metadata = {}
789-
790- return step , dict (custom_metadata )
747+ return step , dict (custom_metadata or {})
791748
792749 def restore_iterator (self ):
793750 """Restores the iterator using MaxText's logic."""
794- if self ._checkpoint_manager is None or self ._iterator is None :
751+ if self ._checkpointer is None or self ._iterator is None :
795752 return None
796753
797- step = self ._checkpoint_manager . latest_step ()
754+ step = self .latest_step ()
798755 if step is None :
799756 return None
800757
@@ -804,9 +761,10 @@ def restore_iterator(self):
804761 data_iter = self ._iterator
805762 local_iter = data_iter .local_iterator if hasattr (data_iter , "local_iterator" ) else data_iter
806763
807- restore_args = GrainCheckpointRestore (item = local_iter )
808-
809- 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+ )
810768 # Since Grain restores in-place via set_state(), we return the original object
811769 return self ._iterator
812770
@@ -816,5 +774,5 @@ def restore_iterator(self):
816774
817775 def wait_until_finished (self ):
818776 """Blocks until all outstanding checkpoint operations are complete."""
819- if self ._checkpoint_manager is not None :
820- self ._checkpoint_manager . wait_until_finished ()
777+ if self ._checkpointer is not None :
778+ self ._checkpointer . wait ()
0 commit comments