mod attention;
#[cfg(feature = "cuda")]
mod cuda_quantized;
mod quantized;
mod standard;
pub use attention::{
attention_with_cache, attention_with_cache_mode, create_causal_mask, repeat_kv, AttentionConfig,
};
#[cfg(feature = "cuda")]
pub use cuda_quantized::CudaQuantizedCache;
pub use quantized::{QuantizationGranularity, QuantizedCache};
pub use standard::StandardCache;
pub use crate::flash_attention::AttentionVariant;
use candle_core::{DType, Device, Result as CandleResult, Tensor};
pub trait KvCache: Send {
fn append(&mut self, k: &Tensor, v: &Tensor) -> CandleResult<()>;
fn seq_len(&self) -> usize;
fn clear(&mut self);
fn memory_bytes(&self) -> usize;
fn get_kv(&self) -> CandleResult<Option<(Tensor, Tensor)>>;
fn supports_fused_attention(&self) -> bool {
false
}
fn forward_attention(
&mut self,
_q: &Tensor,
_num_heads: usize,
_scale: f32,
) -> CandleResult<Tensor> {
panic!("forward_attention called on cache that doesn't support fused attention")
}
}
#[derive(Debug, Clone)]
pub struct KvCacheConfig {
pub num_kv_heads: usize,
pub head_dim: usize,
pub device: Device,
pub dtype: DType,
}
impl KvCacheConfig {
pub fn new(num_kv_heads: usize, head_dim: usize, device: Device, dtype: DType) -> Self {
Self {
num_kv_heads,
head_dim,
device,
dtype,
}
}
}
#[derive(Debug, Clone)]
pub enum CacheType {
Standard,
Quantized(QuantizationGranularity),
#[cfg(feature = "cuda")]
CudaQuantized {
device_id: usize,
},
}
impl Default for CacheType {
fn default() -> Self {
Self::Standard
}
}
impl CacheType {
pub fn create(&self, config: &KvCacheConfig) -> CandleResult<Box<dyn KvCache>> {
match self {
Self::Standard => Ok(Box::new(StandardCache::new())),
Self::Quantized(granularity) => Ok(Box::new(QuantizedCache::new(
config.num_kv_heads,
config.head_dim,
*granularity,
))),
#[cfg(feature = "cuda")]
Self::CudaQuantized { device_id } => {
let cache =
CudaQuantizedCache::new(config.num_kv_heads, config.head_dim, *device_id)
.map_err(|e| {
candle_core::Error::Msg(format!("CUDA cache init failed: {e}"))
})?;
Ok(Box::new(cache))
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_config() {
let config = KvCacheConfig::new(2, 64, Device::Cpu, DType::BF16);
assert_eq!(config.num_kv_heads, 2);
assert_eq!(config.head_dim, 64);
}
#[test]
fn test_cache_type_default() {
let cache_type = CacheType::default();
assert!(matches!(cache_type, CacheType::Standard));
}
}