metrale_model_engine/model/
impl_lora.rs1use anyhow::{Context, Result};
10
11use metrale_gpu_runtime::gpu::DevicePtr;
12
13use super::types::TransformerModel;
14use metrale_model_layers::layers::ops;
15
16impl TransformerModel {
17 pub fn adapter_id_for_slot(&self, slot: i32) -> u64 {
21 match self.lora.as_ref() {
22 Some(lw) => lw.adapter_id_for_slot(slot),
23 None => 0,
24 }
25 }
26
27 pub fn acquire_adapter_slot(&self, slot: i32) -> i32 {
34 match self.lora.as_ref() {
35 Some(lw) => lw.acquire_slot(slot),
36 None => -1,
37 }
38 }
39
40 pub fn release_adapter_slot(&self, resolved: i32) {
43 if let Some(lw) = self.lora.as_ref() {
44 lw.release_slot(resolved);
45 }
46 }
47
48 pub(crate) fn moe_lora_route(
53 &self,
54 adapter_slot: i32,
55 ) -> metrale_model_layers::layer::MoeLoraRoute {
56 let (active, has) = match self.lora.as_ref() {
57 Some(lw) => (lw.active as i32, true),
58 None => (-1, false),
59 };
60 metrale_model_layers::lora::resolve_moe_lora_route(adapter_slot, active, has)
61 }
62
63 pub(crate) fn decode_moe_route(&self) -> metrale_model_layers::layer::MoeLoraRoute {
66 match self
67 .decode_moe_route
68 .load(std::sync::atomic::Ordering::Relaxed)
69 {
70 0 => metrale_model_layers::layer::MoeLoraRoute::Skip,
71 2 => metrale_model_layers::layer::MoeLoraRoute::Refuse,
72 _ => metrale_model_layers::layer::MoeLoraRoute::Fold,
73 }
74 }
75
76 fn store_decode_moe_route(&self, route: metrale_model_layers::layer::MoeLoraRoute) {
77 let v = match route {
78 metrale_model_layers::layer::MoeLoraRoute::Skip => 0,
79 metrale_model_layers::layer::MoeLoraRoute::Fold => 1,
80 metrale_model_layers::layer::MoeLoraRoute::Refuse => 2,
81 };
82 self.decode_moe_route
83 .store(v, std::sync::atomic::Ordering::Relaxed);
84 }
85
86 pub(crate) fn stamp_decode_moe_single(&self, adapter_slot: i32) {
88 self.store_decode_moe_route(self.moe_lora_route(adapter_slot));
89 }
90
91 pub(crate) fn stamp_decode_moe_batch(&self, seqs: &[&mut crate::traits::SequenceState]) {
97 use metrale_model_layers::layer::MoeLoraRoute;
98 let mut any_fold = false;
99 for s in seqs.iter() {
100 match self.moe_lora_route(s.adapter_slot) {
101 MoeLoraRoute::Refuse => {
102 self.store_decode_moe_route(MoeLoraRoute::Refuse);
103 return;
104 }
105 MoeLoraRoute::Fold => any_fold = true,
106 MoeLoraRoute::Skip => {}
107 }
108 }
109 self.store_decode_moe_route(if any_fold {
110 MoeLoraRoute::Fold
111 } else {
112 MoeLoraRoute::Skip
113 });
114 }
115
116 pub fn set_lora_weights(
117 &mut self,
118 mut lora: Option<metrale_model_layers::lora::LoraWeights>,
119 ) -> Result<()> {
120 if let Some(ref lw) = lora {
121 self.lora_rotatable =
125 self.levers.lora_rotate || metrale_model_layers::lora::lora_peer_env().is_some();
126 let kernels = ops::lora_delta::LoraKernels::new(self.gpu.as_ref())?;
127 let active = lw.active_layers().to_vec();
128 let tables = lw.tables.clone();
129 let scale_table = lw.scale_table;
130 let installed = self.install_lora_layers(&active, kernels, &tables, scale_table)?;
131 let resident: Vec<String> = lw
134 .adapter_names()
135 .into_iter()
136 .filter(|n| !n.is_empty())
137 .collect();
138 tracing::info!(
139 "LoRA: {} adapter(s) resident [{}], active '{}' installed on \
140 {installed} layers (r={}, max_rank={}, max_loras={}, \
141 pool={:.1} MiB, rotatable={})",
142 resident.len(),
143 resident.join(", "),
144 lw.name,
145 lw.adapter_config.r,
146 lw.max_rank,
147 lw.max_loras,
148 lw.pool_bytes as f64 / (1024.0 * 1024.0),
149 self.lora_rotatable,
150 );
151 }
152 if let Some(ref mut lw) = lora {
157 let raws = std::mem::take(&mut lw.overlay_raw);
158 if raws.iter().any(|r| r.is_some()) {
159 self.build_token_overlays(lw.max_loras, &raws)?;
160 }
161 }
162 self.lora = lora;
163 Ok(())
164 }
165
166 fn build_token_overlays(
171 &mut self,
172 max_loras: usize,
173 raws: &[Option<metrale_model_layers::lora::OverlayRawSlot>],
174 ) -> Result<()> {
175 let tied = self.lm_head_weight.weight.0 == self.embed_tokens.weight.0
179 || self.lm_head_nvfp4.is_some()
180 || self.lm_head_fp8.is_some();
181 let vocab = self.config.vocab_size;
182 let h = self.config.hidden_size;
183 let served_embed = self.embed_tokens.weight;
184 let served_lmhead = self.lm_head_weight.weight;
185 let stream = self.gpu.default_stream();
186 let mut overlays: Vec<Option<metrale_model_layers::lora::EmbedOverlay>> =
187 (0..max_loras).map(|_| None).collect();
188 for (k, raw) in raws.iter().enumerate() {
189 if let Some(slot) = raw
190 && k < max_loras
191 {
192 overlays[k] = metrale_model_layers::lora::build_overlay(
193 self.gpu.as_ref(),
194 &self.overlay_kernels,
195 slot,
196 served_embed,
197 served_lmhead,
198 vocab,
199 h,
200 tied,
201 stream,
202 )?;
203 }
204 }
205 let set = metrale_model_layers::lora::TokenOverlaySet::from_slots(
206 self.gpu.as_ref(),
207 overlays,
208 max_loras,
209 tied,
210 )?;
211 if set.any_active() {
212 tracing::info!(
213 "LoRA overlay: token-overlay tables built (max_n_override={}, tied={})",
214 set.max_n_override,
215 tied,
216 );
217 self.overlays = Some(set);
218 }
219 Ok(())
220 }
221}