1pub fn mtp_ladder_disabled() -> bool {
31 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
32 *OFF.get_or_init(|| std::env::var_os("METRALE_NO_MTP_K_LADDER").is_some())
33}
34
35fn parse_ladder(value: &str) -> Option<Vec<(usize, usize)>> {
38 let mut steps = Vec::new();
39 for part in value.split(',') {
40 let (n, k) = part.trim().split_once(':')?;
41 steps.push((n.trim().parse().ok()?, k.trim().parse().ok()?));
42 }
43 if steps.is_empty() {
44 return None;
45 }
46 steps.sort_by_key(|&(n, _)| n);
47 Some(steps)
48}
49
50const DEFAULT_LADDER: [(usize, usize); 4] = [(4, 3), (8, 3), (16, 1), (32, 1)];
53
54static PUBLISHED_LADDER: std::sync::OnceLock<Vec<(usize, usize)>> = std::sync::OnceLock::new();
56
57fn mtp_ladder_steps() -> &'static [(usize, usize)] {
58 if let Some(published) = PUBLISHED_LADDER.get() {
59 return published;
60 }
61 static STEPS: std::sync::OnceLock<Vec<(usize, usize)>> = std::sync::OnceLock::new();
62 STEPS.get_or_init(|| {
63 std::env::var("METRALE_MTP_K_LADDER")
64 .ok()
65 .and_then(|value| parse_ladder(&value))
66 .unwrap_or_else(|| DEFAULT_LADDER.to_vec())
67 })
68}
69
70pub fn parse_mtp_k_ladder(value: &str) -> anyhow::Result<Vec<(usize, usize)>> {
73 let steps = parse_ladder(value).ok_or_else(|| {
74 anyhow::anyhow!("--mtp-k-ladder {value:?} is not a list of n_max:drafts steps")
75 })?;
76 anyhow::ensure!(
77 steps.iter().all(|&(n, k)| n >= 1 && k >= 1),
78 "--mtp-k-ladder {value:?}: widths and draft counts start at 1"
79 );
80 Ok(steps)
81}
82
83pub fn set_mtp_k_ladder(steps: Vec<(usize, usize)>) -> anyhow::Result<()> {
87 let got = PUBLISHED_LADDER.get_or_init(|| steps.clone());
88 anyhow::ensure!(
89 *got == steps,
90 "MTP K-ladder already published as {got:?}, refusing {steps:?}"
91 );
92 Ok(())
93}
94
95pub fn mtp_ladder_pinned() -> bool {
99 PUBLISHED_LADDER.get().is_some() || std::env::var_os("METRALE_MTP_K_LADDER").is_some()
100}
101
102pub fn ladder_drafts_from_steps(
105 steps: &[(usize, usize)],
106 n_active: usize,
107 num_drafts: usize,
108) -> usize {
109 if num_drafts == 0 {
110 return 0;
111 }
112 steps
113 .iter()
114 .find(|&&(n_max, _)| n_active <= n_max)
115 .or(steps.last())
116 .map(|&(_, k)| k.clamp(1, num_drafts))
117 .unwrap_or(num_drafts)
118}
119
120pub fn mtp_ladder_drafts(n_active: usize, num_drafts: usize) -> usize {
126 if num_drafts == 0 {
127 return 0;
128 }
129 if mtp_ladder_disabled() {
130 return num_drafts;
131 }
132 ladder_drafts_from_steps(mtp_ladder_steps(), n_active, num_drafts)
133}
134
135pub const DEFAULT_MTP_MAX_SEQS: usize = 32;
139
140pub const MAX_MTP_MAX_SEQS: usize = crate::layer::VERIFY_WY_TABLE_SEQS;
143
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub enum MtpMaxSeqsSource {
147 Cli,
148 ModelDefault,
149 EngineDefault,
150}
151
152pub fn resolve_mtp_max_seqs(
157 cli: Option<usize>,
158 model_default: u32,
159 ladder_disabled: bool,
160) -> anyhow::Result<(usize, MtpMaxSeqsSource)> {
161 let (n, source) = match (cli, model_default) {
162 (Some(n), _) => (n, MtpMaxSeqsSource::Cli),
163 (None, m) if m > 0 => (m as usize, MtpMaxSeqsSource::ModelDefault),
164 (None, _) => (
165 if ladder_disabled {
166 4
167 } else {
168 DEFAULT_MTP_MAX_SEQS
169 },
170 MtpMaxSeqsSource::EngineDefault,
171 ),
172 };
173 anyhow::ensure!(
174 (1..=MAX_MTP_MAX_SEQS).contains(&n),
175 "MTP dispatch cap {n} ({source:?}) is outside 1..={MAX_MTP_MAX_SEQS}"
176 );
177 Ok((n, source))
178}
179
180static PUBLISHED_MTP_MAX_SEQS: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
181
182pub fn set_mtp_max_seqs(n: usize) -> anyhow::Result<()> {
185 let got = *PUBLISHED_MTP_MAX_SEQS.get_or_init(|| n);
186 anyhow::ensure!(
187 got == n,
188 "MTP dispatch cap already published as {got}, refusing {n}"
189 );
190 Ok(())
191}
192
193pub fn mtp_max_seqs() -> usize {
202 PUBLISHED_MTP_MAX_SEQS
203 .get()
204 .copied()
205 .unwrap_or(if mtp_ladder_disabled() {
206 4
207 } else {
208 DEFAULT_MTP_MAX_SEQS
209 })
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
219 fn default_ladder_holds_depth_to_the_cap() {
220 assert_eq!(mtp_ladder_drafts(1, 3), 3);
221 assert_eq!(mtp_ladder_drafts(4, 3), 3);
222 assert_eq!(mtp_ladder_drafts(5, 3), 3);
223 assert_eq!(mtp_ladder_drafts(8, 3), 3);
224 assert_eq!(mtp_ladder_drafts(9, 3), 1);
225 assert_eq!(mtp_ladder_drafts(16, 3), 1);
226 assert_eq!(mtp_ladder_drafts(17, 3), 1);
227 assert_eq!(mtp_ladder_drafts(32, 3), 1);
228 assert_eq!(mtp_ladder_drafts(64, 3), 1);
229 assert_eq!(mtp_ladder_drafts(16, 1), 1);
230 }
231
232 #[test]
235 fn the_flag_parser_refuses_a_malformed_or_zero_ladder() {
236 assert_eq!(parse_mtp_k_ladder("4:1, 2:2").unwrap(), [(2, 2), (4, 1)]);
237 for bad in ["", "2", "2:x", "0:2", "2:0", "2:2,,4:1"] {
238 assert!(parse_mtp_k_ladder(bad).is_err(), "{bad:?} accepted");
239 }
240 }
241
242 #[test]
243 fn depth_at_width_env_rungs_parse_shape() {
244 let steps = parse_ladder("32:2, 4:3,8:3, 16:2,24:2").unwrap();
245 assert_eq!(steps, [(4, 3), (8, 3), (16, 2), (24, 2), (32, 2)]);
246 assert_eq!(ladder_drafts_from_steps(&steps, 17, 3), 2);
247 assert_eq!(ladder_drafts_from_steps(&steps, 24, 3), 2);
248 assert_eq!(ladder_drafts_from_steps(&steps, 25, 3), 2);
249 assert_eq!(ladder_drafts_from_steps(&steps, 32, 3), 2);
250 }
251
252 #[test]
255 fn mtp_max_seqs_resolution_order_and_bounds() {
256 use MtpMaxSeqsSource::*;
257 assert_eq!(resolve_mtp_max_seqs(Some(8), 128, false).unwrap(), (8, Cli));
258 assert_eq!(
259 resolve_mtp_max_seqs(None, 128, false).unwrap(),
260 (128, ModelDefault)
261 );
262 assert_eq!(
263 resolve_mtp_max_seqs(None, 0, false).unwrap(),
264 (32, EngineDefault)
265 );
266 assert_eq!(
267 resolve_mtp_max_seqs(None, 0, true).unwrap(),
268 (4, EngineDefault)
269 );
270 assert_eq!(
271 resolve_mtp_max_seqs(Some(32), 128, false).unwrap(),
272 (32, Cli)
273 );
274 assert!(resolve_mtp_max_seqs(Some(0), 128, false).is_err());
275 assert!(resolve_mtp_max_seqs(Some(129), 0, false).is_err());
276 assert!(resolve_mtp_max_seqs(None, 129, false).is_err());
277 assert_eq!(MAX_MTP_MAX_SEQS, 128);
278 }
279
280 #[test]
281 fn explicit_steps_are_honored() {
282 let steps = parse_ladder("4:3,8:2").unwrap();
283 assert_eq!(ladder_drafts_from_steps(&steps, 4, 3), 3);
284 assert_eq!(ladder_drafts_from_steps(&steps, 5, 3), 2);
285 assert_eq!(ladder_drafts_from_steps(&steps, 8, 3), 2);
286 assert_eq!(ladder_drafts_from_steps(&steps, 9, 3), 2);
287 }
288
289 #[test]
290 fn malformed_ladder_is_rejected_as_a_unit() {
291 assert_eq!(parse_ladder(""), None);
292 assert_eq!(parse_ladder("4:3,broken,8:2"), None);
293 assert_eq!(parse_ladder("4:three"), None);
294 }
295
296 #[test]
297 fn ladder_clamps_to_configured_ceiling() {
298 assert_eq!(mtp_ladder_drafts(2, 1), 1);
299 assert_eq!(mtp_ladder_drafts(2, 0), 0);
300 }
301}