spark_model/kimi_k3/
bound.rs1use std::collections::HashMap;
12use std::sync::{Arc, OnceLock};
13
14use anyhow::{Context, Result};
15use avarok_core::config::ModelConfig;
16use avarok_core::kimi_k3::{
17 AttnResStream, K3CpuLayer, K3Graph, K3LayerSpec, KdaConfig, LatentMoeConfig, LayerCache,
18 MixerKind, MlaConfig,
19};
20use parking_lot::Mutex;
21use spark_runtime::gpu::{DevicePtr, GpuBackend};
22use spark_runtime::kv_cache::PagedKvCache;
23use spark_runtime::weights::WeightDtype;
24
25use super::kda_cuda::K3KdaDecodeKernels;
26use super::mla_cuda::K3MlaDecodeKernels;
27use super::moe_cuda::K3MoeGemmKernels;
28use super::state::K3CpuFallbackState;
29use crate::layer::{ForwardContext, LayerState, TransformerLayer};
30use crate::weight_map::{DenseWeight, QuantizedWeight};
31
32#[derive(Clone, Debug)]
34pub struct WeightMeta {
35 pub name: String,
36 pub dtype: WeightDtype,
37 pub numel: usize,
38}
39
40pub struct K3HostShared {
42 pub config: ModelConfig,
43 pub graph: K3Graph,
44 pub kda: KdaConfig,
45 pub mla: MlaConfig,
46 pub moe: LatentMoeConfig,
47 pub output_res_proj: DenseWeight,
48 pub output_res_norm: DenseWeight,
49 pub output_res_proj_meta: (WeightDtype, usize),
50 pub output_res_norm_meta: (WeightDtype, usize),
51 pub output_host: OnceLock<(Vec<f32>, Vec<f32>)>,
52 pub kda_kernels: OnceLock<K3KdaDecodeKernels>,
54 pub mla_kernels: OnceLock<K3MlaDecodeKernels>,
57 pub moe_kernels: OnceLock<K3MoeGemmKernels>,
59 pub attnres: Mutex<HashMap<DevicePtr, AttnResStream>>,
63}
64
65pub struct K3BoundLayer {
67 pub index: usize,
68 pub spec: K3LayerSpec,
69 pub weights: Vec<DenseWeight>,
70 pub weight_meta: Vec<WeightMeta>,
71 pub mxfp4_experts: Vec<(String, QuantizedWeight)>,
74 pub host: OnceLock<K3CpuLayer>,
75 pub shared: Arc<K3HostShared>,
76}
77
78impl TransformerLayer for K3BoundLayer {
79 fn decode_graph_unsupported(&self) -> bool {
80 true
82 }
83
84 fn decode_multi_seq_unsupported(&self) -> bool {
85 true
86 }
87
88 fn decode_verify_multi_unsupported(&self) -> bool {
89 true
91 }
92
93 fn decode_rollback_unsupported(&self) -> bool {
94 true
97 }
98
99 fn uses_ssm_pool(&self) -> bool {
100 false
102 }
103
104 fn has_aux_state(&self) -> bool {
105 true
106 }
107
108 fn snapshot_aux(
109 &self,
110 state: &dyn LayerState,
111 gpu: &dyn GpuBackend,
112 stream: u64,
113 ) -> Result<Option<Vec<u8>>> {
114 let st = state
115 .as_any()
116 .downcast_ref::<K3CpuFallbackState>()
117 .context("K3 snapshot_aux: expected K3CpuFallbackState")?;
118 Ok(Some(st.snapshot(gpu, stream)?))
119 }
120
121 fn restore_aux(
122 &self,
123 state: &mut dyn LayerState,
124 blob: &[u8],
125 gpu: &dyn GpuBackend,
126 stream: u64,
127 ) -> Result<()> {
128 let st = state
129 .as_any_mut()
130 .downcast_mut::<K3CpuFallbackState>()
131 .context("K3 restore_aux: expected K3CpuFallbackState")?;
132 st.restore(gpu, blob, stream)
133 }
134
135 #[allow(clippy::too_many_arguments)]
136 fn decode(
137 &self,
138 hidden: DevicePtr,
139 residual: DevicePtr,
140 state: &mut dyn LayerState,
141 _kv_cache: &mut PagedKvCache,
142 seq_len: usize,
143 _block_table: &mut Vec<u32>,
144 _disk_block_ids: &mut Vec<u32>,
145 _disk_last_offloaded_per_layer: &mut Vec<u32>,
146 ctx: &ForwardContext,
147 stream: u64,
148 ) -> Result<()> {
149 self.decode_host(
150 hidden,
151 residual,
152 state,
153 seq_len,
154 ctx,
155 stream,
156 avarok_core::kimi_k3::cuda_kda_enabled(),
157 avarok_core::kimi_k3::cuda_mla_enabled(),
158 )
159 }
160
161 fn alloc_state(&self, _gpu: &dyn GpuBackend) -> Result<Box<dyn LayerState>> {
162 let cache = match self.spec.mixer {
163 MixerKind::Kda => {
164 LayerCache::Kda(avarok_core::kimi_k3::KdaState::new(&self.shared.kda))
165 }
166 MixerKind::Mla => LayerCache::Mla(avarok_core::kimi_k3::MlaKv::default()),
167 };
168 Ok(Box::new(K3CpuFallbackState::new(cache)))
169 }
170
171 fn release_state(&self, state: &mut dyn LayerState, gpu: &dyn GpuBackend) -> Result<()> {
172 state
173 .as_any_mut()
174 .downcast_mut::<K3CpuFallbackState>()
175 .context("K3 release_state: expected K3CpuFallbackState")?
176 .release(gpu)
177 }
178}