use std::{collections::BTreeSet, num::NonZeroU32};
use serde::{Deserialize, Serialize};
use crate::attention::AttentionPolicy;
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CacheRepresentation {
KeyValue,
CompressedLatentRotary,
}
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct CacheRankIdentity {
stage_rank: Option<usize>,
shard_rank: Option<usize>,
addressable_rank: Option<usize>,
}
impl CacheRankIdentity {
pub const fn new(
stage_rank: Option<usize>,
shard_rank: Option<usize>,
addressable_rank: Option<usize>,
) -> Self {
Self {
stage_rank,
shard_rank,
addressable_rank,
}
}
pub const fn stage_rank(&self) -> Option<usize> {
self.stage_rank
}
pub const fn shard_rank(&self) -> Option<usize> {
self.shard_rank
}
pub const fn addressable_rank(&self) -> Option<usize> {
self.addressable_rank
}
}
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct CacheBlockId {
pub session_id: u64,
pub global_layer: usize,
pub representation: CacheRepresentation,
pub start: i64,
pub end: i64,
pub rank: Option<CacheRankIdentity>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CacheTier {
Device,
Host,
Disk,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LayerCachePolicy {
NoState,
KeyValue {
attention: AttentionPolicy,
num_key_value_heads: NonZeroU32,
head_dim: NonZeroU32,
},
KeyOnly {
attention: AttentionPolicy,
num_key_heads: NonZeroU32,
head_dim: NonZeroU32,
},
CompressedLatentRotary {
attention: AttentionPolicy,
latent_dim: NonZeroU32,
rotary_dim: NonZeroU32,
},
FixedState {
tensors: Vec<StateTensorPolicy>,
},
KeyValueWithFixedState {
attention: AttentionPolicy,
num_key_value_heads: NonZeroU32,
head_dim: NonZeroU32,
tensors: Vec<StateTensorPolicy>,
},
KeyOnlyWithFixedState {
attention: AttentionPolicy,
num_key_heads: NonZeroU32,
head_dim: NonZeroU32,
tensors: Vec<StateTensorPolicy>,
},
}
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateTensorRole {
Convolution {
slot: u32,
},
Recurrent,
PrefixEmbedding,
PositionDelta,
Pooling {
stream: u32,
component: PoolingStateComponent,
},
}
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PoolingStateComponent {
PendingValues,
PendingGates,
Pooled,
OverlapValues,
OverlapGates,
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateResidencyClass {
AlwaysDeviceMutable,
SealablePaged,
LayerScopedOffloadable,
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MutableStateResidency {
AlwaysDeviceMutable,
LayerScopedOffloadable,
}
impl From<MutableStateResidency> for StateResidencyClass {
fn from(value: MutableStateResidency) -> Self {
match value {
MutableStateResidency::AlwaysDeviceMutable => Self::AlwaysDeviceMutable,
MutableStateResidency::LayerScopedOffloadable => Self::LayerScopedOffloadable,
}
}
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateTensorDimension {
Batch,
PrefixTokens,
PrefixTokensDiv(NonZeroU32),
PrefixTokensRem(NonZeroU32),
Fixed(NonZeroU32),
Scalar,
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateTensorPresence {
Required,
Optional,
PrefixRemainderNonZero(NonZeroU32),
PrefixAtLeast(NonZeroU32),
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateTensorDtype {
Floating,
Float32,
Int32,
Uint32,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct StateTensorPolicy {
pub role: StateTensorRole,
pub shape: Vec<StateTensorDimension>,
pub dtype: StateTensorDtype,
pub residency: StateResidencyClass,
pub presence: StateTensorPresence,
}
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateTensorOwner {
Layer(usize),
}
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateComponentRole {
AttentionKeys,
AttentionValues,
CompressedLatent,
RotaryKeys,
Fixed(StateTensorRole),
}
impl StateComponentRole {
pub fn stable_name(self) -> String {
match self {
Self::AttentionKeys => "attention.keys".into(),
Self::AttentionValues => "attention.values".into(),
Self::CompressedLatent => "attention.compressed_latent".into(),
Self::RotaryKeys => "attention.rotary_keys".into(),
Self::Fixed(StateTensorRole::Convolution { slot }) => {
format!("state.convolution.{slot}")
}
Self::Fixed(StateTensorRole::Recurrent) => "state.recurrent".into(),
Self::Fixed(StateTensorRole::PrefixEmbedding) => "state.prefix_embedding".into(),
Self::Fixed(StateTensorRole::PositionDelta) => "state.position_delta".into(),
Self::Fixed(StateTensorRole::Pooling { stream, component }) => {
let component = match component {
PoolingStateComponent::PendingValues => "pending_values",
PoolingStateComponent::PendingGates => "pending_gates",
PoolingStateComponent::Pooled => "pooled",
PoolingStateComponent::OverlapValues => "overlap_values",
PoolingStateComponent::OverlapGates => "overlap_gates",
};
format!("state.pooling.{stream}.{component}")
}
}
}
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct StateComponentPolicy {
role: StateComponentRole,
shape: Vec<StateTensorDimension>,
dtype: StateTensorDtype,
residency: StateResidencyClass,
presence: StateTensorPresence,
}
impl StateComponentPolicy {
pub const fn role(&self) -> StateComponentRole {
self.role
}
pub fn shape(&self) -> &[StateTensorDimension] {
&self.shape
}
pub const fn dtype(&self) -> StateTensorDtype {
self.dtype
}
pub const fn residency(&self) -> StateResidencyClass {
self.residency
}
pub const fn presence(&self) -> StateTensorPresence {
self.presence
}
}
impl LayerCachePolicy {
pub const fn attention_residency_class(&self) -> Option<StateResidencyClass> {
match self {
Self::NoState | Self::FixedState { .. } => None,
Self::KeyValue { .. }
| Self::KeyOnly { .. }
| Self::CompressedLatentRotary { .. }
| Self::KeyValueWithFixedState { .. }
| Self::KeyOnlyWithFixedState { .. } => Some(StateResidencyClass::SealablePaged),
}
}
pub fn key_value(
attention: AttentionPolicy,
num_key_value_heads: i32,
head_dim: i32,
) -> Result<Self, CachePolicyError> {
let policy = Self::KeyValue {
attention,
num_key_value_heads: positive_u32(num_key_value_heads, "key/value head count")?,
head_dim: positive_u32(head_dim, "key/value head dimension")?,
};
policy.validate()?;
Ok(policy)
}
pub fn key_only(
attention: AttentionPolicy,
num_key_heads: i32,
head_dim: i32,
) -> Result<Self, CachePolicyError> {
let policy = Self::KeyOnly {
attention,
num_key_heads: positive_u32(num_key_heads, "key head count")?,
head_dim: positive_u32(head_dim, "key head dimension")?,
};
policy.validate()?;
Ok(policy)
}
pub fn compressed_latent_rotary(
attention: AttentionPolicy,
latent_dim: i32,
rotary_dim: i32,
) -> Result<Self, CachePolicyError> {
let policy = Self::CompressedLatentRotary {
attention,
latent_dim: positive_u32(latent_dim, "compressed latent dimension")?,
rotary_dim: positive_u32(rotary_dim, "rotary-key dimension")?,
};
policy.validate()?;
Ok(policy)
}
pub fn fixed_only(tensors: Vec<StateTensorPolicy>) -> Result<Self, CachePolicyError> {
let policy = Self::FixedState { tensors };
policy.validate()?;
Ok(policy)
}
pub fn key_value_with_fixed_state(
attention: AttentionPolicy,
num_key_value_heads: i32,
head_dim: i32,
tensors: Vec<StateTensorPolicy>,
) -> Result<Self, CachePolicyError> {
let policy = Self::KeyValueWithFixedState {
attention,
num_key_value_heads: positive_u32(num_key_value_heads, "key/value head count")?,
head_dim: positive_u32(head_dim, "key/value head dimension")?,
tensors,
};
policy.validate()?;
Ok(policy)
}
pub fn key_only_with_fixed_state(
attention: AttentionPolicy,
num_key_heads: i32,
head_dim: i32,
tensors: Vec<StateTensorPolicy>,
) -> Result<Self, CachePolicyError> {
let policy = Self::KeyOnlyWithFixedState {
attention,
num_key_heads: positive_u32(num_key_heads, "key head count")?,
head_dim: positive_u32(head_dim, "key head dimension")?,
tensors,
};
policy.validate()?;
Ok(policy)
}
pub const fn attention(&self) -> Option<AttentionPolicy> {
match self {
Self::NoState | Self::FixedState { .. } => None,
Self::KeyValue { attention, .. }
| Self::KeyOnly { attention, .. }
| Self::CompressedLatentRotary { attention, .. }
| Self::KeyValueWithFixedState { attention, .. }
| Self::KeyOnlyWithFixedState { attention, .. } => Some(*attention),
}
}
pub fn fixed_state(&self) -> &[StateTensorPolicy] {
match self {
Self::FixedState { tensors }
| Self::KeyValueWithFixedState { tensors, .. }
| Self::KeyOnlyWithFixedState { tensors, .. } => tensors,
_ => &[],
}
}
pub fn components(&self) -> Vec<StateComponentPolicy> {
let mut components = Vec::new();
let floating = StateTensorDtype::Floating;
let required = StateTensorPresence::Required;
match self {
Self::NoState | Self::FixedState { .. } => {}
Self::KeyValue {
num_key_value_heads,
head_dim,
..
}
| Self::KeyValueWithFixedState {
num_key_value_heads,
head_dim,
..
} => {
let shape = vec![
StateTensorDimension::Batch,
StateTensorDimension::Fixed(*num_key_value_heads),
StateTensorDimension::PrefixTokens,
StateTensorDimension::Fixed(*head_dim),
];
for role in [
StateComponentRole::AttentionKeys,
StateComponentRole::AttentionValues,
] {
components.push(StateComponentPolicy {
role,
shape: shape.clone(),
dtype: floating,
residency: StateResidencyClass::SealablePaged,
presence: required,
});
}
}
Self::KeyOnly {
num_key_heads,
head_dim,
..
}
| Self::KeyOnlyWithFixedState {
num_key_heads,
head_dim,
..
} => components.push(StateComponentPolicy {
role: StateComponentRole::AttentionKeys,
shape: vec![
StateTensorDimension::Batch,
StateTensorDimension::Fixed(*num_key_heads),
StateTensorDimension::PrefixTokens,
StateTensorDimension::Fixed(*head_dim),
],
dtype: floating,
residency: StateResidencyClass::SealablePaged,
presence: required,
}),
Self::CompressedLatentRotary {
latent_dim,
rotary_dim,
..
} => {
for (role, dimension) in [
(StateComponentRole::CompressedLatent, *latent_dim),
(StateComponentRole::RotaryKeys, *rotary_dim),
] {
components.push(StateComponentPolicy {
role,
shape: vec![
StateTensorDimension::Batch,
StateTensorDimension::PrefixTokens,
StateTensorDimension::Fixed(dimension),
],
dtype: floating,
residency: StateResidencyClass::SealablePaged,
presence: required,
});
}
}
}
components.extend(
self.fixed_state()
.iter()
.map(|tensor| StateComponentPolicy {
role: StateComponentRole::Fixed(tensor.role),
shape: tensor.shape.clone(),
dtype: tensor.dtype,
residency: tensor.residency_class(),
presence: tensor.presence,
}),
);
components
}
pub fn validate(&self) -> Result<(), CachePolicyError> {
if let Some(attention) = self.attention() {
attention
.sliding_window_i32()
.map_err(|error| CachePolicyError::Invalid(error.to_string()))?;
}
let validate_dimension = |dimension: NonZeroU32| {
(dimension.get() <= i32::MAX as u32)
.then_some(())
.ok_or_else(|| {
CachePolicyError::Invalid(format!(
"prompt-cache layer dimension {dimension} exceeds the runtime i32 range"
))
})
};
match self {
Self::NoState | Self::FixedState { .. } => {}
Self::KeyValue {
num_key_value_heads,
head_dim,
..
}
| Self::KeyValueWithFixedState {
num_key_value_heads,
head_dim,
..
} => {
validate_dimension(*num_key_value_heads)?;
validate_dimension(*head_dim)?;
}
Self::KeyOnly {
num_key_heads,
head_dim,
..
}
| Self::KeyOnlyWithFixedState {
num_key_heads,
head_dim,
..
} => {
validate_dimension(*num_key_heads)?;
validate_dimension(*head_dim)?;
}
Self::CompressedLatentRotary {
latent_dim,
rotary_dim,
..
} => {
validate_dimension(*latent_dim)?;
validate_dimension(*rotary_dim)?;
}
}
let tensors = self.fixed_state();
if tensors.is_empty()
&& matches!(
self,
Self::FixedState { .. }
| Self::KeyValueWithFixedState { .. }
| Self::KeyOnlyWithFixedState { .. }
)
{
return Err(CachePolicyError::Invalid(
"fixed-state cache policy must contain at least one tensor".into(),
));
}
validate_state_tensor_policies(tensors)
}
}
impl StateTensorDimension {
pub fn fixed(value: i32) -> Result<Self, CachePolicyError> {
positive_u32(value, "fixed-state tensor dimension").map(Self::Fixed)
}
}
impl StateTensorPolicy {
pub fn new(
role: StateTensorRole,
shape: Vec<StateTensorDimension>,
dtype: StateTensorDtype,
residency: MutableStateResidency,
) -> Result<Self, CachePolicyError> {
Self::new_with_residency(role, shape, dtype, residency.into())
}
pub fn new_with_residency(
role: StateTensorRole,
shape: Vec<StateTensorDimension>,
dtype: StateTensorDtype,
residency: StateResidencyClass,
) -> Result<Self, CachePolicyError> {
let policy = Self {
role,
shape,
dtype,
residency,
presence: StateTensorPresence::Required,
};
validate_state_tensor_policies(std::slice::from_ref(&policy))?;
Ok(policy)
}
pub const fn optional(mut self) -> Self {
self.presence = StateTensorPresence::Optional;
self
}
pub const fn when_prefix_remainder_nonzero(mut self, divisor: NonZeroU32) -> Self {
self.presence = StateTensorPresence::PrefixRemainderNonZero(divisor);
self
}
pub const fn when_prefix_at_least(mut self, divisor: NonZeroU32) -> Self {
self.presence = StateTensorPresence::PrefixAtLeast(divisor);
self
}
pub fn is_required_for(&self, prefix_tokens: usize) -> bool {
match self.presence {
StateTensorPresence::Required => true,
StateTensorPresence::Optional => false,
StateTensorPresence::PrefixRemainderNonZero(divisor) => {
!prefix_tokens.is_multiple_of(divisor.get() as usize)
}
StateTensorPresence::PrefixAtLeast(divisor) => prefix_tokens >= divisor.get() as usize,
}
}
pub fn residency_class(&self) -> StateResidencyClass {
self.residency
}
pub fn resolved_shape(
&self,
batch_size: usize,
prefix_tokens: usize,
) -> Result<Vec<i32>, CachePolicyError> {
self.shape
.iter()
.map(|dimension| match dimension {
StateTensorDimension::Batch => i32::try_from(batch_size),
StateTensorDimension::PrefixTokens => i32::try_from(prefix_tokens),
StateTensorDimension::PrefixTokensDiv(divisor) => {
i32::try_from(prefix_tokens / divisor.get() as usize)
}
StateTensorDimension::PrefixTokensRem(divisor) => {
i32::try_from(prefix_tokens % divisor.get() as usize)
}
StateTensorDimension::Fixed(value) => i32::try_from(value.get()),
StateTensorDimension::Scalar => Ok(1),
})
.collect::<Result<Vec<_>, _>>()
.map_err(|_| {
CachePolicyError::Invalid(
"fixed-state tensor dimension exceeds runtime i32 range".into(),
)
})
}
pub fn accepts_dtype_name(&self, dtype: &str) -> bool {
match self.dtype {
StateTensorDtype::Floating => {
matches!(dtype, "Float16" | "Bfloat16" | "Float32" | "Float64")
}
StateTensorDtype::Float32 => dtype == "Float32",
StateTensorDtype::Int32 => dtype == "Int32",
StateTensorDtype::Uint32 => dtype == "Uint32",
}
}
}
fn positive_u32(value: i32, field: &str) -> Result<NonZeroU32, CachePolicyError> {
u32::try_from(value)
.ok()
.and_then(NonZeroU32::new)
.ok_or_else(|| {
CachePolicyError::Invalid(format!(
"prompt-cache {field} must be positive and fit u32, got {value}"
))
})
}
fn validate_state_tensor_policies(tensors: &[StateTensorPolicy]) -> Result<(), CachePolicyError> {
let mut roles = BTreeSet::new();
for tensor in tensors {
if !roles.insert(tensor.role) {
return Err(CachePolicyError::Invalid(format!(
"duplicate fixed-state tensor role {:?}",
tensor.role
)));
}
if tensor.shape.is_empty()
|| (tensor.shape.contains(&StateTensorDimension::Scalar)
&& tensor.shape.as_slice() != [StateTensorDimension::Scalar])
{
return Err(CachePolicyError::Invalid(format!(
"invalid fixed-state tensor shape for role {:?}",
tensor.role
)));
}
let expected = match tensor.role {
StateTensorRole::Recurrent => StateResidencyClass::LayerScopedOffloadable,
StateTensorRole::Convolution { .. }
| StateTensorRole::PrefixEmbedding
| StateTensorRole::PositionDelta => StateResidencyClass::AlwaysDeviceMutable,
StateTensorRole::Pooling {
component: PoolingStateComponent::Pooled,
..
} => StateResidencyClass::SealablePaged,
StateTensorRole::Pooling { .. } => StateResidencyClass::AlwaysDeviceMutable,
};
if tensor.residency != expected {
return Err(CachePolicyError::Invalid(format!(
"fixed-state tensor role {:?} requires {:?} residency, got {:?}",
tensor.role, expected, tensor.residency
)));
}
}
Ok(())
}
#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
pub enum CachePolicyError {
#[error("{0}")]
Invalid(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validates_layer_and_fixed_state_contracts() {
let recurrent = StateTensorPolicy::new(
StateTensorRole::Recurrent,
vec![
StateTensorDimension::Batch,
StateTensorDimension::fixed(16).unwrap(),
],
StateTensorDtype::Floating,
MutableStateResidency::LayerScopedOffloadable,
)
.unwrap();
let layer = LayerCachePolicy::key_value_with_fixed_state(
AttentionPolicy::sliding(128).unwrap(),
8,
64,
vec![recurrent.clone()],
)
.unwrap();
assert_eq!(
layer.attention_residency_class(),
Some(StateResidencyClass::SealablePaged)
);
assert_eq!(recurrent.resolved_shape(2, 9).unwrap(), vec![2, 16]);
assert!(recurrent.accepts_dtype_name("Float16"));
assert!(!recurrent.accepts_dtype_name("Int32"));
}
#[test]
fn rejects_invalid_policy_without_a_backend() {
assert!(LayerCachePolicy::key_value(AttentionPolicy::Full, 0, 64).is_err());
assert!(StateTensorPolicy::new(
StateTensorRole::Recurrent,
vec![StateTensorDimension::Scalar, StateTensorDimension::Batch],
StateTensorDtype::Floating,
MutableStateResidency::LayerScopedOffloadable,
)
.is_err());
}
#[test]
fn policy_schema_round_trips() {
let policy = LayerCachePolicy::key_only(AttentionPolicy::Full, 4, 32).unwrap();
let json = serde_json::to_string(&policy).unwrap();
assert_eq!(
serde_json::from_str::<LayerCachePolicy>(&json).unwrap(),
policy
);
}
}