1use crate::errors::{GraphError, GraphResult};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::fmt::Debug;
11
12pub trait StateSchema:
14 Clone + Send + Sync + 'static + Serialize + for<'de> Deserialize<'de> + Debug
15{
16 fn from_input(input: Self) -> Self {
18 input
19 }
20
21 fn to_json(&self) -> GraphResult<serde_json::Value> {
27 serde_json::to_value(self).map_err(|e| {
28 GraphError::StateError(format!("Failed to serialize state to JSON: {}", e))
29 })
30 }
31}
32
33#[derive(Debug, Clone, Serialize)]
38pub struct StateUpdate<S: StateSchema> {
39 pub update: Option<S>,
41
42 pub metadata: HashMap<String, serde_json::Value>,
44}
45
46impl<S: StateSchema> StateUpdate<S> {
47 pub fn full(state: S) -> Self {
49 Self {
50 update: Some(state),
51 metadata: HashMap::new(),
52 }
53 }
54
55 pub fn with_metadata(state: S, metadata: HashMap<String, serde_json::Value>) -> Self {
57 Self {
58 update: Some(state),
59 metadata,
60 }
61 }
62
63 pub fn unchanged() -> Self {
65 Self {
66 update: None,
67 metadata: HashMap::new(),
68 }
69 }
70
71 pub fn add_metadata(&mut self, key: impl Into<String>, value: serde_json::Value) {
73 self.metadata.insert(key.into(), value);
74 }
75}
76
77pub trait Reducer<S: StateSchema>: Send + Sync {
82 fn reduce(&self, current: &S, update: &S) -> S;
84}
85
86pub struct ReplaceReducer;
88
89impl<S: StateSchema> Reducer<S> for ReplaceReducer {
90 fn reduce(&self, _current: &S, update: &S) -> S {
91 update.clone()
92 }
93}
94
95pub struct AppendReducer<S: StateSchema, T: Clone + Send + Sync> {
100 pub field_accessor: fn(&S) -> &[T],
102 pub field_mutator: fn(&mut S, Vec<T>),
104}
105
106impl<S: StateSchema, T: Clone + Send + Sync> Reducer<S> for AppendReducer<S, T> {
107 fn reduce(&self, current: &S, update: &S) -> S {
108 let current_items = (self.field_accessor)(current);
109 let update_items = (self.field_accessor)(update);
110
111 let mut merged: Vec<T> = current_items.to_vec();
112 merged.extend(update_items.iter().cloned());
113
114 let mut result = current.clone();
115 (self.field_mutator)(&mut result, merged);
116 result
117 }
118}
119
120pub struct AppendMessagesReducer;
122
123impl Reducer<AgentState> for AppendMessagesReducer {
124 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
125 let mut result = update.clone();
126 result.messages = current.messages.clone();
127 result.messages.extend(update.messages.iter().cloned());
128 result
129 }
130}
131
132pub struct AppendStepsReducer;
134
135impl Reducer<AgentState> for AppendStepsReducer {
136 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
137 let mut result = update.clone();
138 result.steps = current.steps.clone();
139 result.steps.extend(update.steps.iter().cloned());
140 result
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct AgentState {
150 pub input: String,
152
153 pub messages: Vec<MessageEntry>,
155
156 pub steps: Vec<StepEntry>,
158
159 pub output: Option<String>,
161}
162
163impl StateSchema for AgentState {}
164
165impl AgentState {
166 pub fn new(input: impl Into<String>) -> Self {
168 let input = input.into();
169 let msg = MessageEntry::human(input.clone());
170 Self {
171 input,
172 messages: vec![msg],
173 steps: vec![],
174 output: None,
175 }
176 }
177
178 pub fn add_message(&mut self, message: MessageEntry) {
180 self.messages.push(message);
181 }
182
183 pub fn add_step(&mut self, step: StepEntry) {
185 self.steps.push(step);
186 }
187
188 pub fn set_output(&mut self, output: impl Into<String>) {
190 self.output = Some(output.into());
191 }
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct MessageEntry {
197 pub role: MessageRole,
199 pub content: String,
201}
202
203impl MessageEntry {
204 pub fn human(content: impl Into<String>) -> Self {
206 Self {
207 role: MessageRole::Human,
208 content: content.into(),
209 }
210 }
211
212 pub fn ai(content: impl Into<String>) -> Self {
214 Self {
215 role: MessageRole::AI,
216 content: content.into(),
217 }
218 }
219
220 pub fn system(content: impl Into<String>) -> Self {
222 Self {
223 role: MessageRole::System,
224 content: content.into(),
225 }
226 }
227
228 pub fn tool(content: impl Into<String>) -> Self {
230 Self {
231 role: MessageRole::Tool,
232 content: content.into(),
233 }
234 }
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
239pub enum MessageRole {
240 System,
242 Human,
244 AI,
246 Tool,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct StepEntry {
253 pub action: String,
255 pub observation: String,
257}
258
259impl StepEntry {
260 pub fn new(action: impl Into<String>, observation: impl Into<String>) -> Self {
262 Self {
263 action: action.into(),
264 observation: observation.into(),
265 }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_append_messages_reducer() {
275 let mut current = AgentState::new("Hello".to_string());
276 current.add_message(MessageEntry::ai("Response 1".to_string()));
277
278 let mut update = AgentState::new("Hello".to_string());
279 update.add_message(MessageEntry::ai("Response 2".to_string()));
280 update.set_output("Done".to_string());
281
282 let reducer = AppendMessagesReducer;
283 let result = reducer.reduce(¤t, &update);
284
285 assert_eq!(result.messages.len(), 4);
286 assert_eq!(result.output, Some("Done".to_string()));
287 }
288
289 #[test]
290 fn test_append_steps_reducer() {
291 let mut current = AgentState::new("Test".to_string());
292 current.add_step(StepEntry::new(
293 "Action 1".to_string(),
294 "Result 1".to_string(),
295 ));
296
297 let mut update = AgentState::new("Test".to_string());
298 update.add_step(StepEntry::new(
299 "Action 2".to_string(),
300 "Result 2".to_string(),
301 ));
302
303 let reducer = AppendStepsReducer;
304 let result = reducer.reduce(¤t, &update);
305
306 assert_eq!(result.steps.len(), 2);
307 }
308}