use model_io::{ArchConfig, ResidentIndex};
use super::batched::BatchedScratch;
pub use super::dflash_state::{dflash_speculation_blocker, install_has_dflash};
pub(crate) use super::dflash_state::{
DflashDraftPolicy, DflashShape, DFLASH_AUX_LAYERS, DFLASH_MASK_TOKEN, DFLASH_RESIDUAL_EPS,
DFLASH_RESIDUAL_SCALE, DFLASH_RING_SLACK, DFLASH_SERVING_BLOCK, DFLASH_TOP_K, DFLASH_WINDOW,
};
use super::MOE_SPECULATION_BLOCKER_MARKER;
use crate::real_forward::{RealForwardError, RealForwardRunner};
pub(crate) struct DflashState {
pub(crate) kv: gpu::KvCacheManager,
pub(crate) capture: gpu::MetalBuffer,
pub(crate) capture_base: usize,
pub(crate) capture_rows: usize,
pub(crate) block: usize,
pub(crate) shape: DflashShape,
pub(crate) batched: BatchedScratch,
pub(crate) x: gpu::MetalBuffer,
pub(crate) normed: gpu::MetalBuffer,
pub(crate) conv_p: gpu::MetalBuffer,
pub(crate) conv_f: gpu::MetalBuffer,
pub(crate) sub_out: gpu::MetalBuffer,
pub(crate) coeffs: gpu::MetalBuffer,
pub(crate) q: gpu::MetalBuffer,
pub(crate) attn_out: gpu::MetalBuffer,
pub(crate) attn: gpu::AttentionScratch,
pub(crate) ffn_gate: gpu::MetalBuffer,
pub(crate) ffn_up: gpu::MetalBuffer,
pub(crate) ffn_act: gpu::MetalBuffer,
pub(crate) ctx_combined: gpu::MetalBuffer,
pub(crate) ctx_normed: gpu::MetalBuffer,
pub(crate) hproj: gpu::MetalBuffer,
pub(crate) logits: gpu::MetalBuffer,
}
fn dflash_moe_unsupported(num_experts: i64) -> String {
format!(
"{MOE_SPECULATION_BLOCKER_MARKER}: this install routes to {num_experts} experts, and no \
published MoE checkpoint of this architecture ships a DFlash2 drafter (the \
published one targets the dense half). The batched routed verify itself \
runs, so this is a checkpoint gap and not a missing kernel"
)
}
impl DflashState {
pub(crate) fn build(
context: &mut gpu::MetalContext,
index: &ResidentIndex,
arch: &ArchConfig,
max_context: usize,
policy: DflashDraftPolicy,
gdn_shape: gpu::GdnShape,
) -> Result<Option<Self>, RealForwardError> {
let block = match policy {
DflashDraftPolicy::Off => return Ok(None),
DflashDraftPolicy::Auto => {
if !install_has_dflash(index) {
return Ok(None);
}
if arch.num_experts != 0 {
return Err(RealForwardError::Unsupported(dflash_moe_unsupported(
arch.num_experts,
)));
}
DFLASH_SERVING_BLOCK
}
DflashDraftPolicy::Fixed(block) => {
if arch.num_experts != 0 {
return Err(RealForwardError::Unsupported(dflash_moe_unsupported(
arch.num_experts,
)));
}
if !install_has_dflash(index) {
return Err(RealForwardError::Unsupported(format!(
"TURBOSPARK_DFLASH_DRAFT={block} asks for the DFlash2 drafter, but this \
install carries none (dflash.fc.weight is not in the resident index); \
stream it beside the trunk (docs/DFLASH2.md)"
)));
}
if block == 0 {
return Err(RealForwardError::Unsupported(
"the DFlash2 drafter was asked for at a block of 0; use Off (or \
TURBOSPARK_DFLASH_DRAFT=0) to turn drafting off"
.to_string(),
));
}
block
}
};
if block + 1 > gpu::MAX_BATCH_ROWS {
return Err(RealForwardError::Unsupported(format!(
"DFlash2 block {block} verifies {} rows, over the batched kernel's cap of {}",
block + 1,
gpu::MAX_BATCH_ROWS
)));
}
let hidden = arch.hidden_size as usize;
let vocab = arch.vocab_size as usize;
let shape = DflashShape::derive(index, hidden, vocab)?;
if shape.aux_count != DFLASH_AUX_LAYERS.len() {
return Err(RealForwardError::Unsupported(format!(
"dflash.fc.weight fuses {} aux states but this drafter taps {} trunk layers",
shape.aux_count,
DFLASH_AUX_LAYERS.len()
)));
}
let mut kv_arch = arch.clone();
kv_arch.num_layers = shape.layers as i64;
kv_arch.full_attention_layer_mask = vec![0u8; shape.layers];
kv_arch.num_kv_heads = shape.num_kv_heads as i64;
kv_arch.num_full_kv_heads = shape.num_kv_heads as i64;
kv_arch.head_dim = shape.head_dim as i64;
kv_arch.full_head_dim = shape.head_dim as i64;
let kv = gpu::KvCacheManager::new(
context.device(),
&kv_arch,
max_context,
true,
Some(DFLASH_WINDOW),
1,
Some(DFLASH_WINDOW + DFLASH_RING_SLACK),
)
.map_err(RealForwardError::Gpu)?;
let kv_stride = shape.num_kv_heads * shape.head_dim * 2;
for layer in 0..shape.layers {
if kv.k_stride(layer) != kv_stride {
return Err(RealForwardError::Unsupported(format!(
"the drafter's KV stride {} is not the {kv_stride} bytes a batched \
projection writes per row",
kv.k_stride(layer)
)));
}
}
let rows = gpu::MAX_BATCH_ROWS as u64;
let h = hidden as u64;
let inter = shape.inter as u64;
let vocab = arch.vocab_size as u64;
let batched = BatchedScratch::new(context, arch, gdn_shape, block + 1, true)
.map_err(RealForwardError::Gpu)?;
let halfs = |n: u64| context.new_output_buffer(rows * n * 2);
let state = Self {
kv,
capture: context.new_output_buffer(rows * shape.aux_count as u64 * h * 2),
capture_base: 0,
capture_rows: 0,
block,
attn: gpu::AttentionScratch::new(
context,
shape.num_heads as u32,
shape.head_dim as u32,
),
batched,
x: halfs(h),
normed: halfs(h),
conv_p: halfs(h),
conv_f: halfs(h),
sub_out: halfs(h),
coeffs: context.new_output_buffer(rows * shape.conv_rows as u64 * 2),
q: halfs(shape.num_heads as u64 * shape.head_dim as u64),
attn_out: halfs(shape.num_heads as u64 * shape.head_dim as u64),
ffn_gate: halfs(inter),
ffn_up: halfs(inter),
ffn_act: halfs(inter),
ctx_combined: halfs(h),
ctx_normed: halfs(h),
hproj: context.new_output_buffer(rows * shape.rank as u64 * 2),
logits: halfs(vocab),
shape,
};
Ok(Some(state))
}
pub(crate) fn reset(&mut self) {
self.kv.reset();
self.capture_base = 0;
self.capture_rows = 0;
}
pub(crate) fn note_capture(&mut self, base: usize, rows: usize) {
self.capture_base = base;
self.capture_rows = rows;
}
pub(crate) fn aux_slot(&self, layer: usize) -> Option<usize> {
DFLASH_AUX_LAYERS.iter().position(|&l| l == layer)
}
}
impl RealForwardRunner {
pub fn dflash_speculation_blocker(&self) -> Option<String> {
dflash_speculation_blocker(&self.index, &self.arch, self.real_dflash.is_some())
}
}