use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::agent::error::AgentResult;
#[async_trait]
pub trait Reducer: Send + Sync {
async fn reduce(&self, current: Option<&Value>, update: &Value) -> AgentResult<Value>;
fn name(&self) -> &str;
fn reducer_type(&self) -> ReducerType;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ReducerType {
Overwrite,
Append,
Extend,
Merge {
deep: bool,
},
LastN {
n: usize,
},
First,
Last,
Custom(String),
}
impl Default for ReducerType {
fn default() -> Self {
Self::Overwrite
}
}
impl std::fmt::Display for ReducerType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ReducerType::Overwrite => write!(f, "overwrite"),
ReducerType::Append => write!(f, "append"),
ReducerType::Extend => write!(f, "extend"),
ReducerType::Merge { deep } => write!(f, "merge(deep={})", deep),
ReducerType::LastN { n } => write!(f, "last_n({})", n),
ReducerType::First => write!(f, "first"),
ReducerType::Last => write!(f, "last"),
ReducerType::Custom(name) => write!(f, "custom({})", name),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateUpdate {
pub key: String,
pub value: Value,
}
impl StateUpdate {
pub fn new(key: impl Into<String>, value: Value) -> Self {
Self {
key: key.into(),
value,
}
}
pub fn from_serializable<T: Serialize>(key: impl Into<String>, value: &T) -> AgentResult<Self> {
Ok(Self::new(key, serde_json::to_value(value)?))
}
}
impl From<(String, Value)> for StateUpdate {
fn from((key, value): (String, Value)) -> Self {
Self::new(key, value)
}
}
impl From<(&str, Value)> for StateUpdate {
fn from((key, value): (&str, Value)) -> Self {
Self::new(key, value)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_state_update_creation() {
let update = StateUpdate::new("key", json!("value"));
assert_eq!(update.key, "key");
assert_eq!(update.value, json!("value"));
}
#[test]
fn test_state_update_from_tuple() {
let update: StateUpdate = ("message", json!("hello")).into();
assert_eq!(update.key, "message");
assert_eq!(update.value, json!("hello"));
}
#[test]
fn test_reducer_type_display() {
assert_eq!(ReducerType::Overwrite.to_string(), "overwrite");
assert_eq!(
ReducerType::Merge { deep: true }.to_string(),
"merge(deep=true)"
);
assert_eq!(ReducerType::LastN { n: 5 }.to_string(), "last_n(5)");
}
}