use alloc::{collections::BTreeMap,vec::Vec};
use ruda_model::{module::ParamId,tensor::{DType,MoeExpertStrategy,backend::Backend}};
use crate::{LoRALinearConfig,ExpertAdapterTarget,FrozenPackedSwiGluExperts,AdaptedPackedSwiGluExperts,SelectablePackedExperts,
SelectablePackedMoeTransformerModel,Nf4MoeLayer};
use super::{TransformerProjectionShape,Nf4MoeTransformerModel,Nf4MoeTransformerLayer,Nf4MoeTransformerBlock};
#[derive(Clone,Debug)]
pub struct PackedExpertLayerAdapterConfig {
pub layer:usize,
pub adapter:LoRALinearConfig,
pub targets:Vec<ExpertAdapterTarget>,
pub adapter_dtype:DType,
pub use_rslora:bool,
pub forward:MoeExpertStrategy,
pub backward:MoeExpertStrategy,
}
impl<B:Backend,P:TransformerProjectionShape<B>> Nf4MoeTransformerModel<B,P,FrozenPackedSwiGluExperts<B>> {
pub fn with_expert_adapters(self,targets:&[PackedExpertLayerAdapterConfig]) -> SelectablePackedMoeTransformerModel<B,P> {
let mut selected=BTreeMap::new();
for config in targets {
assert!(config.layer<self.layers.len(),"expert adapter layer index exceeds original loaded model");
assert!(selected.insert(config.layer,config).is_none(),"duplicate original expert adapter layer index");
let Nf4MoeTransformerLayer::Packed(block)=&self.layers[config.layer] else {panic!("selected expert adapters require an actual original packed-expert layer")};
AdaptedPackedSwiGluExperts::from_frozen(block.routed.experts.clone()).validate_adapter_targets(&config.adapter,&config.targets,config.adapter_dtype);
}
let layers=self.layers.into_iter().enumerate().map(|(index,layer)|match layer {
Nf4MoeTransformerLayer::Dense(block)=>Nf4MoeTransformerLayer::Dense(block),
Nf4MoeTransformerLayer::Floating(block)=>Nf4MoeTransformerLayer::Floating(block),
Nf4MoeTransformerLayer::Packed(block)=>{
let routed=block.routed;
let experts=if let Some(config)=selected.get(&index) {SelectablePackedExperts::Adapted(AdaptedPackedSwiGluExperts::from_frozen(routed.experts)
.with_adapters(&config.adapter,&config.targets,config.adapter_dtype,config.use_rslora,config.forward,config.backward))}
else {SelectablePackedExperts::Original(routed.experts)};
Nf4MoeTransformerLayer::Packed(Nf4MoeTransformerBlock {attention:block.attention,
routed:Nf4MoeLayer {router:routed.router,experts,correction_bias:routed.correction_bias,routing:routed.routing,router_input_dtype:routed.router_input_dtype},
shared:block.shared,attention_norm:block.attention_norm,feed_forward_norm:block.feed_forward_norm,residual_dropout:block.residual_dropout,norm_first:block.norm_first})
},
}).collect();
Nf4MoeTransformerModel::from_parts(self.embeddings,layers,self.normalization,self.head)
}
}
impl<B:Backend,P:TransformerProjectionShape<B>> Nf4MoeTransformerModel<B,P,SelectablePackedExperts<B>> {
pub fn expert_adapter_parameter_ids(&self) -> Vec<ParamId> {
let mut ids=Vec::new();for layer in &self.layers {if let Nf4MoeTransformerLayer::Packed(block)=layer {
for id in block.routed.experts.adapter_parameter_ids() {if !ids.contains(&id) {ids.push(id);}}}}ids
}
}