1use crate::errors::{GraphError, GraphResult};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::fmt::Debug;
11use std::sync::Arc;
12
13pub trait StateSchema:
15 Clone + Send + Sync + 'static + Serialize + for<'de> Deserialize<'de> + Debug
16{
17 fn from_input(input: Self) -> Self {
19 input
20 }
21
22 fn to_json(&self) -> GraphResult<serde_json::Value> {
28 serde_json::to_value(self).map_err(|e| {
29 GraphError::StateError(format!("Failed to serialize state to JSON: {}", e))
30 })
31 }
32}
33
34#[derive(Debug, Clone, Serialize)]
39pub struct StateUpdate<S: StateSchema> {
40 pub update: Option<S>,
42
43 pub metadata: HashMap<String, serde_json::Value>,
45}
46
47impl<S: StateSchema> StateUpdate<S> {
48 pub fn full(state: S) -> Self {
50 Self {
51 update: Some(state),
52 metadata: HashMap::new(),
53 }
54 }
55
56 pub fn with_metadata(state: S, metadata: HashMap<String, serde_json::Value>) -> Self {
58 Self {
59 update: Some(state),
60 metadata,
61 }
62 }
63
64 pub fn unchanged() -> Self {
66 Self {
67 update: None,
68 metadata: HashMap::new(),
69 }
70 }
71
72 pub fn add_metadata(&mut self, key: impl Into<String>, value: serde_json::Value) {
74 self.metadata.insert(key.into(), value);
75 }
76}
77
78pub trait Reducer<S: StateSchema>: Send + Sync {
83 fn reduce(&self, current: &S, update: &S) -> S;
85}
86
87pub struct ReplaceReducer;
89
90impl<S: StateSchema> Reducer<S> for ReplaceReducer {
91 fn reduce(&self, _current: &S, update: &S) -> S {
92 update.clone()
93 }
94}
95
96pub struct MergeReducer<S: StateSchema> {
106 fallback: Arc<dyn Reducer<S>>,
107 fields: Vec<Arc<dyn Reducer<S>>>,
108}
109
110impl<S: StateSchema> MergeReducer<S> {
111 pub fn new(fallback: Arc<dyn Reducer<S>>, fields: Vec<Arc<dyn Reducer<S>>>) -> Self {
113 Self { fallback, fields }
114 }
115}
116
117impl<S: StateSchema> Reducer<S> for MergeReducer<S> {
118 fn reduce(&self, current: &S, update: &S) -> S {
119 let mut merged = self.fallback.reduce(current, update);
120 for reducer in &self.fields {
121 let next = reducer.reduce(current, &merged);
122 merged = next;
123 }
124 merged
125 }
126}
127
128pub struct AppendReducer<S: StateSchema, T: Clone + Send + Sync> {
133 pub field_accessor: fn(&S) -> &[T],
135 pub field_mutator: fn(&mut S, Vec<T>),
137}
138
139impl<S: StateSchema, T: Clone + Send + Sync> Reducer<S> for AppendReducer<S, T> {
140 fn reduce(&self, current: &S, update: &S) -> S {
141 let current_items = (self.field_accessor)(current);
142 let update_items = (self.field_accessor)(update);
143
144 let mut merged: Vec<T> = current_items.to_vec();
145 merged.extend(update_items.iter().cloned());
146
147 let mut result = current.clone();
148 (self.field_mutator)(&mut result, merged);
149 result
150 }
151}
152
153pub struct AppendMessagesReducer;
155
156impl Reducer<AgentState> for AppendMessagesReducer {
157 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
158 let mut result = update.clone();
159 result.messages = current.messages.clone();
160 result.messages.extend(update.messages.iter().cloned());
161 result
162 }
163}
164
165pub struct AppendStepsReducer;
167
168impl Reducer<AgentState> for AppendStepsReducer {
169 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
170 let mut result = update.clone();
171 result.steps = current.steps.clone();
172 result.steps.extend(update.steps.iter().cloned());
173 result
174 }
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct AgentState {
183 pub input: String,
185
186 pub messages: Vec<MessageEntry>,
188
189 pub steps: Vec<StepEntry>,
191
192 pub output: Option<String>,
194}
195
196impl StateSchema for AgentState {}
197
198impl AgentState {
199 pub fn new(input: impl Into<String>) -> Self {
201 let input = input.into();
202 let msg = MessageEntry::human(input.clone());
203 Self {
204 input,
205 messages: vec![msg],
206 steps: vec![],
207 output: None,
208 }
209 }
210
211 pub fn add_message(&mut self, message: MessageEntry) {
213 self.messages.push(message);
214 }
215
216 pub fn add_step(&mut self, step: StepEntry) {
218 self.steps.push(step);
219 }
220
221 pub fn set_output(&mut self, output: impl Into<String>) {
223 self.output = Some(output.into());
224 }
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct MessageEntry {
230 pub role: MessageRole,
232 pub content: String,
234}
235
236impl MessageEntry {
237 pub fn human(content: impl Into<String>) -> Self {
239 Self {
240 role: MessageRole::Human,
241 content: content.into(),
242 }
243 }
244
245 pub fn ai(content: impl Into<String>) -> Self {
247 Self {
248 role: MessageRole::AI,
249 content: content.into(),
250 }
251 }
252
253 pub fn system(content: impl Into<String>) -> Self {
255 Self {
256 role: MessageRole::System,
257 content: content.into(),
258 }
259 }
260
261 pub fn tool(content: impl Into<String>) -> Self {
263 Self {
264 role: MessageRole::Tool,
265 content: content.into(),
266 }
267 }
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
272pub enum MessageRole {
273 System,
275 Human,
277 AI,
279 Tool,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct StepEntry {
286 pub action: String,
288 pub observation: String,
290}
291
292impl StepEntry {
293 pub fn new(action: impl Into<String>, observation: impl Into<String>) -> Self {
295 Self {
296 action: action.into(),
297 observation: observation.into(),
298 }
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_append_messages_reducer() {
308 let mut current = AgentState::new("Hello".to_string());
309 current.add_message(MessageEntry::ai("Response 1".to_string()));
310
311 let mut update = AgentState::new("Hello".to_string());
312 update.add_message(MessageEntry::ai("Response 2".to_string()));
313 update.set_output("Done".to_string());
314
315 let reducer = AppendMessagesReducer;
316 let result = reducer.reduce(¤t, &update);
317
318 assert_eq!(result.messages.len(), 4);
319 assert_eq!(result.output, Some("Done".to_string()));
320 }
321
322 #[test]
323 fn test_append_steps_reducer() {
324 let mut current = AgentState::new("Test".to_string());
325 current.add_step(StepEntry::new(
326 "Action 1".to_string(),
327 "Result 1".to_string(),
328 ));
329
330 let mut update = AgentState::new("Test".to_string());
331 update.add_step(StepEntry::new(
332 "Action 2".to_string(),
333 "Result 2".to_string(),
334 ));
335
336 let reducer = AppendStepsReducer;
337 let result = reducer.reduce(¤t, &update);
338
339 assert_eq!(result.steps.len(), 2);
340 }
341}