@@ -192,7 +192,7 @@ def _nnx_init_cache_dict(self, mode: str = MODEL_MODE_PREFILL) -> dict:
192192 """Zero-filled pure-dict cache matching the abstract NNX model."""
193193 src = self ._abstract_model_for_mode (mode )
194194 _ , cache_state , _ = nnx .split (src , nnx .Cache , ...)
195- cache_dict = cache_state .to_pure_dict () # pyrefly: ignore[missing-attribute]
195+ cache_dict = nnx .to_pure_dict (cache_state )
196196 return jax .tree .map (lambda x : jnp .zeros (x .shape , x .dtype ), cache_dict )
197197
198198 def _nnx_run_model (
@@ -239,7 +239,7 @@ def _nnx_run_model(
239239 true_length = true_length ,
240240 slot = slot ,
241241 )
242- new_cache = nnx .state (model , nnx .Cache ). to_pure_dict ( )
242+ new_cache = nnx .to_pure_dict ( nnx . state (model , nnx .Cache ))
243243 return logits , new_cache
244244
245245 def generate_aot (
@@ -398,22 +398,13 @@ def _load_params_nnx(self, params, rng):
398398 forward. Same output as serve mode (absmax calibration), slower.
399399 """
400400
401- if params :
402- print ("Resharding given NNX params" )
403- _ , params_abs , _ = nnx .split (self .model , nnx .Param , ...)
404- # self.model is abstract (built via nnx.eval_shape), so its leaves carry logical
405- # axis metadata but no physical .sharding. Resolve logical to physical here so
406- # device_put actually reshards instead of being a no-op.
407- with nn_partitioning .axis_rules (self .config .logical_axis_rules ):
408- target_shardings = sharding .nnx_construct_named_sharding (params_abs , self ._mesh )
409- params_state = jax .device_put (params , target_shardings )
410- # We only need a concrete `rest` (RNG vars) for nnx.merge. create_nnx_sharded_model
411- # builds the model with a jitted out_shardings so params are produced already
412- # sharded, avoiding a single-device allocation of the full model (an OOM risk for
413- # large models). self.model is abstract with no .sharding, so pass an explicit one.
401+ # Safely create the concrete PREFILL model once, avoiding OOM risks:
402+ # create_nnx_sharded_model builds the model with a jitted out_shardings so params
403+ # are produced already sharded, avoiding a single-device allocation of the full
404+ # model.
405+ with nn_partitioning .axis_rules (self .config .logical_axis_rules ):
414406 _ , full_abs = nnx .split (self .model )
415- with nn_partitioning .axis_rules (self .config .logical_axis_rules ):
416- full_sharding = sharding .nnx_construct_named_sharding (full_abs , self ._mesh )
407+ full_sharding = sharding .nnx_construct_named_sharding (full_abs , self ._mesh )
417408 concrete_model = maxtext_utils_nnx .create_nnx_sharded_model (
418409 self .model ,
419410 self ._create_model_fn ,
@@ -422,8 +413,18 @@ def _load_params_nnx(self, params, rng):
422413 )
423414 graphdef , _ , _ , rest_state = nnx .split (concrete_model , nnx .Param , nnx .Cache , ...)
424415 self .graphdef = graphdef
425- self ._nnx_rest_state = rest_state
426416 del concrete_model
417+
418+ if params :
419+ print ("Resharding given NNX params" )
420+ _ , params_abs , _ = nnx .split (self .model , nnx .Param , ...)
421+ # self.model is abstract (built via nnx.eval_shape), so its leaves carry logical
422+ # axis metadata but no physical .sharding. Resolve logical to physical here so
423+ # device_put actually reshards instead of being a no-op.
424+ with nn_partitioning .axis_rules (self .config .logical_axis_rules ):
425+ target_shardings = sharding .nnx_construct_named_sharding (params_abs , self ._mesh )
426+ params_state = jax .device_put (params , target_shardings )
427+ self ._nnx_rest_state = rest_state
427428 else :
428429 max_logging .log ("Loading NNX params via from_pretrained" )
429430 with self ._mesh :
@@ -438,22 +439,14 @@ def _load_params_nnx(self, params, rng):
438439 # `_nnx_rest_state`. Param-only filtering would silently drop them and
439440 # the model would run with random qrhs values.
440441 _ , params_state , _ , loaded_rest_state = nnx .split (nnx_model , nnx .Param , nnx .Cache , ...)
441- # `_prefill_jit` re-merges with `self.graphdef`, which must be the PREFILL
442- # graphdef built in `__init__` (matching `_create_model_fn`). Don't
443- # overwrite with the AR-mode graphdef from `from_pretrained` — the
444- # PREFILL/AR attention ops have different cache variable shapes, and a
445- # mismatch trips the `assert prefill_kv_cache` check inside attention_op.
446- with nn_partitioning .axis_rules (self .config .logical_axis_rules ):
447- concrete_model = self ._create_model_fn () # pyrefly: ignore[not-callable]
448- graphdef , _ , _ , rest_state = nnx .split (concrete_model , nnx .Param , nnx .Cache , ...)
449442 # Overlay loaded non-Param/non-Cache leaves (e.g. AQT qrhs.frozen) onto
450443 # the PREFILL-mode rest_state. The PREFILL concrete_model already has
451444 # placeholder qrhs vars at the right paths; we just swap in the loaded
452445 # values. Anything only in `loaded_rest_state` (e.g. AR-only RNG slots)
453446 # is ignored. We keep PREFILL rest_state as the base so RNG variables
454447 # match the PREFILL graphdef's expectations.
455- loaded_rest_dict = loaded_rest_state .to_pure_dict () # pyrefly: ignore[missing-attribute]
456- rest_dict = rest_state .to_pure_dict () # pyrefly: ignore[missing-attribute]
448+ loaded_rest_dict = nnx .to_pure_dict (loaded_rest_state )
449+ rest_dict = nnx .to_pure_dict (rest_state )
457450
458451 def _overlay (dst , src ):
459452 if isinstance (dst , dict ) and isinstance (src , dict ):
@@ -469,9 +462,8 @@ def _overlay(dst, src):
469462
470463 rest_dict = _overlay (rest_dict , loaded_rest_dict )
471464 nnx .replace_by_pure_dict (rest_state , rest_dict )
472- self .graphdef = graphdef
473465 self ._nnx_rest_state = rest_state
474- del nnx_model , concrete_model
466+ del nnx_model , loaded_rest_state , loaded_rest_dict , rest_dict
475467
476468 self .abstract_params = jax .tree .map (
477469 lambda x : jax .ShapeDtypeStruct (shape = x .shape , dtype = x .dtype , sharding = x .sharding )
@@ -2011,7 +2003,7 @@ def _logical_axes_for(var):
20112003 cache_state ,
20122004 is_leaf = lambda v : isinstance (v , nnx .Variable ),
20132005 )
2014- self .kv_cache_annotations_named = annotations_state .to_pure_dict ()
2006+ self .kv_cache_annotations_named = nnx .to_pure_dict (annotations_state )
20152007
20162008 return {
20172009 "logits" : jnp .zeros ((batch , 1 , vocab ), dtype = jnp .float32 ),
0 commit comments