@@ -794,6 +794,7 @@ def __call__(
794794 kv_caches : list [jax .Array ] | None = None ,
795795 attention_metadata = None ,
796796 deepstack_visual_embeds : None | list [jnp .ndarray ] = None ,
797+ forced_routed_experts : jax .Array | None = None ,
797798 ):
798799 cfg = self .config
799800 mesh = self .mesh
@@ -1013,19 +1014,34 @@ def __call__(
10131014 current_broadcast_args = list (broadcast_args )
10141015 current_in_axes_tuple = list (in_axes_tuple )
10151016
1017+ if forced_routed_experts is not None :
1018+ # Transpose [B, L, N, E] -> [N, B, L, E] for scan
1019+ forced_routed_experts_t = jnp .transpose (forced_routed_experts , (2 , 0 , 1 , 3 ))
1020+ cycle_interval = cfg .inhomogeneous_layer_cycle_interval
1021+ reshaped_forced = jnp .reshape (
1022+ forced_routed_experts_t , (scan_length , cycle_interval ) + forced_routed_experts_t .shape [1 :]
1023+ )
1024+ else :
1025+ reshaped_forced = None
1026+
10161027 if kv_caches is not None :
10171028 # Stack kv_caches for scan: [num_layers, ...]
10181029 stacked_kv_cache = jnp .stack (kv_caches , axis = 0 )
10191030
10201031 # We pass (y, stacked_kv_cache, 0) as the carry
10211032 carry = (y , stacked_kv_cache , 0 )
10221033
1023- # We don't pass kv_cache as a scanned argument anymore
1024-
10251034 # Pass None for previous_chunk, slot, kv_cache to align with __call__ signature
10261035 current_broadcast_args .extend ([None , None , None , attention_metadata ])
10271036 current_in_axes_tuple .extend ([nn .broadcast ] * 4 )
10281037
1038+ if reshaped_forced is not None :
1039+ current_broadcast_args .append (reshaped_forced )
1040+ current_in_axes_tuple .append (0 )
1041+ else :
1042+ current_broadcast_args .append (None )
1043+ current_in_axes_tuple .append (nn .broadcast )
1044+
10291045 max_logging .info (f"DEBUG: len(current_broadcast_args)={ len (current_broadcast_args )} " )
10301046 max_logging .info (f"DEBUG: current_broadcast_args={ [type (a ) for a in current_broadcast_args ]} " )
10311047
@@ -1047,8 +1063,15 @@ def __call__(
10471063 kv_caches [i ] = returned_kv_cache [i ]
10481064 else :
10491065 # 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 )
1066+ current_broadcast_args .extend ([None , None , None , None ])
1067+ current_in_axes_tuple .extend ([nn .broadcast ] * 4 )
1068+
1069+ if reshaped_forced is not None :
1070+ current_broadcast_args .append (reshaped_forced )
1071+ current_in_axes_tuple .append (0 )
1072+ else :
1073+ current_broadcast_args .append (None )
1074+ current_in_axes_tuple .append (nn .broadcast )
10521075
10531076 y , _ = self .scan_decoder_layers (
10541077 cfg ,
@@ -1077,6 +1100,10 @@ def __call__(
10771100 global_layer_idx = global_layer_idx_offset + index
10781101 kv_cache = kv_caches [index ] if kv_caches is not None else None
10791102 input_tokens = decoder_input_tokens if cfg .engram_layers else None
1103+ extra_kwargs = {}
1104+ if layer_prefix == "moe_layers" and forced_routed_experts is not None :
1105+ extra_kwargs ["forced_routed_experts" ] = forced_routed_experts [:, :, index , :]
1106+
10801107 y , kv_cache = layer (
10811108 config = cfg ,
10821109 mesh = mesh ,
@@ -1095,6 +1122,7 @@ def __call__(
10951122 kv_cache = kv_cache ,
10961123 attention_metadata = attention_metadata ,
10971124 decoder_input_tokens = input_tokens ,
1125+ ** extra_kwargs ,
10981126 )
10991127 if kv_caches is not None and kv_cache is not None :
11001128 kv_caches [index ] = kv_cache
@@ -1114,6 +1142,7 @@ def __call__(
11141142 slot = slot ,
11151143 )
11161144 else :
1145+ moe_lyr_idx = 0
11171146 for lyr in range (cfg .num_decoder_layers ):
11181147 RemattedBlockLayer = RemattedBlockLayers [0 ]
11191148 layer_kwargs = {}
@@ -1150,6 +1179,26 @@ def __call__(
11501179 layer = RemattedBlockLayer (
11511180 config = cfg , mesh = mesh , name = f"layers_{ lyr } " , quant = self .quant , model_mode = self .model_mode , ** layer_kwargs
11521181 )
1182+
1183+ is_moe = False
1184+ if cfg .decoder_block in (
1185+ DecoderBlockType .QWEN3_MOE ,
1186+ DecoderBlockType .QWEN3_NEXT ,
1187+ DecoderBlockType .QWEN3_5 ,
1188+ ):
1189+ is_moe = True
1190+
1191+ current_forced_routed_experts = None
1192+ if is_moe and forced_routed_experts is not None :
1193+ current_forced_routed_experts = forced_routed_experts [:, :, moe_lyr_idx , :]
1194+ moe_lyr_idx += 1
1195+ elif is_moe :
1196+ moe_lyr_idx += 1
1197+
1198+ extra_kwargs = {}
1199+ if is_moe and current_forced_routed_experts is not None :
1200+ extra_kwargs ["forced_routed_experts" ] = current_forced_routed_experts
1201+
11531202 y , returned_cache = layer (
11541203 y ,
11551204 decoder_segment_ids ,
@@ -1160,6 +1209,7 @@ def __call__(
11601209 slot = slot ,
11611210 kv_cache = kv_cache ,
11621211 attention_metadata = attention_metadata ,
1212+ ** extra_kwargs ,
11631213 ** layer_call_kwargs ,
11641214 )
11651215 if kv_caches is not None and returned_cache is not None :
0 commit comments