use model_io::{ArchConfig, ModelFamily, ResidentIndex};
use crate::families::gptoss::layer_tensor;
use crate::real_forward::RealForwardError;
use crate::real_forward_types::MAX_PREFILL_BATCH;
use crate::real_forward_utils::entry;
const BF16_ONE: u16 = 0x3F80;
pub(crate) struct RealGptOssState {
pub(crate) rotated_pairs: u32,
pub(crate) rms_eps: f32,
pub(crate) rope_frequencies: gpu::MetalBuffer,
pub(crate) rope_mscale: f32,
pub(crate) router_bias: Vec<Vec<f32>>,
pub(crate) router_ones: gpu::MetalBuffer,
pub(crate) per_expert_ones: Vec<f32>,
pub(crate) router_logits_f32: gpu::MetalBuffer,
pub(crate) moe_x: gpu::MetalBuffer,
pub(crate) h2: gpu::MetalBuffer,
pub(crate) batched: Option<BatchedRoutedScratch>,
}
pub(crate) struct BatchedRoutedScratch {
pub(crate) batch_acts: gpu::MetalBuffer,
pub(crate) batch_y: gpu::MetalBuffer,
pub(crate) batch_routing_w: gpu::MetalBuffer,
pub(crate) batch_routes: gpu::MetalBuffer,
pub(crate) batch_zero: gpu::MetalBuffer,
pub(crate) wide_blobs: gpu::RoutedBlobsWideBuffer,
}
impl RealGptOssState {
pub(crate) fn build(
context: &mut gpu::MetalContext,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
arch: &ArchConfig,
) -> Result<Self, RealForwardError> {
let unsupported = |detail: String| Err(RealForwardError::Unsupported(detail));
if arch.family != ModelFamily::GptOss {
return unsupported(format!(
"the gpt-oss flow refuses family {:?}; flow selection keys on the family, never \
on tensor naming",
arch.family
));
}
if arch.ffn_sandwich_norms
|| arch.router_scaled
|| arch.embedding_scaled_by_sqrt_hidden
|| arch.attn_output_gate
|| arch.shared_expert_gated
|| arch.rope_neox_subdim
|| arch.attention_k_eq_v
{
return unsupported(
"the gpt-oss flow takes none of ffnSandwichNorms / routerScaled / \
embeddingScaledBySqrtHidden / attnOutputGate / sharedExpertGated / \
ropeNeoxSubdim / attentionKEqV"
.to_string(),
);
}
if arch.final_logit_softcap != 0.0 {
return unsupported("gpt-oss has no final logit softcap".to_string());
}
if arch.tie_word_embeddings {
return unsupported(
"gpt-oss ships an untied `output.weight`; an install claiming tied embeddings \
was built from something else"
.to_string(),
);
}
if arch.num_experts <= 0 || arch.top_k_experts <= 0 {
return unsupported(format!(
"gpt-oss is MoE-only; got num_experts {} and top_k_experts {}",
arch.num_experts, arch.top_k_experts
));
}
if arch.top_k_experts as usize > gpu::MAX_STREAMED_EXPERTS {
return unsupported(format!(
"top_k {} exceeds the {}-slot MoE kernels",
arch.top_k_experts,
gpu::MAX_STREAMED_EXPERTS
));
}
if arch.full_attention_layer_mask.iter().any(|&m| m > 1) {
return unsupported(
"gpt-oss layers are sliding-window (0) or full (1); no linear or compressed \
layer kinds exist in this architecture"
.to_string(),
);
}
if arch.sliding_window <= 0 {
return unsupported(format!(
"gpt-oss alternates a sliding window; {} is not a usable one",
arch.sliding_window
));
}
let head_dim = arch.full_head_dim;
let rotated_pairs = (head_dim as f64 * arch.partial_rotary_factor / 2.0).round() as i64;
if rotated_pairs <= 0 || rotated_pairs > head_dim / 2 {
return unsupported(format!(
"rotated pairs {rotated_pairs} (full_head_dim {head_dim} x \
partial_rotary_factor {}) must be positive and at most half the head",
arch.partial_rotary_factor
));
}
if arch.rope_scaling.factor <= 0.0 {
return unsupported(
"gpt-oss declares YaRN rope scaling; an install with no `ropeScaling` block \
would decode with unscaled frequencies, which is fluent and wrong past the \
original context"
.to_string(),
);
}
let yarn = compute::yarn_frequencies(
head_dim as usize,
arch.full_rope_theta as f32,
arch.rope_scaling.factor as f32,
arch.rope_scaling.original_context,
arch.rope_scaling.beta_fast as f32,
arch.rope_scaling.beta_slow as f32,
);
let freq_bytes: Vec<u8> = yarn
.frequencies
.iter()
.flat_map(|f| f.to_le_bytes())
.collect();
let rope_frequencies = context.new_output_buffer(freq_bytes.len().max(4) as u64);
gpu::write_buffer_bytes(&rope_frequencies, 0, &freq_bytes);
let hidden = arch.hidden_size as usize;
let num_experts = arch.num_experts as usize;
let mut router_bias = Vec::with_capacity(arch.num_layers as usize);
for layer in 0..arch.num_layers as usize {
for suffix in [
"input_layernorm.weight",
"post_attention_layernorm.weight",
"self_attn.q_proj.weight",
"self_attn.k_proj.weight",
"self_attn.v_proj.weight",
"self_attn.o_proj.weight",
"self_attn.q_proj.bias",
"self_attn.k_proj.bias",
"self_attn.v_proj.bias",
"self_attn.o_proj.bias",
"self_attn.sinks.weight",
"mlp.gate.weight",
"mlp.gate.bias",
] {
entry(index, &layer_tensor(layer, suffix))?;
}
let sinks = entry(index, &layer_tensor(layer, "self_attn.sinks.weight"))?;
let want = arch.num_heads as usize * 2;
if sinks.size_bytes as usize != want {
return unsupported(format!(
"layer {layer} attention sinks are {} bytes; expected {want} (one BF16 \
logit per QUERY head, {} of them)",
sinks.size_bytes, arch.num_heads
));
}
router_bias.push(read_bias(weights, index, layer, num_experts)?);
}
entry(index, "language_model.lm_head.weight")?;
entry(index, "language_model.model.norm.weight")?;
entry(index, "language_model.model.embed_tokens.weight")?;
let ones: Vec<u8> = (0..hidden).flat_map(|_| BF16_ONE.to_le_bytes()).collect();
let router_ones = context.new_output_buffer(ones.len() as u64);
gpu::write_buffer_bytes(&router_ones, 0, &ones);
let halfs = |n: usize| context.new_output_buffer((n.max(1) * 2) as u64);
Ok(Self {
rotated_pairs: rotated_pairs as u32,
rms_eps: 1e-5,
rope_frequencies,
rope_mscale: yarn.mscale,
router_bias,
router_ones,
per_expert_ones: vec![1.0; num_experts],
router_logits_f32: context
.new_output_buffer((num_experts * 4 * MAX_PREFILL_BATCH) as u64),
moe_x: halfs(hidden * MAX_PREFILL_BATCH),
h2: halfs(hidden),
batched: None,
})
}
pub(crate) fn ensure_batched(
&mut self,
context: &mut gpu::MetalContext,
arch: &ArchConfig,
) -> Result<(), RealForwardError> {
if self.batched.is_some() {
return Ok(());
}
let top_k = arch.top_k_experts as usize;
let hidden = arch.hidden_size as usize;
let moe_inter = arch.moe_intermediate_size.max(1) as usize;
let wide_blobs =
gpu::new_routed_blobs_wide_mxfp4(context).map_err(RealForwardError::Gpu)?;
let halfs = |n: usize| context.new_output_buffer((n.max(1) * 2) as u64);
let batch_rows = |per_token: usize| halfs(MAX_PREFILL_BATCH * per_token);
self.batched = Some(BatchedRoutedScratch {
batch_acts: batch_rows(top_k * moe_inter),
batch_y: batch_rows(hidden),
batch_routing_w: batch_rows(top_k),
batch_routes: context.new_output_buffer(
(MAX_PREFILL_BATCH * top_k * gpu::MoePrefillRoute::STRIDE_BYTES) as u64,
),
batch_zero: {
let bytes = MAX_PREFILL_BATCH * hidden.max(1) * 2;
let buffer = context.new_output_buffer(bytes as u64);
gpu::write_buffer_bytes(&buffer, 0, &vec![0u8; bytes]);
buffer
},
wide_blobs,
});
Ok(())
}
pub(crate) fn batched(&self) -> &BatchedRoutedScratch {
self.batched
.as_ref()
.expect("prefill_chunk_real_gpt_oss calls ensure_batched before any layer runs")
}
}
fn read_bias(
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
layer: usize,
num_experts: usize,
) -> Result<Vec<f32>, RealForwardError> {
let name = layer_tensor(layer, "mlp.gate.bias");
let bias = crate::real_forward_utils::read_bf16_host(weights, index, &name)?;
if bias.len() != num_experts {
return Err(RealForwardError::Unsupported(format!(
"{name}: expected {num_experts} values, got {}",
bias.len()
)));
}
Ok(bias)
}
#[cfg(test)]
mod tests {
#[test]
fn the_gpt_oss_yarn_scalars_produce_llama_cpps_mscale() {
let arch = model_io::known_architecture(model_io::ModelFamily::GptOss);
let yarn = compute::yarn_frequencies(
arch.full_head_dim as usize,
arch.full_rope_theta as f32,
arch.rope_scaling.factor as f32,
arch.rope_scaling.original_context,
arch.rope_scaling.beta_fast as f32,
arch.rope_scaling.beta_slow as f32,
);
assert!(
(yarn.mscale - 1.346_573_6).abs() < 1e-6,
"mscale is {}, expected 1.3465736; 1.0 is the value llama.cpp's apparent \
cancellation invites and is wrong",
yarn.mscale
);
assert_eq!(yarn.frequencies.len(), arch.full_head_dim as usize / 2);
let first = yarn.frequencies[0];
let last = yarn.frequencies[yarn.frequencies.len() - 1];
assert!(
first > last,
"frequencies must fall across the head ({first} then {last})"
);
assert!(
(first - 1.0).abs() < 1e-6,
"pair 0 is the fastest dimension and YaRN leaves it unscaled, got {first}"
);
}
}