@@ -107,9 +107,17 @@ def __call__(
107107 )
108108
109109 if self .model_mode == MODEL_MODE_PREFILL :
110- logical_axis_names = ("activation_batch" , "prefill_activation_length" , "activation_embed" )
110+ logical_axis_names = (
111+ "activation_batch" ,
112+ "prefill_activation_length" ,
113+ "activation_embed" ,
114+ )
111115 else :
112- logical_axis_names = ("activation_batch" , "activation_length" , "activation_embed" )
116+ logical_axis_names = (
117+ "activation_batch" ,
118+ "activation_length" ,
119+ "activation_embed" ,
120+ )
113121
114122 if model_mode == MODEL_MODE_PREFILL :
115123 inputs = _maybe_shard_with_logical (inputs , logical_axis_names )
@@ -250,7 +258,11 @@ def __call__(
250258 ) -> jnp .ndarray :
251259 for lyr in range (self .num_decoder_layers ):
252260 inputs = self .decoder_layer (
253- config = self .config , mesh = self .mesh , name = f"layers_{ lyr } " , quant = self .quant , model_mode = model_mode
261+ config = self .config ,
262+ mesh = self .mesh ,
263+ name = f"layers_{ lyr } " ,
264+ quant = self .quant ,
265+ model_mode = model_mode ,
254266 )(
255267 inputs ,
256268 decoder_segment_ids ,
@@ -308,7 +320,10 @@ def setup(self):
308320 pipeline_stage_module = self .get_pipeline_stage_module (self .decoder_layer )
309321 remat_policy = self .get_remat_policy ()
310322 self .pipeline_module = pipeline .create_pipeline (
311- config = self .config , mesh = self .mesh , layers = pipeline_stage_module , remat_policy = remat_policy
323+ config = self .config ,
324+ mesh = self .mesh ,
325+ layers = pipeline_stage_module ,
326+ remat_policy = remat_policy ,
312327 )
313328
314329 def minimal_policy (self , with_context = False , with_quantization = False ):
@@ -398,7 +413,11 @@ def get_remat_policy(self):
398413 elif cfg .remat_policy == "qkv_proj_offloaded" :
399414 policy = jax .checkpoint_policies .save_and_offload_only_these_names (
400415 names_which_can_be_saved = [],
401- names_which_can_be_offloaded = ["query_proj" , "value_proj" , "key_proj" ],
416+ names_which_can_be_offloaded = [
417+ "query_proj" ,
418+ "value_proj" ,
419+ "key_proj" ,
420+ ],
402421 offload_src = "device" ,
403422 offload_dst = "pinned_host" ,
404423 )
@@ -521,7 +540,10 @@ def map_fn(path, value):
521540 block_layer ,
522541 prevent_cse = maxtext_utils .should_prevent_cse_in_remat (self .config ),
523542 policy = policy ,
524- static_argnums = (4 , 5 ), # Deterministic and model mode are static arguments.
543+ static_argnums = (
544+ 4 ,
545+ 5 ,
546+ ), # Deterministic and model mode are static arguments.
525547 )
526548 RemattedBlockLayers .append (layer )
527549 return RemattedBlockLayers
@@ -551,15 +573,34 @@ def get_norm_layer(self, num_features: int):
551573 ):
552574 return functools .partial (rms_norm , num_features = num_features , shard_mode = self .config .shard_mode )
553575 elif self .config .decoder_block == DecoderBlockType .GPT3 :
554- return functools .partial (gpt3 .gpt3_layer_norm , num_features = num_features , reductions_in_fp32 = False , use_bias = True )
555- elif self .config .decoder_block in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 ):
556576 return functools .partial (
557- normalizations .Qwen3NextRMSNormLinen , num_features = num_features , shard_mode = self .config .shard_mode
577+ gpt3 .gpt3_layer_norm ,
578+ num_features = num_features ,
579+ reductions_in_fp32 = False ,
580+ use_bias = True ,
581+ )
582+ elif self .config .decoder_block in (
583+ DecoderBlockType .QWEN3_NEXT ,
584+ DecoderBlockType .QWEN3_5 ,
585+ ):
586+ return functools .partial (
587+ normalizations .Qwen3NextRMSNormLinen ,
588+ num_features = num_features ,
589+ shard_mode = self .config .shard_mode ,
558590 )
559591 else :
560592 raise ValueError (f"Incorrect decoder_block name { self .config .decoder_block .value = } " )
561593
562- def scan_decoder_layers (self , cfg , decoder_layer , length , metadata_axis_name , mesh , in_axes_tuple , ** kwargs ):
594+ def scan_decoder_layers (
595+ self ,
596+ cfg ,
597+ decoder_layer ,
598+ length ,
599+ metadata_axis_name ,
600+ mesh ,
601+ in_axes_tuple ,
602+ ** kwargs ,
603+ ):
563604 """scan decoder layers, calls `flax.linen.transforms.scan`"""
564605 initializing = self .is_mutable_collection ("params" )
565606 params_spec = cfg .param_scan_axis if initializing else ScanIn (cfg .param_scan_axis )
@@ -583,7 +624,11 @@ def scan_decoder_layers(self, cfg, decoder_layer, length, metadata_axis_name, me
583624 metadata_params = {nn .PARTITION_NAME : metadata_axis_name },
584625 )
585626 return scan_fn (
586- config = cfg , mesh = mesh , name = metadata_axis_name , quant = self .quant , ** kwargs # pytype: disable=wrong-keyword-args
627+ config = cfg ,
628+ mesh = mesh ,
629+ name = metadata_axis_name ,
630+ quant = self .quant ,
631+ ** kwargs , # pytype: disable=wrong-keyword-args
587632 )
588633
589634 def get_pipeline_stage_module (self , decoder_blocks ):
@@ -674,7 +719,11 @@ def _apply_embedding(
674719 raise ValueError (f"Unsupported model_name for multimodal: { cfg .model_name } " )
675720
676721 if video_embeddings is not None and cfg .use_multimodal :
677- if cfg .model_name in ["qwen3-omni-30b-a3b" , "qwen3.5-35b-a3b" , "qwen3.5-397b-a17b" ]:
722+ if cfg .model_name in [
723+ "qwen3-omni-30b-a3b" ,
724+ "qwen3.5-35b-a3b" ,
725+ "qwen3.5-397b-a17b" ,
726+ ]:
678727 y = mm_utils .merge_mm_embeddings (
679728 text_embeddings = y ,
680729 multimodal_embeddings = video_embeddings ,
@@ -737,7 +786,12 @@ def apply_output_head(self, shared_embedding: nn.Module | nnx.Module, y, determi
737786 out_sharding = create_sharding (self .mesh , (None , None , "activation_vocab" ))
738787 else :
739788 out_sharding = create_sharding (
740- self .mesh , ("activation_embed_and_logits_batch" , "activation_length" , "activation_vocab" )
789+ self .mesh ,
790+ (
791+ "activation_embed_and_logits_batch" ,
792+ "activation_length" ,
793+ "activation_vocab" ,
794+ ),
741795 )
742796
743797 # [batch, length, emb_dim] -> [batch, length, vocab_size]
@@ -794,6 +848,7 @@ def __call__(
794848 kv_caches : list [jax .Array ] | None = None ,
795849 attention_metadata = None ,
796850 deepstack_visual_embeds : None | list [jnp .ndarray ] = None ,
851+ forced_routed_experts : jax .Array | None = None ,
797852 ):
798853 cfg = self .config
799854 mesh = self .mesh
@@ -863,7 +918,10 @@ def __call__(
863918 remaining_layers = self .config .num_decoder_layers - self .config .pipeline_parallel_layers
864919 if remaining_layers > 0 :
865920 logical_axis_rules_pp_as_dp = sharding .logical_axis_rules_pp_act_as_dp (self .config .logical_axis_rules )
866- with self .mesh , nn .partitioning .axis_rules (logical_axis_rules_pp_as_dp ):
921+ with (
922+ self .mesh ,
923+ nn .partitioning .axis_rules (logical_axis_rules_pp_as_dp ),
924+ ):
867925 y , _ = self .scan_decoder_layers (
868926 cfg ,
869927 RemattedBlockLayers [0 ],
@@ -1013,19 +1071,42 @@ def __call__(
10131071 current_broadcast_args = list (broadcast_args )
10141072 current_in_axes_tuple = list (in_axes_tuple )
10151073
1074+ supports_forced_routing = cfg .decoder_block in (
1075+ DecoderBlockType .QWEN3_MOE ,
1076+ DecoderBlockType .QWEN3_NEXT ,
1077+ DecoderBlockType .QWEN3_5 ,
1078+ )
1079+
1080+ if supports_forced_routing and forced_routed_experts is not None :
1081+ # Transpose [B, L, N, E] -> [N, B, L, E] for scan
1082+ forced_routed_experts_t = jnp .transpose (forced_routed_experts , (2 , 0 , 1 , 3 ))
1083+ cycle_interval = cfg .inhomogeneous_layer_cycle_interval
1084+ reshaped_forced = jnp .reshape (
1085+ forced_routed_experts_t ,
1086+ (scan_length , cycle_interval ) + forced_routed_experts_t .shape [1 :],
1087+ )
1088+ else :
1089+ reshaped_forced = None
1090+
10161091 if kv_caches is not None :
10171092 # Stack kv_caches for scan: [num_layers, ...]
10181093 stacked_kv_cache = jnp .stack (kv_caches , axis = 0 )
10191094
10201095 # We pass (y, stacked_kv_cache, 0) as the carry
10211096 carry = (y , stacked_kv_cache , 0 )
10221097
1023- # We don't pass kv_cache as a scanned argument anymore
1024-
10251098 # Pass None for previous_chunk, slot, kv_cache to align with __call__ signature
10261099 current_broadcast_args .extend ([None , None , None , attention_metadata ])
10271100 current_in_axes_tuple .extend ([nn .broadcast ] * 4 )
10281101
1102+ if supports_forced_routing and forced_routed_experts is not None :
1103+ if reshaped_forced is not None :
1104+ current_broadcast_args .append (reshaped_forced )
1105+ current_in_axes_tuple .append (0 )
1106+ else :
1107+ current_broadcast_args .append (None )
1108+ current_in_axes_tuple .append (nn .broadcast )
1109+
10291110 max_logging .info (f"DEBUG: len(current_broadcast_args)={ len (current_broadcast_args )} " )
10301111 max_logging .info (f"DEBUG: current_broadcast_args={ [type (a ) for a in current_broadcast_args ]} " )
10311112
@@ -1047,8 +1128,19 @@ def __call__(
10471128 kv_caches [i ] = returned_kv_cache [i ]
10481129 else :
10491130 # Fallback to old behavior if kv_caches is None (not vLLM RPA)
1050- current_broadcast_args .append (None )
1051- current_in_axes_tuple .append (nn .broadcast )
1131+ if supports_forced_routing and forced_routed_experts is not None :
1132+ current_broadcast_args .extend ([None , None , None , None ])
1133+ current_in_axes_tuple .extend ([nn .broadcast ] * 4 )
1134+
1135+ if reshaped_forced is not None :
1136+ current_broadcast_args .append (reshaped_forced )
1137+ current_in_axes_tuple .append (0 )
1138+ else :
1139+ current_broadcast_args .append (None )
1140+ current_in_axes_tuple .append (nn .broadcast )
1141+ else :
1142+ current_broadcast_args .append (None )
1143+ current_in_axes_tuple .append (nn .broadcast )
10521144
10531145 y , _ = self .scan_decoder_layers (
10541146 cfg ,
@@ -1077,6 +1169,10 @@ def __call__(
10771169 global_layer_idx = global_layer_idx_offset + index
10781170 kv_cache = kv_caches [index ] if kv_caches is not None else None
10791171 input_tokens = decoder_input_tokens if cfg .engram_layers else None
1172+ extra_kwargs = {}
1173+ if layer_prefix == "moe_layers" and forced_routed_experts is not None :
1174+ extra_kwargs ["forced_routed_experts" ] = forced_routed_experts [:, :, index , :]
1175+
10801176 y , kv_cache = layer (
10811177 config = cfg ,
10821178 mesh = mesh ,
@@ -1095,6 +1191,7 @@ def __call__(
10951191 kv_cache = kv_cache ,
10961192 attention_metadata = attention_metadata ,
10971193 decoder_input_tokens = input_tokens ,
1194+ ** extra_kwargs ,
10981195 )
10991196 if kv_caches is not None and kv_cache is not None :
11001197 kv_caches [index ] = kv_cache
@@ -1114,6 +1211,7 @@ def __call__(
11141211 slot = slot ,
11151212 )
11161213 else :
1214+ moe_lyr_idx = 0
11171215 for lyr in range (cfg .num_decoder_layers ):
11181216 RemattedBlockLayer = RemattedBlockLayers [0 ]
11191217 layer_kwargs = {}
@@ -1133,23 +1231,60 @@ def __call__(
11331231 "is_nope_layer" : llama4 .determine_is_nope_layer (lyr , self .config .nope_layer_interval ),
11341232 "is_moe_layer" : llama4 .determine_is_moe_layer (lyr , self .config .interleave_moe_layer_step ),
11351233 }
1136- if cfg .decoder_block in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 ):
1234+ if cfg .decoder_block in (
1235+ DecoderBlockType .QWEN3_NEXT ,
1236+ DecoderBlockType .QWEN3_5 ,
1237+ ):
11371238 layer_kwargs = {"layer_idx" : lyr }
11381239 kv_cache = None
1139- if kv_caches is not None and cfg .decoder_block not in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 ):
1240+ if kv_caches is not None and cfg .decoder_block not in (
1241+ DecoderBlockType .QWEN3_NEXT ,
1242+ DecoderBlockType .QWEN3_5 ,
1243+ ):
11401244 kv_cache = kv_caches [lyr ]
1141- elif kv_caches is not None and cfg .decoder_block in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 ):
1245+ elif kv_caches is not None and cfg .decoder_block in (
1246+ DecoderBlockType .QWEN3_NEXT ,
1247+ DecoderBlockType .QWEN3_5 ,
1248+ ):
11421249 # For Qwen3Next & Qwen3.5, kv_caches is a dictionary of lists of caches.
11431250 if (lyr + 1 ) % cfg .inhomogeneous_layer_cycle_interval == 0 :
1144- kv_cache = (kv_caches ["key_cache" ][lyr ], kv_caches ["value_cache" ][lyr ])
1251+ kv_cache = (
1252+ kv_caches ["key_cache" ][lyr ],
1253+ kv_caches ["value_cache" ][lyr ],
1254+ )
11451255
11461256 if cfg .decoder_block == DecoderBlockType .GPT_OSS :
11471257 layer_kwargs = {"attention_type" : gpt_oss .get_attention_type (layer_id = lyr )}
11481258 if cfg .decoder_block == DecoderBlockType .OLMO3 :
11491259 layer_kwargs = {"attention_type" : olmo3 .get_attention_type (layer_id = lyr )}
11501260 layer = RemattedBlockLayer (
1151- config = cfg , mesh = mesh , name = f"layers_{ lyr } " , quant = self .quant , model_mode = self .model_mode , ** layer_kwargs
1261+ config = cfg ,
1262+ mesh = mesh ,
1263+ name = f"layers_{ lyr } " ,
1264+ quant = self .quant ,
1265+ model_mode = self .model_mode ,
1266+ ** layer_kwargs ,
11521267 )
1268+
1269+ is_moe = False
1270+ if cfg .decoder_block in (
1271+ DecoderBlockType .QWEN3_MOE ,
1272+ DecoderBlockType .QWEN3_NEXT ,
1273+ DecoderBlockType .QWEN3_5 ,
1274+ ):
1275+ is_moe = True
1276+
1277+ current_forced_routed_experts = None
1278+ if is_moe and forced_routed_experts is not None :
1279+ current_forced_routed_experts = forced_routed_experts [:, :, moe_lyr_idx , :]
1280+ moe_lyr_idx += 1
1281+ elif is_moe :
1282+ moe_lyr_idx += 1
1283+
1284+ extra_kwargs = {}
1285+ if is_moe and current_forced_routed_experts is not None :
1286+ extra_kwargs ["forced_routed_experts" ] = current_forced_routed_experts
1287+
11531288 y , returned_cache = layer (
11541289 y ,
11551290 decoder_segment_ids ,
@@ -1160,10 +1295,14 @@ def __call__(
11601295 slot = slot ,
11611296 kv_cache = kv_cache ,
11621297 attention_metadata = attention_metadata ,
1298+ ** extra_kwargs ,
11631299 ** layer_call_kwargs ,
11641300 )
11651301 if kv_caches is not None and returned_cache is not None :
1166- if cfg .decoder_block not in (DecoderBlockType .QWEN3_NEXT , DecoderBlockType .QWEN3_5 ):
1302+ if cfg .decoder_block not in (
1303+ DecoderBlockType .QWEN3_NEXT ,
1304+ DecoderBlockType .QWEN3_5 ,
1305+ ):
11671306 kv_caches [lyr ] = returned_cache
11681307 elif (lyr + 1 ) % cfg .inhomogeneous_layer_cycle_interval == 0 :
11691308 kv_caches ["key_cache" ][lyr ] = returned_cache [0 ]
@@ -1265,7 +1404,12 @@ def _apply_gemma3_scanned_blocks(
12651404 # We name the remainder block with a 'remainder' suffix to avoid parameter name collisions
12661405 rem_layer_kwargs = {"num_of_layers" : num_remaining_layers }
12671406 layer = RemattedGemma3Block (
1268- config = cfg , mesh = mesh , quant = self .quant , model_mode = self .model_mode , name = "layers_remainder" , ** rem_layer_kwargs
1407+ config = cfg ,
1408+ mesh = mesh ,
1409+ quant = self .quant ,
1410+ model_mode = self .model_mode ,
1411+ name = "layers_remainder" ,
1412+ ** rem_layer_kwargs ,
12691413 ) # pytype: disable=wrong-keyword-args
12701414 y , _ = layer (
12711415 y ,
0 commit comments