ferrum_interfaces/
recurrent_state.rs1use ferrum_types::{DataType, Device, RequestId, Result};
8use serde::{Deserialize, Serialize};
9use std::{any::Any, sync::Arc, time::Instant};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct RecurrentStateTensorSpec {
14 pub layer_index: usize,
16 pub name: String,
18 pub shape: Vec<usize>,
20 pub dtype: DataType,
22}
23
24impl RecurrentStateTensorSpec {
25 pub fn new(
26 layer_index: usize,
27 name: impl Into<String>,
28 shape: Vec<usize>,
29 dtype: DataType,
30 ) -> Self {
31 Self {
32 layer_index,
33 name: name.into(),
34 shape,
35 dtype,
36 }
37 }
38
39 pub fn checked_num_elements(&self) -> Option<usize> {
40 self.shape
41 .iter()
42 .copied()
43 .try_fold(1usize, usize::checked_mul)
44 }
45
46 pub fn num_elements(&self) -> usize {
47 self.checked_num_elements().unwrap_or(usize::MAX)
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct RecurrentStateSpec {
54 pub request_id: RequestId,
56 pub num_layers: usize,
58 pub tensors: Vec<RecurrentStateTensorSpec>,
60 pub device: Device,
62 pub max_batch_slots: usize,
64}
65
66impl RecurrentStateSpec {
67 pub fn estimated_memory_bytes(&self) -> usize {
68 let state_bytes_per_slot = self.tensors.iter().fold(0usize, |total, tensor| {
69 total.saturating_add(
70 tensor
71 .num_elements()
72 .saturating_mul(tensor.dtype.size_bytes()),
73 )
74 });
75 state_bytes_per_slot.saturating_mul(self.max_batch_slots)
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
81pub enum RecurrentStateResumePolicy {
82 RecomputeOnResume,
84 SnapshotOnPreempt,
86}
87
88#[derive(Debug, Clone)]
90pub struct RecurrentStateHandleStats {
91 pub memory_bytes: usize,
93 pub state_tensors: usize,
95 pub batch_slots: usize,
97 pub last_access: Instant,
99}
100
101pub trait RecurrentStateHandle: Send + Sync + std::fmt::Debug {
103 fn request_id(&self) -> RequestId;
105
106 fn device(&self) -> Device;
108
109 fn num_layers(&self) -> usize;
111
112 fn state_bytes(&self) -> usize;
114
115 fn clone_handle(&self) -> Result<Arc<dyn RecurrentStateHandle>>;
117
118 fn as_any(&self) -> &dyn Any;
120
121 fn stats(&self) -> RecurrentStateHandleStats;
123
124 fn is_valid(&self) -> bool;
126
127 fn cache_id(&self) -> String;
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct RecurrentStateManagerStats {
134 pub total_memory_bytes: usize,
136 pub used_memory_bytes: usize,
138 pub active_states: usize,
140 pub active_state_tensors: usize,
142 pub total_batch_slots: usize,
144 pub used_batch_slots: usize,
146 pub allocation_count: u64,
148 pub allocation_failures: u64,
150 pub eviction_count: u64,
152}
153
154#[async_trait::async_trait]
156pub trait RecurrentStateManager: Send + Sync {
157 async fn allocate(&self, spec: &RecurrentStateSpec) -> Result<Arc<dyn RecurrentStateHandle>>;
159
160 async fn deallocate(&self, request_id: RequestId) -> Result<()>;
162
163 fn can_allocate(&self, spec: &RecurrentStateSpec) -> bool;
165
166 fn get_handle(&self, request_id: RequestId) -> Option<Arc<dyn RecurrentStateHandle>>;
168
169 fn list_handles(&self) -> Vec<(RequestId, Arc<dyn RecurrentStateHandle>)>;
171
172 fn stats(&self) -> RecurrentStateManagerStats;
174
175 async fn reset(&self) -> Result<()>;
177}