metrale_model_layers/layers/qwen3_ssm/
lora.rs1use anyhow::Result;
10use metrale_gpu_runtime::gpu::GpuBackend;
11
12use super::Qwen3SsmLayer;
13use crate::layers::FfnComponent;
14use crate::layers::ops::lora_delta::{LoraKernels, LoraPair};
15use crate::lora::ExpertLoraLayer;
16
17impl Qwen3SsmLayer {
18 pub fn set_moe_lora_weights(
21 &mut self,
22 router: Option<LoraPair>,
23 experts: ExpertLoraLayer,
24 kernels: LoraKernels,
25 gpu: &dyn GpuBackend,
26 ) -> Result<()> {
27 if let FfnComponent::Moe(m) = &mut self.ffn {
28 return m.set_lora_weights(router, experts, kernels, gpu);
29 }
30 anyhow::bail!(
31 "LoRA: router/expert deltas installed on a linear-attention layer whose \
32 FFN is not MoE (loader/adapter mismatch)"
33 )
34 }
35}
36
37impl Qwen3SsmLayer {
38 pub fn set_ffn_lora_weights(
42 &mut self,
43 ffn: crate::layers::ops::lora_delta::LoraFfnWeights,
44 ) -> Result<()> {
45 match &mut self.ffn {
46 FfnComponent::Dense(d) => d.set_lora_weights(ffn),
47 FfnComponent::Moe(_) => anyhow::bail!(
48 "LoRA: dense-FFN delta on a linear-attention layer whose FFN is MoE — \
49 routed-expert deltas belong on set_moe_lora_weights"
50 ),
51 FfnComponent::None => {
52 anyhow::bail!("LoRA: dense-FFN delta on a linear-attention layer that has no FFN")
53 }
54 }
55 }
56}
57
58impl Qwen3SsmLayer {
59 pub fn set_out_proj_lora(&mut self, pair: LoraPair, kernels: LoraKernels) {
62 self.lora_out_proj = Some((pair, kernels));
63 }
64
65 pub(super) fn apply_lora_out_proj(
73 &self,
74 ctx: &crate::layers::ForwardContext,
75 normed_out: metrale_gpu_runtime::gpu::DevicePtr,
76 out: metrale_gpu_runtime::gpu::DevicePtr,
77 m: u32,
78 stream: u64,
79 ) -> Result<()> {
80 if crate::layers::ops::lora_delta::lora_no_ffn() {
81 return Ok(());
82 }
83 let Some((ref pair, ref kernels)) = self.lora_out_proj else {
84 return Ok(());
85 };
86 crate::layers::ops::lora_delta::apply_lora_delta(
87 ctx.gpu,
88 kernels,
89 pair,
90 normed_out,
91 out,
92 m,
93 ctx.buffers.lora_xa(),
94 ctx.buffers.lora_delta(),
95 stream,
96 )
97 }
98}