1use anyhow::Result;
14use metrale_gpu_runtime::gpu::DevicePtr;
15
16use crate::model::types::TransformerModel;
17use crate::traits::{ModelDraft, SequenceState};
18
19impl ModelDraft for TransformerModel {
20 fn mtp_slot_draft_capacity(&self, slot_idx: usize) -> usize {
21 self.ssm_pool.verify_draft_capacity(slot_idx)
22 }
23
24 fn generate_speculative(
25 &self,
26 prompt_tokens: &[u32],
27 params: &metrale_sampling::SamplingParams,
28 num_drafts: usize,
29 ) -> Result<crate::engine::GenerateResult> {
30 self.gdn_carry_flush_pending()?;
31 self.generate_speculative_dispatch(prompt_tokens, params, num_drafts)
32 }
33
34 fn has_proposer(&self) -> bool {
35 self.has_proposer_dispatch()
36 }
37
38 fn dflash_gamma(&self) -> Option<usize> {
39 self.proposer.as_ref().and_then(|p| p.block_gamma())
40 }
41
42 fn has_self_speculative(&self) -> bool {
43 self.has_self_speculative_dispatch()
44 }
45
46 fn decode_draft(&self, token: u32, seq: &mut SequenceState, stream: u64) -> Result<DevicePtr> {
47 self.gdn_carry_flush_pending()?;
48 self.decode_draft_dispatch(token, seq, stream)
49 }
50
51 fn stash_verify_hidden_rows(&self, rows: &[usize], _stream: u64) -> Result<()> {
52 self.stash_verify_hidden_rows_dispatch(rows, _stream)
53 }
54
55 fn save_hidden_for_mtp_from_stash(&self, idx: usize, _stream: u64) -> Result<()> {
56 self.save_hidden_for_mtp_from_stash_dispatch(idx, _stream)
57 }
58
59 fn stash_verify_catchup_rows(&self, slot_rows: &[(usize, usize)]) -> Result<()> {
60 self.stash_verify_catchup_rows_dispatch(slot_rows)
61 }
62
63 fn run_mtp_catchup_batched(
64 &self,
65 tokens: &[Vec<u32>],
66 first_slot: &[usize],
67 first_pos: &[usize],
68 seqs: &mut [&mut SequenceState],
69 ) -> Result<usize> {
70 self.run_mtp_catchup_batched_dispatch(tokens, first_slot, first_pos, seqs)
71 }
72
73 fn run_mtp_propose_batched(
74 &self,
75 tokens: &[u32],
76 positions: &[usize],
77 stash_idx: &[usize],
78 num_drafts: usize,
79 seqs: &mut [&mut SequenceState],
80 _stream: u64,
81 out_conf: Option<&mut Vec<Vec<f32>>>,
82 ) -> Result<Option<Vec<Vec<u32>>>> {
83 self.run_mtp_propose_batched_dispatch(
84 tokens, positions, stash_idx, num_drafts, seqs, out_conf,
85 )
86 }
87
88 fn mtp_propose_batch_max(&self) -> usize {
89 match &self.proposer {
90 Some(p) => p.propose_batch_max(&self.buffers, &self.config),
91 None => 1,
92 }
93 }
94
95 fn save_hidden_for_catchup(&self, token_idx: usize, pos: usize) -> Result<()> {
96 self.save_hidden_for_catchup_dispatch(token_idx, pos)
97 }
98
99 fn save_hidden_for_mtp(&self, token_idx: usize, _stream: u64) -> Result<()> {
100 self.save_hidden_for_mtp_dispatch(token_idx, _stream)
101 }
102
103 fn save_dflash_hidden_for_propose(&self, token_idx: usize, _stream: u64) -> Result<()> {
104 self.save_dflash_hidden_dispatch(token_idx, _stream)
105 }
106
107 fn dflash_accept_append(&self, seq: &mut SequenceState) -> Result<()> {
108 let base = match self.dflash_hidden_save {
109 Some(p) => p,
110 None => return Ok(()),
111 };
112 let prop = match seq.proposer_state.as_mut() {
113 Some(p) => p.as_mut(),
114 None => return Ok(()),
115 };
116 let d = prop
117 .as_any_mut()
118 .downcast_mut::<metrale_model_arch::dflash_head::DflashProposerState>()
119 .ok_or_else(|| anyhow::anyhow!("not DFlash proposer state"))?;
120 let n_layers = self.dflash_capture_layers.len();
121 if n_layers == 0 {
122 return Ok(());
123 }
124 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
125 let save_1 = base.offset(ctx_slot_bytes);
126 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
127 self.gpu
128 .copy_d2d_async(save_1, dst, ctx_slot_bytes, self.gpu.default_stream())?;
129 d.ctx_positions.push((seq.seq_len as i32).saturating_sub(1));
130 d.ctx_len += 1;
131 Ok(())
132 }
133
134 fn dflash_eagle_accept_append(&self, seq: &mut SequenceState) -> Result<()> {
135 let base = match self.dflash_hidden_save {
136 Some(p) => p,
137 None => return Ok(()),
138 };
139 let prop = match seq.proposer_state.as_mut() {
140 Some(p) => p.as_mut(),
141 None => return Ok(()),
142 };
143 let d = prop
144 .as_any_mut()
145 .downcast_mut::<metrale_model_arch::dflash_head::DflashProposerState>()
146 .ok_or_else(|| anyhow::anyhow!("not DFlash proposer state"))?;
147 let n_layers = self.dflash_capture_layers.len();
148 if n_layers == 0 {
149 return Ok(());
150 }
151 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
152 let stream = self.gpu.default_stream();
153 let pos_row0 = (seq.seq_len as i32).saturating_sub(2);
154 let pos_row1 = (seq.seq_len as i32).saturating_sub(1);
155 let save_0 = base;
156 let dst_0 = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
157 self.gpu
158 .copy_d2d_async(save_0, dst_0, ctx_slot_bytes, stream)?;
159 d.ctx_positions.push(pos_row0);
160 d.ctx_len += 1;
161 let save_1 = base.offset(ctx_slot_bytes);
162 let dst_1 = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
163 self.gpu
164 .copy_d2d_async(save_1, dst_1, ctx_slot_bytes, stream)?;
165 d.ctx_positions.push(pos_row1);
166 d.ctx_len += 1;
167 d.skip_next_decode_append = true;
168 Ok(())
169 }
170
171 fn dflash_eagle_kgamma_append(
172 &self,
173 seq: &mut SequenceState,
174 num_accepted: usize,
175 base_pos: usize,
176 ) -> Result<()> {
177 let base = match self.dflash_hidden_save {
178 Some(p) => p,
179 None => return Ok(()),
180 };
181 let prop = match seq.proposer_state.as_mut() {
182 Some(p) => p.as_mut(),
183 None => return Ok(()),
184 };
185 let d = prop
186 .as_any_mut()
187 .downcast_mut::<metrale_model_arch::dflash_head::DflashProposerState>()
188 .ok_or_else(|| anyhow::anyhow!("not DFlash proposer state"))?;
189 let n_layers = self.dflash_capture_layers.len();
190 if n_layers == 0 {
191 return Ok(());
192 }
193 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
194 let stream = self.gpu.default_stream();
195 for t in 0..=num_accepted {
196 let row = base.offset(t * ctx_slot_bytes);
197 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
198 self.gpu.copy_d2d_async(row, dst, ctx_slot_bytes, stream)?;
199 let pos = (base_pos + t) as i32;
200 d.ctx_positions.push(pos);
201 d.ctx_len += 1;
202 }
203 d.skip_next_decode_append = true;
204 Ok(())
205 }
206
207 fn dflash_capture_band(&self) -> usize {
208 self.dflash_kgamma
209 }
210
211 fn commit_ctx(
212 &self,
213 seq: &mut SequenceState,
214 num_committed: usize,
215 base_pos: usize,
216 scratch_row: usize,
217 ) -> Result<()> {
218 if num_committed == 0 {
219 return Ok(());
220 }
221 let base = match self.dflash_hidden_save {
222 Some(p) => p,
223 None => return Ok(()),
224 };
225 let prop = match seq.proposer_state.as_mut() {
226 Some(p) => p.as_mut(),
227 None => return Ok(()),
228 };
229 let d = match prop
230 .as_any_mut()
231 .downcast_mut::<metrale_model_arch::dflash_head::DflashProposerState>()
232 {
233 Some(d) => d,
234 None => return Ok(()),
235 };
236 let n_layers = self.dflash_capture_layers.len();
237 if n_layers == 0 {
238 return Ok(());
239 }
240 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
241 let stream = self.gpu.default_stream();
242
243 if scratch_row + num_committed > self.dflash_hidden_save_rows {
248 tracing::warn!(target: "metrale_model_engine::model::trait_impl", "commit_ctx: scratch rows {}..{} exceed capture capacity {} — skipping (ctx hole)",
249 scratch_row,
250 scratch_row + num_committed,
251 self.dflash_hidden_save_rows,
252 );
253 return Ok(());
254 }
255
256 if d.ctx_len + num_committed > d.max_ctx_len {
263 let keep = (d.max_ctx_len / 2).min(d.max_ctx_len.saturating_sub(num_committed));
264 let drop_n = d.ctx_len.saturating_sub(keep);
265 if drop_n > 0 {
266 let src = d.ctx_hidden_acc.offset(drop_n * ctx_slot_bytes);
267 let dst0 = d.ctx_hidden_acc.offset(0);
268 self.gpu
269 .copy_d2d_async(src, dst0, keep * ctx_slot_bytes, stream)?;
270 d.ctx_positions.drain(..drop_n);
271 d.ctx_len = keep;
272 d.ctx_committed = 0;
273 tracing::info!(target: "metrale_model_engine::model::trait_impl", "DFlash UNIFIED_CTX watermark: slid ctx window (dropped {} oldest, keep {})",
274 drop_n,
275 keep,
276 );
277 }
278 }
279
280 debug_assert_eq!(d.ctx_positions.len(), d.ctx_len);
284 for t in 0..num_committed {
285 let row = base.offset((scratch_row + t) * ctx_slot_bytes);
286 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
287 self.gpu.copy_d2d_async(row, dst, ctx_slot_bytes, stream)?;
288 d.ctx_positions.push((base_pos + t) as i32);
289 d.ctx_len += 1;
290 }
291 d.skip_next_decode_append = true;
294
295 tracing::debug!(target: "metrale_model_engine::model::trait_impl", "CTX_COMMIT slot={} rows={} base_pos={} ctx_len_after={}",
296 seq.slot_idx,
297 num_committed,
298 base_pos,
299 d.ctx_len,
300 );
301 if self.stats.once("log:dflash_unified_ctx") {
302 tracing::info!(target: "metrale_model_engine::model::trait_impl", "DFlash UNIFIED_CTX ACTIVE: first commit_ctx rows={} base_pos={} ctx_len={}",
303 num_committed,
304 base_pos,
305 d.ctx_len,
306 );
307 }
308 Ok(())
309 }
310
311 fn dflash_serial_ctx_append(&self, seq: &mut SequenceState) -> Result<()> {
312 let base = match self.dflash_hidden_save {
317 Some(p) => p,
318 None => return Ok(()),
319 };
320 let prop = match seq.proposer_state.as_mut() {
321 Some(p) => p.as_mut(),
322 None => return Ok(()),
323 };
324 let d = match prop
327 .as_any_mut()
328 .downcast_mut::<metrale_model_arch::dflash_head::DflashProposerState>()
329 {
330 Some(d) => d,
331 None => return Ok(()),
332 };
333 let n_layers = self.dflash_capture_layers.len();
334 if n_layers == 0 {
335 return Ok(());
336 }
337 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
338 let stream = self.gpu.default_stream();
339 if d.ctx_len >= d.max_ctx_len {
346 let keep = d.max_ctx_len / 2;
347 let drop_n = d.ctx_len - keep;
348 let src = d.ctx_hidden_acc.offset(drop_n * ctx_slot_bytes);
349 let dst0 = d.ctx_hidden_acc.offset(0);
350 self.gpu
351 .copy_d2d_async(src, dst0, keep * ctx_slot_bytes, stream)?;
352 d.ctx_positions.drain(..drop_n);
353 d.ctx_len = keep;
354 d.ctx_committed = 0;
355 tracing::info!(target: "metrale_model_engine::model::trait_impl", "DFlash SERIAL_APPEND watermark: slid ctx window (dropped {} oldest, keep {})",
356 drop_n,
357 keep,
358 );
359 }
360 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
361 self.gpu.copy_d2d_async(base, dst, ctx_slot_bytes, stream)?;
362 if self.stats.once("log:dflash_serial_append") {
363 tracing::info!(target: "metrale_model_engine::model::trait_impl", "DFlash SERIAL_APPEND ACTIVE: first serial ctx append at ctx_len={} pos={}",
364 d.ctx_len,
365 seq.seq_len.saturating_sub(1),
366 );
367 }
368 debug_assert_eq!(d.ctx_positions.len(), d.ctx_len);
371 d.ctx_positions.push(seq.seq_len.saturating_sub(1) as i32);
372 d.ctx_len += 1;
373 d.skip_next_decode_append = true;
376 Ok(())
377 }
378
379 fn run_mtp_propose(
380 &self,
381 token: u32,
382 position: usize,
383 seq: &mut SequenceState,
384 _stream: u64,
385 ) -> Result<Option<u32>> {
386 self.run_mtp_propose_dispatch(token, position, seq, _stream)
387 }
388
389 fn run_mtp_propose_multi(
390 &self,
391 token: u32,
392 position: usize,
393 num_drafts: usize,
394 seq: &mut SequenceState,
395 _stream: u64,
396 grammar_bitmask: Option<&[i32]>,
397 ) -> Result<Vec<u32>> {
398 self.run_mtp_propose_multi_dispatch(
399 token,
400 position,
401 num_drafts,
402 seq,
403 _stream,
404 grammar_bitmask,
405 )
406 }
407
408 fn read_deferred_draft_token(&self) -> Result<u32> {
409 self.read_deferred_draft_token_dispatch()
410 }
411
412 fn trim_proposer_state(
413 &self,
414 seq: &mut SequenceState,
415 num_accepted: usize,
416 _stream: u64,
417 ) -> Result<()> {
418 self.trim_proposer_state_dispatch(seq, num_accepted, _stream)
419 }
420}