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: String, value: serde_json::Value) {
73 self.metadata.insert(key, 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],
101 pub field_mutator: fn(&mut S, Vec<T>),
102}
103
104impl<S: StateSchema, T: Clone + Send + Sync> Reducer<S> for AppendReducer<S, T> {
105 fn reduce(&self, current: &S, update: &S) -> S {
106 let current_items = (self.field_accessor)(current);
107 let update_items = (self.field_accessor)(update);
108
109 let mut merged: Vec<T> = current_items.to_vec();
110 merged.extend(update_items.iter().cloned());
111
112 let mut result = current.clone();
113 (self.field_mutator)(&mut result, merged);
114 result
115 }
116}
117
118pub struct AppendMessagesReducer;
120
121impl Reducer<AgentState> for AppendMessagesReducer {
122 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
123 let mut result = update.clone();
124 result.messages = current.messages.clone();
125 result.messages.extend(update.messages.iter().cloned());
126 result
127 }
128}
129
130pub struct AppendStepsReducer;
132
133impl Reducer<AgentState> for AppendStepsReducer {
134 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
135 let mut result = update.clone();
136 result.steps = current.steps.clone();
137 result.steps.extend(update.steps.iter().cloned());
138 result
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct AgentState {
148 pub input: String,
150
151 pub messages: Vec<MessageEntry>,
153
154 pub steps: Vec<StepEntry>,
156
157 pub output: Option<String>,
159}
160
161impl StateSchema for AgentState {}
162
163impl AgentState {
164 pub fn new(input: String) -> Self {
166 let msg = MessageEntry::human(input.clone());
167 Self {
168 input,
169 messages: vec![msg],
170 steps: vec![],
171 output: None,
172 }
173 }
174
175 pub fn add_message(&mut self, message: MessageEntry) {
177 self.messages.push(message);
178 }
179
180 pub fn add_step(&mut self, step: StepEntry) {
182 self.steps.push(step);
183 }
184
185 pub fn set_output(&mut self, output: String) {
187 self.output = Some(output);
188 }
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct MessageEntry {
194 pub role: MessageRole,
195 pub content: String,
196}
197
198impl MessageEntry {
199 pub fn human(content: String) -> Self {
200 Self {
201 role: MessageRole::Human,
202 content,
203 }
204 }
205
206 pub fn ai(content: String) -> Self {
207 Self {
208 role: MessageRole::AI,
209 content,
210 }
211 }
212
213 pub fn system(content: String) -> Self {
214 Self {
215 role: MessageRole::System,
216 content,
217 }
218 }
219
220 pub fn tool(content: String) -> Self {
221 Self {
222 role: MessageRole::Tool,
223 content,
224 }
225 }
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
230pub enum MessageRole {
231 System,
232 Human,
233 AI,
234 Tool,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct StepEntry {
240 pub action: String,
241 pub observation: String,
242}
243
244impl StepEntry {
245 pub fn new(action: String, observation: String) -> Self {
246 Self {
247 action,
248 observation,
249 }
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn test_append_messages_reducer() {
259 let mut current = AgentState::new("Hello".to_string());
260 current.add_message(MessageEntry::ai("Response 1".to_string()));
261
262 let mut update = AgentState::new("Hello".to_string());
263 update.add_message(MessageEntry::ai("Response 2".to_string()));
264 update.set_output("Done".to_string());
265
266 let reducer = AppendMessagesReducer;
267 let result = reducer.reduce(¤t, &update);
268
269 assert_eq!(result.messages.len(), 4);
270 assert_eq!(result.output, Some("Done".to_string()));
271 }
272
273 #[test]
274 fn test_append_steps_reducer() {
275 let mut current = AgentState::new("Test".to_string());
276 current.add_step(StepEntry::new(
277 "Action 1".to_string(),
278 "Result 1".to_string(),
279 ));
280
281 let mut update = AgentState::new("Test".to_string());
282 update.add_step(StepEntry::new(
283 "Action 2".to_string(),
284 "Result 2".to_string(),
285 ));
286
287 let reducer = AppendStepsReducer;
288 let result = reducer.reduce(¤t, &update);
289
290 assert_eq!(result.steps.len(), 2);
291 }
292}