1use crate::error::{AgentGraphError, Result};
2use crate::reducer::Reducer;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::HashMap;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8use std::time::Duration;
9use tokio::sync::RwLock;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct StateLimits {
14 pub max_keys: usize,
16 pub max_value_bytes: usize,
18 pub max_history_len: usize,
20 pub lock_timeout: Duration,
22}
23
24impl Default for StateLimits {
25 fn default() -> Self {
26 Self {
27 max_keys: 10_000,
28 max_value_bytes: 1_048_576,
29 max_history_len: 100,
30 lock_timeout: Duration::from_secs(5),
31 }
32 }
33}
34
35pub struct StateTransaction {
41 state: AgentState,
42 working: RwLock<HashMap<String, Value>>,
43 snapshot_version: u64,
44 committed: bool,
45}
46
47impl StateTransaction {
48 pub async fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T> {
50 let working = self.working.read().await;
51 let value = working
52 .get(key)
53 .ok_or_else(|| AgentGraphError::StateError(format!("Key not found: {}", key)))?;
54
55 serde_json::from_value(value.clone()).map_err(|e| {
56 AgentGraphError::StateError(format!("Failed to deserialize {}: {}", key, e))
57 })
58 }
59
60 pub async fn set<T: Serialize>(&self, key: &str, value: T) -> Result<()> {
62 let json_value = self.state.serialize_value(key, value)?;
63 let existing = {
64 let working = self.working.read().await;
65 working.get(key).cloned()
66 };
67 let next_value = if let Some(existing) = existing.as_ref() {
68 self.state
69 .reduce_value_if_needed(key, existing, &json_value)
70 .await?
71 } else {
72 json_value
73 };
74
75 let mut working = self.working.write().await;
76 self.state.validate_insert(&working, key, &next_value)?;
77 working.insert(key.to_string(), next_value);
78 Ok(())
79 }
80
81 pub async fn commit(mut self) -> Result<()> {
86 let current_version = self.state.version.load(Ordering::SeqCst);
87 if current_version != self.snapshot_version {
88 return Err(AgentGraphError::StateError(
89 "Transaction conflict: state was modified concurrently".to_string(),
90 ));
91 }
92 let next = self.working.read().await.clone();
93 self.state.replace_data(next).await;
95 self.committed = true;
96 Ok(())
97 }
98
99 pub async fn rollback(mut self) {
101 self.committed = true;
102 }
103}
104
105impl Drop for StateTransaction {
106 fn drop(&mut self) {
107 if !self.committed {
108 tracing::debug!("dropping uncommitted state transaction");
109 }
110 }
111}
112
113#[derive(Clone)]
116pub struct AgentState {
117 data: Arc<RwLock<HashMap<String, Value>>>,
118 history: Arc<RwLock<Vec<StateSnapshot>>>,
119 pub(crate) reducers: Arc<RwLock<HashMap<String, Arc<dyn Reducer>>>>,
120 limits: StateLimits,
121 version: Arc<AtomicU64>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct StateSnapshot {
126 pub timestamp: chrono::DateTime<chrono::Utc>,
127 pub data: HashMap<String, Value>,
128}
129
130impl AgentState {
131 pub fn new() -> Self {
133 Self::with_limits(StateLimits::default())
134 }
135
136 pub fn with_limits(limits: StateLimits) -> Self {
138 Self {
139 data: Arc::new(RwLock::new(HashMap::new())),
140 history: Arc::new(RwLock::new(Vec::new())),
141 reducers: Arc::new(RwLock::new(HashMap::new())),
142 limits,
143 version: Arc::new(AtomicU64::new(0)),
144 }
145 }
146
147 pub fn with_data(data: HashMap<String, Value>) -> Self {
149 Self {
150 data: Arc::new(RwLock::new(data)),
151 history: Arc::new(RwLock::new(Vec::new())),
152 reducers: Arc::new(RwLock::new(HashMap::new())),
153 limits: StateLimits::default(),
154 version: Arc::new(AtomicU64::new(0)),
155 }
156 }
157
158 pub fn with_data_and_limits(data: HashMap<String, Value>, limits: StateLimits) -> Self {
160 Self {
161 data: Arc::new(RwLock::new(data)),
162 history: Arc::new(RwLock::new(Vec::new())),
163 reducers: Arc::new(RwLock::new(HashMap::new())),
164 limits,
165 version: Arc::new(AtomicU64::new(0)),
166 }
167 }
168
169 pub async fn register_reducer(&self, key: impl Into<String>, reducer: impl Reducer + 'static) {
173 match self.write_reducers().await {
174 Ok(mut reducers) => {
175 reducers.insert(key.into(), Arc::new(reducer));
176 }
177 Err(error) => {
178 tracing::warn!(error = %error, "failed to register reducer");
179 }
180 }
181 }
182
183 pub async fn get<T>(&self, key: &str) -> Result<T>
185 where
186 T: serde::de::DeserializeOwned,
187 {
188 let data = self.read_data().await?;
189 let value = data
190 .get(key)
191 .ok_or_else(|| AgentGraphError::StateError(format!("Key not found: {}", key)))?;
192
193 serde_json::from_value(value.clone()).map_err(|e| {
194 AgentGraphError::StateError(format!("Failed to deserialize {}: {}", key, e))
195 })
196 }
197
198 pub async fn get_opt<T>(&self, key: &str) -> Result<Option<T>>
200 where
201 T: serde::de::DeserializeOwned,
202 {
203 let data = self.read_data().await?;
204 match data.get(key) {
205 Some(value) => {
206 let v = serde_json::from_value(value.clone())?;
207 Ok(Some(v))
208 }
209 None => Ok(None),
210 }
211 }
212
213 pub async fn set<T>(&self, key: &str, value: T) -> Result<()>
217 where
218 T: Serialize,
219 {
220 let json_value = self.serialize_value(key, value)?;
221 let existing = {
222 let data = self.read_data().await?;
223 data.get(key).cloned()
224 };
225 let next_value = if let Some(existing) = existing.as_ref() {
226 self.reduce_value_if_needed(key, existing, &json_value)
227 .await?
228 } else {
229 json_value
230 };
231
232 let mut data = self.write_data().await?;
233 self.validate_insert(&data, key, &next_value)?;
234 data.insert(key.to_string(), next_value);
235 self.version.fetch_add(1, Ordering::SeqCst);
236 Ok(())
237 }
238
239 pub async fn set_raw(&self, key: &str, value: Value) -> Result<()> {
241 let mut data = self.write_data().await?;
242 self.validate_insert(&data, key, &value)?;
243 data.insert(key.to_string(), value);
244 self.version.fetch_add(1, Ordering::SeqCst);
245 Ok(())
246 }
247
248 pub async fn apply_reducer(&self, key: &str, current: &Value, new: &Value) -> Result<Value> {
251 let reducers = self.read_reducers().await?;
252 if let Some(reducer) = reducers.get(key) {
253 reducer.reduce(current, new)
254 } else {
255 Ok(new.clone())
256 }
257 }
258
259 pub async fn update<T, F>(&self, key: &str, f: F) -> Result<()>
261 where
262 T: serde::de::DeserializeOwned + Serialize,
263 F: FnOnce(T) -> T,
264 {
265 let mut data = self.write_data().await?;
266
267 if let Some(value) = data.get(key).cloned() {
268 let current: T = serde_json::from_value(value)?;
269 let updated = f(current);
270 let updated = self.serialize_value(key, updated)?;
271 self.validate_insert(&data, key, &updated)?;
272 data.insert(key.to_string(), updated);
273 self.version.fetch_add(1, Ordering::SeqCst);
274 }
275
276 Ok(())
277 }
278
279 pub async fn contains(&self, key: &str) -> bool {
281 match self.read_data().await {
282 Ok(data) => data.contains_key(key),
283 Err(_) => false,
284 }
285 }
286
287 pub async fn remove(&self, key: &str) -> Option<Value> {
289 match self.write_data().await {
290 Ok(mut data) => {
291 let removed = data.remove(key);
292 if removed.is_some() {
293 self.version.fetch_add(1, Ordering::SeqCst);
294 }
295 removed
296 }
297 Err(_) => None,
298 }
299 }
300
301 pub async fn keys(&self) -> Vec<String> {
303 match self.read_data().await {
304 Ok(data) => data.keys().cloned().collect(),
305 Err(_) => Vec::new(),
306 }
307 }
308
309 pub async fn snapshot(&self) -> StateSnapshot {
311 StateSnapshot {
312 timestamp: chrono::Utc::now(),
313 data: self
314 .read_data()
315 .await
316 .map(|data| data.clone())
317 .unwrap_or_default(),
318 }
319 }
320
321 pub async fn save_to_history(&self) {
323 let snapshot = self.snapshot().await;
324 if let Ok(mut history) = self.write_history().await {
325 if history.len() >= self.limits.max_history_len && !history.is_empty() {
326 history.remove(0);
327 }
328 history.push(snapshot);
329 }
330 }
331
332 pub async fn restore(&self, snapshot: &StateSnapshot) {
334 self.replace_data(snapshot.data.clone()).await;
335 }
336
337 pub async fn get_history(&self) -> Vec<StateSnapshot> {
339 self.read_history()
340 .await
341 .map(|history| history.clone())
342 .unwrap_or_default()
343 }
344
345 pub async fn export(&self) -> HashMap<String, Value> {
347 self.read_data()
348 .await
349 .map(|data| data.clone())
350 .unwrap_or_default()
351 }
352
353 pub async fn transaction(&self) -> StateTransaction {
355 let snapshot_version = self.version.load(Ordering::SeqCst);
356 StateTransaction {
357 state: self.clone(),
358 working: RwLock::new(self.export().await),
359 snapshot_version,
360 committed: false,
361 }
362 }
363
364 pub async fn fork(&self) -> AgentState {
367 let data = self.export().await;
368 AgentState {
369 data: Arc::new(RwLock::new(data)),
370 history: Arc::new(RwLock::new(Vec::new())),
371 reducers: self.reducers.clone(),
372 limits: self.limits.clone(),
373 version: Arc::new(AtomicU64::new(0)),
374 }
375 }
376
377 fn serialize_value<T: Serialize>(&self, key: &str, value: T) -> Result<Value> {
378 let json_value = serde_json::to_value(value)?;
379 self.validate_value_size(key, &json_value)?;
380 Ok(json_value)
381 }
382
383 fn validate_value_size(&self, key: &str, value: &Value) -> Result<()> {
384 let bytes = serde_json::to_vec(value)?.len();
385 if bytes > self.limits.max_value_bytes {
386 return Err(AgentGraphError::StateError(format!(
387 "Value for key '{}' exceeds max size: {} > {} bytes",
388 key, bytes, self.limits.max_value_bytes
389 )));
390 }
391 Ok(())
392 }
393
394 fn validate_insert(
395 &self,
396 data: &HashMap<String, Value>,
397 key: &str,
398 value: &Value,
399 ) -> Result<()> {
400 self.validate_value_size(key, value)?;
401 if !data.contains_key(key) && data.len() >= self.limits.max_keys {
402 return Err(AgentGraphError::StateError(format!(
403 "State key limit exceeded: {} >= {}",
404 data.len() + 1,
405 self.limits.max_keys
406 )));
407 }
408 Ok(())
409 }
410
411 async fn reduce_value_if_needed(
412 &self,
413 key: &str,
414 current: &Value,
415 new: &Value,
416 ) -> Result<Value> {
417 let reducers = self.read_reducers().await?;
418 if let Some(reducer) = reducers.get(key) {
419 reducer.reduce(current, new)
420 } else {
421 Ok(new.clone())
422 }
423 }
424
425 async fn replace_data(&self, next: HashMap<String, Value>) {
426 if let Err(error) = self.validate_state_map(&next) {
427 tracing::warn!(error = %error, "rejected state replacement that exceeded limits");
428 return;
429 }
430
431 if let Ok(mut data) = self.write_data().await {
432 *data = next;
433 self.version.fetch_add(1, Ordering::SeqCst);
434 }
435 }
436
437 fn validate_state_map(&self, data: &HashMap<String, Value>) -> Result<()> {
438 if data.len() > self.limits.max_keys {
439 return Err(AgentGraphError::StateError(format!(
440 "State key limit exceeded: {} > {}",
441 data.len(),
442 self.limits.max_keys
443 )));
444 }
445
446 for (key, value) in data {
447 self.validate_value_size(key, value)?;
448 }
449
450 Ok(())
451 }
452
453 async fn read_data(&self) -> Result<tokio::sync::RwLockReadGuard<'_, HashMap<String, Value>>> {
454 tokio::time::timeout(self.limits.lock_timeout, self.data.read())
455 .await
456 .map_err(|_| {
457 AgentGraphError::StateError(format!(
458 "Timed out acquiring state read lock after {} ms",
459 self.limits.lock_timeout.as_millis()
460 ))
461 })
462 }
463
464 async fn write_data(
465 &self,
466 ) -> Result<tokio::sync::RwLockWriteGuard<'_, HashMap<String, Value>>> {
467 tokio::time::timeout(self.limits.lock_timeout, self.data.write())
468 .await
469 .map_err(|_| {
470 AgentGraphError::StateError(format!(
471 "Timed out acquiring state write lock after {} ms",
472 self.limits.lock_timeout.as_millis()
473 ))
474 })
475 }
476
477 async fn read_history(&self) -> Result<tokio::sync::RwLockReadGuard<'_, Vec<StateSnapshot>>> {
478 tokio::time::timeout(self.limits.lock_timeout, self.history.read())
479 .await
480 .map_err(|_| {
481 AgentGraphError::StateError(format!(
482 "Timed out acquiring state history read lock after {} ms",
483 self.limits.lock_timeout.as_millis()
484 ))
485 })
486 }
487
488 async fn write_history(&self) -> Result<tokio::sync::RwLockWriteGuard<'_, Vec<StateSnapshot>>> {
489 tokio::time::timeout(self.limits.lock_timeout, self.history.write())
490 .await
491 .map_err(|_| {
492 AgentGraphError::StateError(format!(
493 "Timed out acquiring state history write lock after {} ms",
494 self.limits.lock_timeout.as_millis()
495 ))
496 })
497 }
498
499 async fn read_reducers(
500 &self,
501 ) -> Result<tokio::sync::RwLockReadGuard<'_, HashMap<String, Arc<dyn Reducer>>>> {
502 tokio::time::timeout(self.limits.lock_timeout, self.reducers.read())
503 .await
504 .map_err(|_| {
505 AgentGraphError::StateError(format!(
506 "Timed out acquiring reducer read lock after {} ms",
507 self.limits.lock_timeout.as_millis()
508 ))
509 })
510 }
511
512 async fn write_reducers(
513 &self,
514 ) -> Result<tokio::sync::RwLockWriteGuard<'_, HashMap<String, Arc<dyn Reducer>>>> {
515 tokio::time::timeout(self.limits.lock_timeout, self.reducers.write())
516 .await
517 .map_err(|_| {
518 AgentGraphError::StateError(format!(
519 "Timed out acquiring reducer write lock after {} ms",
520 self.limits.lock_timeout.as_millis()
521 ))
522 })
523 }
524}
525
526impl Default for AgentState {
527 fn default() -> Self {
528 Self::new()
529 }
530}
531
532impl std::fmt::Debug for AgentState {
533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534 f.debug_struct("AgentState")
535 .field("data", &"<locked>")
536 .finish()
537 }
538}