metrale_model_arch/glm5next_mtp_head/
proposer.rs1use super::*;
11
12impl DraftProposer for Glm5NextMtpHead {
13 fn prefill_hidden_rows(&self, max_seq_len: usize) -> usize {
16 max_seq_len.min(self.max_seq_len)
17 }
18
19 fn alloc_state(&self, gpu: &dyn GpuBackend) -> Result<Box<dyn ProposerState>> {
20 let dsa = match &self.module.layer.mixer {
21 crate::glm5next_layer::Glm5NextMixer::Dsa(l) => Glm5NextDsaState::alloc(gpu, &l.cfg)?,
22 _ => bail!("GLM MTP block is not a DSA layer"),
23 };
24 let h = self.hidden;
25 let blocks = (self.max_seq_len / 16 + 2) as u32;
28 Ok(Box::new(Glm5NextMtpProposerState {
29 dsa,
30 seq_len: 0,
31 block_table: (0..blocks).collect(),
32 last_drafted: 0,
33 concat: gpu.alloc(2 * h * 2)?,
34 x: gpu.alloc(h * 2)?,
35 logits: gpu.alloc(self.vocab * 2)?,
36 arg: gpu.alloc(4)?,
37 head_xchg: gpu.alloc(16)?,
38 released: false,
39 }))
40 }
41
42 fn free_state(&self, gpu: &dyn GpuBackend, state: &mut dyn ProposerState) -> Result<()> {
46 let st = state
47 .as_any_mut()
48 .downcast_mut::<Glm5NextMtpProposerState>()
49 .ok_or_else(|| anyhow::anyhow!("Invalid GLM MTP proposer state"))?;
50 if st.released {
51 return Ok(());
52 }
53 st.released = true;
54 st.dsa.free(gpu)?;
55 for p in [st.concat, st.x, st.logits, st.arg, st.head_xchg] {
56 gpu.free(p)?;
57 }
58 st.block_table.clear();
62 st.seq_len = 0;
63 Ok(())
64 }
65
66 fn needs_comm(&self) -> bool {
70 metrale_model_layers::speculative::mtp_ep_propose_enabled()
71 }
72
73 fn prefill_uses_shared_buffers(&self) -> bool {
75 true
76 }
77
78 fn drafter_rows(&self, state: &mut dyn ProposerState) -> usize {
79 state
80 .as_any_mut()
81 .downcast_mut::<Glm5NextMtpProposerState>()
82 .map_or(0, |st| st.seq_len)
83 }
84
85 fn last_pair_key(&self, state: &mut dyn ProposerState) -> Option<usize> {
87 state
88 .as_any_mut()
89 .downcast_mut::<Glm5NextMtpProposerState>()
90 .and_then(|st| st.seq_len.checked_sub(1))
91 }
92
93 fn prefill_drafter(
94 &self,
95 prompt_tokens: &[u32],
96 hiddens: DevicePtr,
97 state: &mut dyn ProposerState,
98 ctx: &ForwardContext,
99 stream: u64,
100 ) -> Result<usize> {
101 let t0 = std::time::Instant::now();
102 let rows = self.rows_impl(prompt_tokens, hiddens, 0, state, ctx, stream)?;
103 if rows > 0 {
106 tracing::info!(
107 "GLM MTP drafter prefill: {rows} rows ({} prompt tokens) in {:.1} ms",
108 prompt_tokens.len(),
109 t0.elapsed().as_secs_f64() * 1e3,
110 );
111 }
112 Ok(rows)
113 }
114
115 fn catchup_drafter(
118 &self,
119 tokens: &[u32],
120 hiddens: DevicePtr,
121 row_base: usize,
122 _pos_base: usize,
123 state: &mut dyn ProposerState,
124 ctx: &ForwardContext,
125 stream: u64,
126 ) -> Result<usize> {
127 self.rows_impl(tokens, hiddens, row_base, state, ctx, stream)
128 }
129
130 #[allow(clippy::too_many_arguments)]
131 fn propose(
132 &self,
133 last_token: u32,
134 target_hidden: DevicePtr,
135 position: usize,
136 num_drafts: usize,
137 state: &mut dyn ProposerState,
138 ctx: &ForwardContext,
139 stream: u64,
140 _draft_embed_target: Option<DevicePtr>,
141 _grammar_bitmask: Option<&[i32]>,
142 _target_hidden_stack: Option<DevicePtr>,
143 ) -> Result<Vec<u32>> {
144 let st = state
145 .as_any_mut()
146 .downcast_mut::<Glm5NextMtpProposerState>()
147 .ok_or_else(|| anyhow::anyhow!("not a GLM MTP proposer state"))?;
148 if st.seq_len > position {
151 st.dsa.rewind_to(position)?;
152 st.seq_len = position;
153 }
154 if metrale_model_layers::speculative::mtp_refeed_debug() {
155 let fp = metrale_model_layers::speculative::hidden_fingerprint(
156 ctx.gpu,
157 target_hidden,
158 self.hidden,
159 );
160 tracing::info!(
161 "GLM_MTP_DBG propose position={position} drafter_rows={} tok={last_token} \
162 fp_target={fp:016x}",
163 st.seq_len,
164 );
165 }
166 let mut drafts = Vec::with_capacity(num_drafts);
167 let mut token = last_token;
168 let mut hidden = target_hidden;
169 for i in 0..num_drafts {
170 let d = self.forward_one(token, hidden, position + i, st, ctx, stream)?;
171 drafts.push(d);
172 token = d;
173 hidden = st.x;
176 }
177 st.last_drafted = drafts.len();
178 Ok(drafts)
179 }
180
181 fn after_verify(
182 &self,
183 num_accepted: usize,
184 state: &mut dyn ProposerState,
185 _stream: u64,
186 ) -> Result<()> {
187 let st = state
188 .as_any_mut()
189 .downcast_mut::<Glm5NextMtpProposerState>()
190 .ok_or_else(|| anyhow::anyhow!("not a GLM MTP proposer state"))?;
191 let keep = st.last_drafted.min(num_accepted + 1);
200 let trim = st.last_drafted - keep;
201 if trim > 0 {
202 st.seq_len = st.seq_len.saturating_sub(trim);
203 st.dsa.rewind_to(st.seq_len)?;
204 }
205 Ok(())
206 }
207}