use foundation::SteeringMode;
use model_io::{ArchConfig, SteeringSet};
use crate::real_forward_types::RealForwardError;
#[derive(Debug, Clone)]
pub struct SteeringVector {
pub set: SteeringSet,
pub mode: SteeringMode,
pub alpha: f32,
}
#[derive(Debug, Clone, Default)]
pub struct SteeringPolicy {
pub vectors: Vec<SteeringVector>,
pub target: f32,
pub gate_threshold: f32,
}
impl SteeringPolicy {
pub fn off() -> Self {
Self {
vectors: Vec::new(),
target: 0.0,
gate_threshold: 0.0,
}
}
pub fn single(
set: SteeringSet,
mode: SteeringMode,
alpha: f32,
target: f32,
gate_threshold: f32,
) -> Self {
Self {
vectors: vec![SteeringVector { set, mode, alpha }],
target,
gate_threshold,
}
}
pub fn is_active(&self) -> bool {
!self.vectors.is_empty()
}
pub fn primary(&self) -> Option<&SteeringVector> {
self.vectors.first()
}
pub(crate) fn vector_count(&self) -> usize {
self.vectors.len().max(1)
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct LayerSteer {
pub(crate) offset: u64,
pub(crate) inv_norm: f32,
pub(crate) mode: SteeringMode,
pub(crate) alpha: f32,
}
pub const MAX_STEER_ROWS: usize = gpu::MAX_BATCH_ROWS;
pub(crate) struct SteeringState {
pub(crate) directions: gpu::MetalBuffer,
pub(crate) coeff: gpu::MetalBuffer,
pub(crate) layers: Vec<Vec<Option<LayerSteer>>>,
pub(crate) coeff_stride: usize,
pub(crate) target: f32,
pub(crate) gate_threshold: f32,
}
impl SteeringState {
pub(crate) fn build(
context: &gpu::MetalContext,
arch: &ArchConfig,
policy: &SteeringPolicy,
) -> Result<Option<Self>, RealForwardError> {
if policy.vectors.is_empty() {
return Ok(None);
}
if !policy.target.is_finite() || !policy.gate_threshold.is_finite() {
return Err(RealForwardError::Unsupported(
"steering parameters must be finite; a non-finite target or gate \
puts a NaN into the residual stream, which reads as a PERFECT score on \
every rank instrument downstream"
.to_string(),
));
}
for (k, vector) in policy.vectors.iter().enumerate() {
if !vector.alpha.is_finite() {
return Err(RealForwardError::Unsupported(format!(
"steering vector {k}'s alpha must be finite; a non-finite alpha \
puts a NaN into the residual stream, which reads as a PERFECT \
score on every rank instrument downstream"
)));
}
vector.set.validate(arch).map_err(|e| {
RealForwardError::Unsupported(format!("steering vector {k}: {e:?}"))
})?;
}
let num_layers = arch.num_layers as usize;
let stride = policy.vector_count();
let mut packed: Vec<u8> = Vec::new();
let mut layers: Vec<Vec<Option<LayerSteer>>> = vec![vec![None; stride]; num_layers];
for (k, vector) in policy.vectors.iter().enumerate() {
for (layer, slot) in layers.iter_mut().enumerate() {
let Some(dir) = vector.set.layer(layer) else {
continue;
};
let offset = packed.len() as u64;
let mut sum_sq = 0.0f32;
for v in &dir.values {
let h = half::f16::from_f32(*v);
packed.extend_from_slice(&h.to_bits().to_le_bytes());
let r = h.to_f32();
sum_sq += r * r;
}
let inv_norm = if sum_sq > 0.0 && sum_sq.is_finite() {
1.0 / sum_sq.sqrt()
} else {
return Err(RealForwardError::Unsupported(format!(
"steering vector {k}'s direction for layer {layer} has zero norm; \
steering direction must be non-zero"
)));
};
slot[k] = Some(LayerSteer {
offset,
inv_norm,
mode: vector.mode,
alpha: vector.alpha,
});
}
}
Ok(Some(Self {
directions: context.new_buffer_with_data(&packed),
coeff: context.new_output_buffer(Self::coeff_bytes(num_layers, stride)),
layers,
coeff_stride: stride,
target: policy.target,
gate_threshold: policy.gate_threshold,
}))
}
pub(crate) fn layer(&self, layer: usize) -> &[Option<LayerSteer>] {
self.layers.get(layer).map(|v| v.as_slice()).unwrap_or(&[])
}
pub(crate) fn covered_layers(&self) -> usize {
self.layers
.iter()
.filter(|v| v.iter().any(|s| s.is_some()))
.count()
}
pub(crate) fn coeff_bytes(num_layers: usize, vectors: usize) -> u64 {
(num_layers.max(1) * vectors.max(1) * MAX_STEER_ROWS * 4) as u64
}
pub(crate) fn coeff_offset(layer: usize, k: usize, vectors: usize) -> u64 {
((layer * vectors.max(1)) + k) as u64 * (MAX_STEER_ROWS as u64) * 4
}
pub(crate) fn coefficients(&self) -> Vec<Vec<Option<f32>>> {
let raw = gpu::read_f32_buffer(
&self.coeff,
self.layers.len() * self.coeff_stride * MAX_STEER_ROWS,
);
self.layers
.iter()
.enumerate()
.map(|(l, slots)| {
slots
.iter()
.enumerate()
.map(|(k, entry)| {
entry.map(|_| raw[(l * self.coeff_stride + k) * MAX_STEER_ROWS])
})
.collect()
})
.collect()
}
pub(crate) fn summary(&self) -> String {
let gate = if self.gate_threshold > 0.0 {
format!(", gated at |c| >= {}", self.gate_threshold)
} else {
String::new()
};
if self.coeff_stride == 1 {
let e = self
.layers
.iter()
.find(|v| v.iter().any(|s| s.is_some()))
.and_then(|v| v.iter().flatten().next())
.expect("one steered layer was found above");
format!(
"steering: {} at alpha {} over {} of {} layers{}",
e.mode.as_str(),
e.alpha,
self.covered_layers(),
self.layers.len(),
gate
)
} else {
let by_vector: Vec<String> = (0..self.coeff_stride)
.map(|k| {
let mut entry = None;
let mut layers = 0usize;
for slots in &self.layers {
if let Some(e) = slots[k].as_ref() {
entry = Some(e);
layers += 1;
}
}
let e = entry.expect("a vector in an active policy covers a layer");
format!("{}@{} ({} layers)", e.mode.as_str(), e.alpha, layers)
})
.collect();
format!(
"steering: {} vectors over {} of {} layers: {}{}",
self.coeff_stride,
self.covered_layers(),
self.layers.len(),
by_vector.join(" then "),
gate
)
}
}
}
pub fn family_dispatches_steering(family: model_io::ModelFamily) -> bool {
use model_io::ModelFamily as F;
match family {
F::QwenGdnMoe | F::QwenGdnDense => true,
F::Llama | F::Qwen3Moe | F::Qwen3Dense | F::Qwen2Dense | F::MiniMaxM2 | F::Qwen3Vl => true,
F::Gemma4 => true,
F::GptOss => true,
F::MuseGlimmer => true,
F::Spark25 => true,
F::DeepseekV4Flash => false,
F::Deepseek2 => false,
F::Qwen4Exp => false,
}
}
pub fn steering_unsupported_reason(family: model_io::ModelFamily) -> Option<String> {
if family_dispatches_steering(family) {
return None;
}
Some(format!(
"steering is not wired for family {family:?}: its flow does not dispatch the edit, \
so a direction set here would be a silent no-op. Wired today: the qwen flow \
(both halves), the llama flow (Mixtral, Qwen3-MoE, dense qwen3, Qwen2, \
MiniMax-M2, Qwen3-VL, and the dense Llama / Mistral half), Gemma 4 \
(sequential decode and chunked prefill, both batched-routed and \
per-token), gpt-oss, museGlimmer, and spark2_5. Unwired: \
DeepSeek-V4-Flash (no decode flow exists to hook), deepseek2 (the flow \
carries the hooks; the gate stays shut until a real MLA steering gate \
runs), and qwen4_exp (its residual is hc_count streams wide, \
so the boundary is a different shape)"
))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_steering(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
scratch: &crate::real_forward_types::DecodeScratch,
steering: Option<&SteeringState>,
layer: usize,
hidden: usize,
rows: usize,
x_off: u64,
) -> Result<(), RealForwardError> {
let Some(s) = steering else {
return Ok(());
};
if rows > MAX_STEER_ROWS {
return Err(RealForwardError::Unsupported(format!(
"steering a block of {rows} rows, but the coefficient buffer holds {MAX_STEER_ROWS} \
per layer; steering fewer rows than the block would draw part of a block from the \
unsteered model"
)));
}
for (k, slot) in s.layer(layer).iter().enumerate() {
let Some(entry) = slot else {
continue;
};
gpu::encode_steer_direction(
context,
pass,
(&scratch.x, x_off),
(&s.directions, entry.offset),
(
&s.coeff,
SteeringState::coeff_offset(layer, k, s.coeff_stride),
),
&gpu::SteerParams {
d_len: hidden as u32,
rows: rows as u32,
row_stride: hidden as u32,
mode: entry.mode,
alpha: entry.alpha,
inv_norm: entry.inv_norm,
target: s.target,
gate_threshold: s.gate_threshold,
},
)
.map_err(RealForwardError::Gpu)?;
}
Ok(())
}
#[cfg(test)]
#[path = "steering_tests.rs"]
mod steering_tests;