use model_io::{ArchConfig, ResidentIndex};
use crate::families::gptoss::{layer_tensor, RealGptOssState};
use crate::kv_write::{encode_attention_any, encode_kv_commit, kv_write_target, KvHalf};
use crate::real_forward_dispatch::encode_gemv_any;
use crate::real_forward_types::{DecodeScratch, RealForwardError};
use crate::real_forward_utils::norm_view;
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_attention_block(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
arch: &ArchConfig,
state: &RealGptOssState,
scratch: &DecodeScratch,
kv: &gpu::KvCacheManager,
layer: usize,
position: usize,
) -> Result<(), RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let hidden = arch.hidden_size as usize;
let num_heads = arch.num_heads as u32;
let num_kv = arch.num_full_kv_heads as u32;
let head_dim = arch.full_head_dim as u32;
let q_dim = (num_heads * head_dim) as usize;
let kv_dim = (num_kv * head_dim) as usize;
let name = |suffix: &str| layer_tensor(layer, &format!("self_attn.{suffix}"));
let (k_buf, k_off) = kv_write_target(kv, scratch, KvHalf::K, layer, position);
let (v_buf, v_off) = kv_write_target(kv, scratch, KvHalf::V, layer, position);
encode_gemv_any(
context,
pass,
weights,
index,
&name("q_proj.weight"),
q_dim,
hidden,
(&scratch.normed, 0),
(&scratch.q, 0),
)?;
for (suffix, out) in [
("k_proj.weight", (k_buf, k_off)),
("v_proj.weight", (v_buf, v_off)),
] {
encode_gemv_any(
context,
pass,
weights,
index,
&name(suffix),
kv_dim,
hidden,
(&scratch.normed, 0),
out,
)?;
}
for (suffix, elems, out) in [
("q_proj.bias", q_dim, (&scratch.q, 0u64)),
("k_proj.bias", kv_dim, (k_buf, k_off)),
("v_proj.bias", kv_dim, (v_buf, v_off)),
] {
let bias = norm_view(weights, index, &name(suffix), elems)?;
gpu::encode_bias_add(context, pass, out, bias, elems as u32).map_err(gpu_err)?;
}
for (data, heads) in [((&scratch.q, 0u64), num_heads), ((k_buf, k_off), num_kv)] {
gpu::encode_rope_neox_freqs(
context,
pass,
data,
position as u32,
heads,
head_dim,
state.rotated_pairs,
(&state.rope_frequencies, 0),
state.rope_mscale,
)
.map_err(gpu_err)?;
}
let seq_len = (position + 1) as u32;
let is_full = arch
.full_attention_layer_mask
.get(layer)
.copied()
.unwrap_or(1)
== 1;
let (kv_start, active_ring) = if is_full {
(0, 0)
} else {
let ring = kv.ring_capacity(layer) as u32;
(
seq_len.saturating_sub(arch.sliding_window as u32),
if ring > 0 && seq_len > ring { ring } else { 0 },
)
};
encode_kv_commit(
context, pass, kv, scratch, layer, position, head_dim, num_kv,
)?;
let sinks = norm_view(
weights,
index,
&layer_tensor(layer, "self_attn.sinks.weight"),
num_heads as usize,
)?;
encode_attention_any(
context,
pass,
(&scratch.q, 0),
kv,
scratch,
(&scratch.attn_out, 0),
layer,
position,
head_dim,
num_heads,
num_kv,
seq_len,
kv_start,
active_ring,
arch.attention_scale as f32,
Some(sinks),
)?;
encode_gemv_any(
context,
pass,
weights,
index,
&name("o_proj.weight"),
hidden,
q_dim,
(&scratch.attn_out, 0),
(&scratch.o, 0),
)?;
let o_bias = norm_view(weights, index, &name("o_proj.bias"), hidden)?;
gpu::encode_bias_add(context, pass, (&scratch.o, 0), o_bias, hidden as u32).map_err(gpu_err)
}
impl crate::real_forward::RealForwardRunner {
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_gpt_oss_layer_attn_and_router(
&mut self,
pass: &gpu::PassEncoder,
layer: usize,
position: usize,
t: usize,
hidden: usize,
num_experts: usize,
) -> Result<(), RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let x_off = (t * hidden * 2) as u64;
let input_norm = norm_view(
&self.weights,
&self.index,
&layer_tensor(layer, "input_layernorm.weight"),
hidden,
)?;
let post_attn_norm = norm_view(
&self.weights,
&self.index,
&layer_tensor(layer, "post_attention_layernorm.weight"),
hidden,
)?;
let state = self
.real_gpt_oss
.as_ref()
.expect("real gpt-oss state present");
gpu::encode_rms_norm_bf16w(
&mut self.context,
pass,
(&self.scratch.x, x_off),
input_norm,
(&self.scratch.normed, 0),
hidden as u32,
state.rms_eps,
)
.map_err(gpu_err)?;
encode_attention_block(
&mut self.context,
pass,
&self.weights,
&self.index,
&self.arch,
state,
&self.scratch,
&self.kv,
layer,
position,
)?;
gpu::encode_residual_add(
&mut self.context,
pass,
(&self.scratch.x, x_off),
(&self.scratch.o, 0),
hidden as u32,
)
.map_err(gpu_err)?;
let state = self
.real_gpt_oss
.as_ref()
.expect("real gpt-oss state present");
gpu::encode_rms_norm_bf16w(
&mut self.context,
pass,
(&self.scratch.x, x_off),
post_attn_norm,
(&state.moe_x, x_off),
hidden as u32,
state.rms_eps,
)
.map_err(gpu_err)?;
let router_name = layer_tensor(layer, "mlp.gate.weight");
let router = crate::real_forward_utils::entry(&self.index, &router_name)?;
if router.dtype != 5 || router.size_bytes as usize != num_experts * hidden {
return Err(RealForwardError::Unsupported(format!(
"{router_name}: expected INT8 (dtype 5) {num_experts}x{hidden}, got \
dtype {} with {} packed bytes",
router.dtype, router.size_bytes
)));
}
let base = self.index.header.index_size;
gpu::encode_router_gemv_gemma4(
&mut self.context,
pass,
(
self.weights.buffer(),
self.weights.gpu_offset(router.file_offset - base),
),
(
self.weights.buffer(),
self.weights.gpu_offset(router.scale_offset - base),
),
(
self.weights.buffer(),
self.weights.gpu_offset(router.bias_offset - base),
),
(&state.moe_x, x_off),
(&state.router_ones, 0),
(&state.router_logits_f32, (t * num_experts * 4) as u64),
num_experts as u32,
hidden as u32,
)
.map_err(gpu_err)?;
Ok(())
}
}