Retire the Qwen AR pipeline

This commit is contained in:
Georg Bauer committed 2026-10-04 12:49:43 +02:00
1 parent 5773942c8f
commit 1de78fa3ef
22 files changed
+6429 -667

No files matched your search

+2
View File
@@ -29,6 +29,8 @@ mod hyper;
mod indexing;
#[cfg(test)]
mod issue103_analysis;
#[cfg(test)]
mod issue104;
mod linear;
mod load;
mod mlp;
+45 -123
View File
@@ -4,9 +4,9 @@
use super::array::{Array, Dtype};
use super::execution::Throttle;
use super::model::Output;
use super::sampling::{SamplerConfig, lazy_sample, sample_from_logits};
use super::ops;
use super::sampling::{SamplerConfig, sample_from_logits};
use super::stream::{Stream, Streams};
use super::{ops, random};
use crate::engine::Rng;
use std::time::Instant;
@@ -15,36 +15,21 @@ pub(super) struct Options<'a> {
pub(super) max_tokens: usize,
pub(super) seed: u64,
pub(super) stop_tokens: &'a [i64],
pub(super) pipeline: bool,
pub(super) sync_eval: bool,
pub(super) return_hidden: bool,
pub(super) capture_final_state: bool,
}
impl Options<'_> {
pub(super) fn pipeline_eligible(&self, sampler: &SamplerConfig) -> bool {
self.pipeline
&& sampler.temperature > 0.0
&& sampler.top_k > 1
&& sampler.top_k < 4096
&& sampler.presence_penalty == 0.0
&& sampler.frequency_penalty == 0.0
&& self.max_tokens > 2
}
}
#[derive(Default)]
pub(super) struct Decoded {
pub(super) tokens: Vec<i64>,
pub(super) final_logits: Option<Array>,
pub(super) final_state_error: Option<String>,
pub(super) pipeline: bool,
pub(super) verify_calls: usize,
pub(super) forward_seconds: f64,
pub(super) eval_seconds: f64,
pub(super) decode_seconds: f64,
pub(super) elapsed_seconds: f64,
cached_final_row: Option<Array>,
}
pub(super) fn last_row(logits: &Array, stream: Stream) -> Result<Array, String> {
@@ -65,14 +50,12 @@ fn token_array(token: i64) -> Result<Array, String> {
)
}
/// The model owner enables/disables pipeline mode around this call, including
/// errors from the callback. Forward consumes resident token arrays unchanged.
/// Every token uses the request PCG64 stream, matching classic generate_ar.
#[allow(clippy::too_many_arguments)]
pub(super) fn decode(
initial_logits: &Array,
sampler: &SamplerConfig,
options: &Options<'_>,
pipeline_ready: bool,
streams: &Streams,
stream: Stream,
throttle: &mut Throttle,
@@ -83,7 +66,6 @@ pub(super) fn decode(
let mut result = Decoded::default();
let mut rng = Rng::new_qwen(options.seed);
let mut logits = initial_logits.clone();
let mut final_row = None;
let stopped = |token: i64| options.stop_tokens.contains(&token);
let mut commit = |tokens: &mut Vec<i64>, token| -> Result<(), String> {
tokens.push(token);
@@ -92,101 +74,50 @@ pub(super) fn decode(
}
Ok(())
};
if options.pipeline_eligible(sampler) && pipeline_ready {
result.pipeline = true;
let mut key = random::key(options.seed & 0x7fff_ffff)?;
let mut counts = Vec::<(i64, i64)>::new();
for step in 0..options.max_tokens {
let row = ops::reshape(&logits, &[-1], stream)?;
let token = sample_from_logits(&row, sampler, &mut rng, &[], &[], streams, stream)?.0;
if !options.sync_eval && step > 0 {
let started = Instant::now();
ops::evaluate(streams, std::slice::from_ref(&row), stream, false)?;
let elapsed = started.elapsed().as_secs_f64();
result.eval_seconds += elapsed;
result.decode_seconds += elapsed;
}
let token = sample_from_logits(&row, sampler, &mut rng, &counts, &[], streams, stream)?.0;
commit(&mut result.tokens, token)?;
if !stopped(token) {
let mut lane_step = |token: &Array| -> Result<(Array, Array), String> {
let (next, sub) = random::split(&key, stream)?;
key = next;
let out = forward(&ops::reshape(token, &[1, 1], stream)?, false)?;
let row = last_row(
out.logits.as_ref().ok_or("AR forward omitted logits")?,
stream,
)?;
let next = lazy_sample(&ops::reshape(&row, &[-1], stream)?, sampler, &sub, stream)?;
Ok((row, next))
};
let started = Instant::now();
let first_power_started = throttle.begin();
let (mut _row_lazy, mut token_lazy) = lane_step(&token_array(token)?)?;
ops::evaluate(streams, std::slice::from_ref(&token_lazy), stream, true)?;
result.forward_seconds += started.elapsed().as_secs_f64();
throttle.pause(streams, stream, first_power_started)?;
loop {
let power_started = throttle.begin();
let built = Instant::now();
let (row_next, token_next) = lane_step(&token_lazy)?;
ops::evaluate(streams, std::slice::from_ref(&token_next), stream, true)?;
let build_elapsed = built.elapsed().as_secs_f64();
let waited = Instant::now();
let token = ops::read_i64(&token_lazy, streams, stream)?[0];
let wait_elapsed = waited.elapsed().as_secs_f64();
result.forward_seconds += build_elapsed;
result.eval_seconds += wait_elapsed;
result.decode_seconds += build_elapsed + wait_elapsed;
result.verify_calls += 1;
throttle.pause(streams, stream, power_started)?;
commit(&mut result.tokens, token)?;
if stopped(token) || result.tokens.len() >= options.max_tokens {
final_row = Some(row_next);
break;
}
_row_lazy = row_next;
token_lazy = token_next;
if sampler.presence_penalty != 0.0 || sampler.frequency_penalty != 0.0 {
if let Some((_, count)) = counts.iter_mut().find(|(id, _)| *id == token) {
*count += 1;
} else {
counts.push((token, 1));
}
}
} else {
let mut counts = Vec::<(i64, i64)>::new();
for step in 0..options.max_tokens {
let row = ops::reshape(&logits, &[-1], stream)?;
if !options.sync_eval && step > 0 {
let started = Instant::now();
ops::evaluate(streams, std::slice::from_ref(&row), stream, false)?;
let elapsed = started.elapsed().as_secs_f64();
result.eval_seconds += elapsed;
result.decode_seconds += elapsed;
}
let token =
sample_from_logits(&row, sampler, &mut rng, &counts, &[], streams, stream)?.0;
commit(&mut result.tokens, token)?;
if sampler.presence_penalty != 0.0 || sampler.frequency_penalty != 0.0 {
if let Some((_, count)) = counts.iter_mut().find(|(id, _)| *id == token) {
*count += 1;
} else {
counts.push((token, 1));
}
}
if step + 1 >= options.max_tokens || stopped(token) {
break;
}
let started = Instant::now();
let power_started = throttle.begin();
let output = forward(&token_array(token)?, options.return_hidden)?;
result.forward_seconds += started.elapsed().as_secs_f64();
let eval_started = Instant::now();
let next = output.logits.ok_or("AR forward omitted logits")?;
let mut roots = vec![next.clone()];
roots.extend(output.hidden);
ops::evaluate(streams, &roots, stream, !options.sync_eval)?;
result.eval_seconds += eval_started.elapsed().as_secs_f64();
result.decode_seconds += started.elapsed().as_secs_f64();
result.verify_calls += 1;
logits = last_row(&next, stream)?;
throttle.pause(streams, stream, power_started)?;
if step + 1 >= options.max_tokens || stopped(token) {
break;
}
let started = Instant::now();
let power_started = throttle.begin();
let output = forward(&token_array(token)?, options.return_hidden)?;
result.forward_seconds += started.elapsed().as_secs_f64();
let eval_started = Instant::now();
let next = output.logits.ok_or("AR forward omitted logits")?;
let mut roots = vec![next.clone()];
roots.extend(output.hidden);
ops::evaluate(streams, &roots, stream, !options.sync_eval)?;
result.eval_seconds += eval_started.elapsed().as_secs_f64();
result.decode_seconds += started.elapsed().as_secs_f64();
result.verify_calls += 1;
logits = last_row(&next, stream)?;
throttle.pause(streams, stream, power_started)?;
}
// Match generate_ar: response timing ends before session-bank tail capture.
result.elapsed_seconds = started_all.elapsed().as_secs_f64();
result.cached_final_row = final_row;
Ok(result)
}
/// Called after the owner restores non-pipeline mode, as in generate_ar's
/// finally block. Capture failures preserve a successfully finished response.
/// Extend the cache through the last sampled token, as in generate_ar.
/// Capture failures preserve a successfully finished response.
pub(super) fn capture_final_state(
result: &mut Decoded,
options: &Options<'_>,
@@ -194,20 +125,16 @@ pub(super) fn capture_final_state(
stream: Stream,
mut forward: impl FnMut(&Array, bool) -> Result<Output, String>,
) {
let final_row = result.cached_final_row.take();
if options.capture_final_state
&& let Some(&last) = result.tokens.last()
{
let capture = (|| {
let row = match final_row {
Some(row) => row,
None => last_row(
&forward(&token_array(last)?, false)?
.logits
.ok_or("AR tail omitted logits")?,
stream,
)?,
};
let row = last_row(
&forward(&token_array(last)?, false)?
.logits
.ok_or("AR tail omitted logits")?,
stream,
)?;
ops::evaluate(streams, std::slice::from_ref(&row), stream, false)?;
Ok::<_, String>(row)
})();
@@ -235,11 +162,13 @@ fn mtplx_ar_driver_matches_reference() {
.lines()
.map(|line| serde_json::from_str::<serde_json::Value>(line).unwrap())
.collect::<Vec<_>>();
assert_eq!(cases.len(), 480);
assert_eq!(cases.len(), 1120);
for (index, case) in cases.iter().enumerate() {
eprintln!("AR driver reference {index}/{}", cases.len());
let sampler = SamplerConfig {
temperature: case["temperature"].as_f64().unwrap(),
top_p: case["top_p"].as_f64().unwrap(),
top_k: case["top_k"].as_i64().unwrap() as i32,
presence_penalty: case["penalty"].as_f64().unwrap(),
..Default::default()
};
@@ -253,7 +182,6 @@ fn mtplx_ar_driver_matches_reference() {
max_tokens: case["limit"].as_u64().unwrap() as usize,
seed: 4294967297,
stop_tokens: &stops,
pipeline: case["pipeline"].as_bool().unwrap(),
sync_eval: !case["asynchronous"].as_bool().unwrap(),
return_hidden: false,
capture_final_state: case["capture"].as_bool().unwrap(),
@@ -278,7 +206,6 @@ fn mtplx_ar_driver_matches_reference() {
&row,
&sampler,
&options,
true,
&streams,
stream,
&mut Throttle::new(100).unwrap(),
@@ -327,11 +254,6 @@ fn mtplx_ar_driver_matches_reference() {
case["final"],
"final cache row case {index}"
);
assert_eq!(
result.pipeline,
!case["modes"].as_array().unwrap().is_empty(),
"pipeline admission case {index}"
);
assert_eq!(
serde_json::json!(result.verify_calls),
case["verify_calls"],
-4
View File
@@ -674,10 +674,6 @@ fn mtplx_installed_module_binding_matches_reference() {
);
}
assert_eq!(serde_json::json!(tables), sidecar_case["tables"]);
assert_eq!(
model.set_ar_pipeline_mode(true),
sidecar_case["ar_ready"].as_bool().unwrap()
);
drop(model);
assert!(
bank.materialize(&["missing.weight".into()], &streams, gpu)
-1
View File
@@ -532,7 +532,6 @@ fn mtplx_decoder_connected_attention_and_moe_advance_caches() {
CompilePolicy::from_values(Some("1"), None),
)
.unwrap();
assert_eq!(model.set_ar_pipeline_mode(true), !with_ple);
let mut caches = model.make_cache(4);
let mut offset = 0;
for (rows, keep, emit, capture) in [
+1 -13
View File
@@ -827,15 +827,10 @@ impl Execution {
finish_decode: impl FnOnce(f64) -> Result<(), String>,
) -> Result<ar::Decoded, String> {
let graph_options = self.graph_options(false, false)?;
let lane = options.pipeline_eligible(sampler)
&& self
.model
.with_dependent_mut(|_, model| model.set_ar_pipeline_mode(true));
let mut result = ar::decode(
logits,
sampler,
options,
lane,
&self.streams,
self.stream,
&mut self.decode_throttle,
@@ -859,10 +854,6 @@ impl Execution {
},
emit,
);
if lane {
self.model
.with_dependent_mut(|_, model| model.set_ar_pipeline_mode(false));
}
if let Ok(decoded) = &mut result {
finish_decode(decoded.elapsed_seconds)?;
ar::capture_final_state(
@@ -1185,7 +1176,6 @@ fn mtplx_full_installed_ar_matches_reference() {
max_tokens: usize::MAX,
seed: end["seed"].as_u64().unwrap(),
stop_tokens: &stops,
pipeline: flag("MTPLX_AR_PIPELINE"),
sync_eval: !flag("MTPLX_ASYNC_AR")
|| !std::env::var("MTPLX_EVAL_AUDIT")
.unwrap_or_default()
@@ -1222,7 +1212,7 @@ fn mtplx_full_installed_ar_matches_reference() {
println!(
"{}",
serde_json::json!({"event":"canonical_ar_end", "tokens":result.tokens, "verify_calls":result.verify_calls,
"decode_elapsed_seconds":result.elapsed_seconds, "prefill_seconds":prepared.eval_seconds, "finish_reason":"stop", "pipeline":result.pipeline})
"decode_elapsed_seconds":result.elapsed_seconds, "prefill_seconds":prepared.eval_seconds, "finish_reason":"stop"})
);
phase.store(EvaluationPhase::Finishing as u8, Ordering::Relaxed);
}));
@@ -1461,7 +1451,6 @@ fn mtplx_full_installed_ping_forward_matches_reference() {
max_tokens: usize::MAX,
seed: 0,
stop_tokens: &stops,
pipeline: false,
sync_eval: true,
return_hidden: false,
capture_final_state: false,
@@ -1486,7 +1475,6 @@ fn mtplx_full_installed_ping_forward_matches_reference() {
emitted.push(token as i64);
assert_eq!(ar_result.tokens, emitted);
assert_eq!(ar_result.verify_calls, generated.len());
assert!(!ar_result.pipeline);
} else {
assert!(
!receipts.iter().any(|r| r["event"] == "decode_reference"),
+224
View File
@@ -0,0 +1,224 @@
//! Full installed-model AR regression; run serially under test-supervisor.
use super::ar;
use super::array::Array;
use super::execution::Execution;
use super::sampling::SamplerConfig;
use super::session_cache::{DecodeOptions, SessionSnapshot, State, TurnOptions};
fn fingerprint(execution: &Execution, array: &Array) -> serde_json::Value {
let values = execution.values_f32(array).unwrap();
assert!(values.iter().all(|v| v.is_finite()));
serde_json::json!({"shape":array.layout().shape(), "sha256":
super::super::qwen_mtplx_tests::hex_digest(&values.iter().flat_map(|v|v.to_le_bytes()).collect::<Vec<_>>())})
}
fn state(execution: &Execution, saved: &SessionSnapshot) -> serde_json::Value {
let states = saved
.trunk
.iter()
.map(|s| {
s.as_ref().map(|s| {
let arrays = match s {
State::Recurrent(arrays) => arrays.as_slice(),
State::Qsa(arrays) => arrays.as_slice(),
};
arrays
.iter()
.map(|a| a.as_ref().map(|a| fingerprint(execution, a)))
.collect::<Vec<_>>()
})
})
.collect::<Vec<_>>();
serde_json::json!({"ids":saved.token_ids,"logits":fingerprint(execution,&saved.logits),"states":states})
}
#[test]
#[ignore = "full pinned Bare-Speed model; external memory guard required"]
fn issue104_installed_ar_stream_and_state() {
let root = std::path::PathBuf::from(std::env::var_os("DS4_QWEN38_ARTIFACTS").unwrap());
eprintln!(
"#104 loading unchanged Bare-Speed model; resident={}",
super::ngram::resident_policy()
);
let mut execution = Execution::load(&root, false, false, 100).unwrap();
let tables = execution
.model
.borrow_dependent()
.layers
.iter()
.filter_map(|layer| {
layer
.ple
.as_ref()
.map(|ple| ple.embedding.table.resident.is_some())
})
.collect::<Vec<_>>();
assert!(!tables.is_empty());
assert!(
tables.iter().all(|r| *r == super::ngram::resident_policy()),
"full residency must actually attach"
);
println!(
"{}",
serde_json::json!({"event":"issue104_environment","resident_tables":tables,"mtp":false})
);
let ids = [9707, 11, 1075];
let prepared = execution
.prefill_prompt(
&ids,
&super::prefill::Options {
chunk_size: Some(2048),
final_logits_only: true,
external_cache_only: false,
external_emit_logits: true,
return_hidden: false,
},
|_| Ok(()),
)
.unwrap();
let baseline = execution
.capture_session(&ids, &prepared.logits, None, false, false)
.unwrap();
let mut records = Vec::new();
let mut warm_records = Vec::<serde_json::Value>::new();
for temperature in [0.6, 1.0] {
let sampler = SamplerConfig {
temperature,
..Default::default()
};
let mut expected = None;
for asynchronous in [false, true] {
let (logits, _, reused) = execution.restore_session(&baseline, &ids).unwrap();
assert_eq!(reused, ids.len());
let mut emitted = Vec::new();
let out = execution
.decode_ar(
&logits,
&sampler,
&ar::Options {
max_tokens: 8,
seed: 0,
stop_tokens: &[],
sync_eval: !asynchronous,
return_hidden: false,
capture_final_state: true,
},
|id| {
emitted.push(id);
eprintln!("#104 t={temperature} async={asynchronous}: token {id}");
Ok(())
},
|_| Ok(()),
)
.unwrap();
assert!(out.final_state_error.is_none());
for layer in &execution.model.borrow_dependent().layers {
if let Some(ple) = &layer.ple {
assert!(!ple.embedding.stage_disabled);
assert!(!ple.embedding.table.prefer_lazy);
}
}
assert_eq!(out.tokens, emitted);
let mut key = ids.to_vec();
key.extend(out.tokens.iter().map(|id| *id as i32));
let saved = execution
.capture_session(&key, out.final_logits.as_ref().unwrap(), None, false, false)
.unwrap();
let value = state(&execution, &saved);
if !asynchronous {
expected = Some(value.clone());
}
println!(
"{}",
serde_json::json!({"event":"issue104_ar","temperature":temperature,"asynchronous":asynchronous,"tokens":out.tokens,"state":value,"seconds":out.elapsed_seconds})
);
assert_eq!(value, expected.clone().unwrap());
let mut prompt = key.clone();
prompt.push(1075);
let options = TurnOptions {
prefill: super::prefill::Options {
chunk_size: Some(2048),
final_logits_only: true,
external_cache_only: false,
external_emit_logits: true,
return_hidden: false,
},
decode: DecodeOptions::Ar(ar::Options {
max_tokens: 8,
seed: 0,
stop_tokens: &[],
sync_eval: !asynchronous,
return_hidden: false,
capture_final_state: true,
}),
sampler,
draft_sampler: sampler,
lazy_kv: false,
store_min_suffix: None,
fused_max: 512,
};
// Cancel after forwards have advanced the live cache, then retry
// from the committed bank entry. The saved state must survive.
let mut callbacks = 0;
let cancelled = execution.session_turn(
&prompt,
Some(&saved),
&options,
|_| Ok(()),
|_| {
callbacks += 1;
if callbacks == 3 {
Err("cancelled".into())
} else {
Ok(())
}
},
|_| Ok(()),
);
assert!(matches!(cancelled, Err(ref error) if error == "cancelled"));
assert_eq!(state(&execution, &saved), value);
let warm = execution
.session_turn(
&prompt,
Some(&saved),
&options,
|_| Ok(()),
|_| Ok(()),
|_| Ok(()),
)
.unwrap();
assert_eq!(warm.cached_tokens, key.len());
assert!(warm.final_snapshot_error.is_none());
let warm_state = state(&execution, warm.snapshot.as_ref().unwrap());
println!(
"{}",
serde_json::json!({"event":"issue104_warm", "temperature":temperature,
"asynchronous":asynchronous,"state":warm_state})
);
if asynchronous {
assert_eq!(warm_state, warm_records.last().unwrap()["state"]);
} else {
warm_records
.push(serde_json::json!({"temperature":temperature,"state":warm_state}));
}
if !asynchronous {
records.push(serde_json::json!({"temperature":temperature,"state":value}));
}
}
}
if let Ok(path) = std::env::var("DS4_ISSUE104_BASELINE") {
let expected: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(path).unwrap()).unwrap();
assert_eq!(serde_json::json!(records), expected);
}
let oracle: serde_json::Value = serde_json::from_str(include_str!(
"../../../../tests/fixtures/issue104-ar-model.json"
))
.unwrap();
assert_eq!(serde_json::json!(records), oracle["cold"]);
assert_eq!(serde_json::json!(warm_records), oracle["warm"]);
println!(
"{}",
serde_json::json!({"event":"issue104_classic_baseline","records":records})
);
}
+3 -25
View File
@@ -14,7 +14,6 @@ use std::ops::Range;
pub(super) struct CompilePolicy {
explicit_off: bool,
environment: bool,
lane: bool,
}
impl CompilePolicy {
@@ -29,7 +28,6 @@ impl CompilePolicy {
Self {
explicit_off,
environment: !explicit_off && [gdn, family].into_iter().flatten().any(truthy),
lane: false,
}
}
@@ -42,7 +40,7 @@ impl CompilePolicy {
}
fn enabled(self) -> bool {
!self.explicit_off && (self.environment || self.lane)
!self.explicit_off && self.environment
}
}
@@ -305,23 +303,6 @@ impl<'a> TextModel<'a> {
Ok(())
}
pub(super) fn set_ar_pipeline_mode(&mut self, enabled: bool) -> bool {
let mut ready = true;
for layer in &mut self.layers {
let Some(ple) = &mut layer.ple else { continue };
if enabled && ple.embedding.table.resident.is_none() {
ready = false;
continue;
}
ple.embedding.stage_disabled = enabled;
ple.embedding.table.prefer_lazy = enabled;
}
if ready {
self.policy.lane = enabled && !self.policy.explicit_off;
}
ready
}
#[cfg_attr(not(test), expect(dead_code, reason = "Reference diagnostic API"))]
pub(super) fn clear_verify_capture(cache: &mut [Option<Cache>]) {
for cache in cache.iter_mut().flatten() {
@@ -777,19 +758,16 @@ fn mtplx_model_compile_policy_and_run_boundaries() {
assert!(CompilePolicy::from_values(Some(on), None).enabled());
assert!(CompilePolicy::from_values(None, Some(on)).enabled());
for off in ["0", "", "false", "off", "anything"] {
for mut policy in [
for policy in [
CompilePolicy::from_values(Some(on), Some(off)),
CompilePolicy::from_values(Some(off), Some(on)),
] {
policy.lane = true;
assert!(!policy.enabled());
}
}
}
let mut automatic = CompilePolicy::from_values(None, None);
let automatic = CompilePolicy::from_values(None, None);
assert!(!automatic.enabled());
automatic.lane = true;
assert!(automatic.enabled());
let environment = CompilePolicy::from_env();
assert_eq!(
environment.enabled(),
+1 -1
View File
@@ -127,7 +127,7 @@ impl Table {
self.resident = Some(resident);
self.prefer_lazy = false;
println!(
"[qwen4_exp] ngram table resident: {:.1}G in {:.1}s (pipelined-AR lane armed)",
"[qwen4_exp] ngram table resident: {:.1}G in {:.1}s",
nbytes as f64 / (1u64 << 30) as f64,
started.elapsed().as_secs_f64()
);
+53 -3
View File
@@ -53,7 +53,6 @@ pub(super) fn server_overrides(mtp: bool, get: impl Fn(&str) -> Option<String>)
result.insert("MTPLX_SKIP_VERIFY_SNAPSHOT", "0");
}
for key in [
"MTPLX_AR_PIPELINE",
"MTPLX_FAMILY_CAPTURE_COMMIT",
"MTPLX_FUSED_HC_V3",
"MTPLX_FUSED_GDN_INPROJ",
@@ -137,7 +136,6 @@ pub(super) fn turn_options(
max_tokens: usize::MAX,
seed: 0,
stop_tokens: &[],
pipeline: flag("MTPLX_AR_PIPELINE"),
sync_eval: !flag("MTPLX_ASYNC_AR")
|| get("MTPLX_EVAL_AUDIT").is_some_and(|s| !s.is_empty()),
return_hidden: false,
@@ -182,6 +180,42 @@ pub(super) fn turn_options(
))
}
#[test]
fn retired_ar_pipeline_flag_cannot_change_server_or_turn_policy() {
let mut settings = crate::settings::effective_settings(
crate::model::ModelChoice::Qwen38FlashNext,
&crate::settings::GenerationPreferences::default(),
&crate::settings::RuntimePreferences::default(),
std::path::Path::new("/unused-policy-models"),
)
.unwrap()
.engine;
settings.speculative.glm_mtp = false;
for legacy in [None, Some("0"), Some("1")] {
let lookup = |key: &str| {
(key == "MTPLX_AR_PIPELINE")
.then_some(legacy)
.flatten()
.map(str::to_owned)
};
let overrides = server_overrides(false, lookup);
assert_eq!(overrides, server_overrides(false, |_| None));
let (options, _) = turn_options(&settings, |key| {
assert_ne!(key, "MTPLX_AR_PIPELINE", "retired option was read");
overrides
.get(key)
.map(|s| (*s).to_owned())
.or_else(|| lookup(key))
})
.unwrap();
let DecodeOptions::Ar(ar) = options.decode else {
panic!("AR request changed modes");
};
assert!(ar.sync_eval && ar.capture_final_state);
assert!(!ar.return_hidden);
}
}
#[test]
#[ignore = "requires DS4_QWEN38_POLICY_REFERENCE from pinned MTPLX; CPU-only"]
fn qwen_server_policy_matches_original_functions() {
@@ -198,6 +232,23 @@ fn qwen_server_policy_matches_original_functions() {
let mtp = case["mtp"].as_bool().unwrap();
let lookup = |key: &str| case["environment"][key].as_str().map(str::to_owned);
let overrides = server_overrides(mtp, lookup);
if case["server_only"] == true {
// This scoped comparison owns AR retirement and the existing
// defaults. New upstream features are outside #104; retain the
// complete legacy policy comparison below for its own oracle.
assert!(
!case["overrides"]
.as_object()
.unwrap()
.contains_key("MTPLX_AR_PIPELINE")
);
assert!(!overrides.contains_key("MTPLX_AR_PIPELINE"));
for (key, value) in &overrides {
assert_eq!(case["overrides"][key], *value, "default {key} case {count}");
}
count += 1;
continue;
}
assert_eq!(
serde_json::to_value(&overrides).unwrap(),
case["overrides"],
@@ -253,7 +304,6 @@ fn qwen_server_policy_matches_original_functions() {
match actual.decode {
DecodeOptions::Ar(o) => {
assert!(!mtp);
assert_eq!(o.pipeline, expected["pipeline"]);
assert_eq!(o.sync_eval, expected["sync_eval"]);
}
DecodeOptions::Mtp(o) => {
+1
View File
@@ -143,6 +143,7 @@ pub(super) fn scalar(value: f32) -> Result<Array, String> {
/// categorical(sv[None], key): the pinned runtime selects inverse CDF for
/// one distribution with output shape [1], not the Gumbel-max route.
#[cfg(test)]
pub(super) fn categorical_row(
logits: &Array,
key: &Array,
-1
View File
@@ -284,7 +284,6 @@ fn qwen_request_settings_override_backend_policy() {
max_tokens: 1,
seed: 1,
stop_tokens: &[],
pipeline: true,
sync_eval: true,
return_hidden: false,
capture_final_state: true,
+3 -2
View File
@@ -150,7 +150,9 @@ fn order_bounded_support(
/// generation._mx_lazy_sample. Keep selection in the original model dtype,
/// promote only the selected k logits, and return an unevaluated scalar token.
/// The AR lane owns key splitting; neither this graph nor its RNG reads back.
/// Historical diagnostic only: reproduces #104 and the keyed RNG fixtures.
/// Production AR uses sample_from_logits for every token.
#[cfg(test)]
pub(super) fn lazy_sample(
row: &Array,
config: &SamplerConfig,
@@ -299,7 +301,6 @@ pub(super) fn distribution_from_logits(
/// The ordinary generation._sample_from_logits path. Greedy evaluates model
/// logits before constructing/evaluating ArgMax and never advances PCG64.
/// The resident pipelined keyed-device sampler is a separate reference lane.
pub(super) fn sample_from_logits(
logits: &Array,
config: &SamplerConfig,