1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt::Debug;
10
11pub trait StateSchema:
13 Clone + Send + Sync + 'static + Serialize + for<'de> Deserialize<'de> + Debug
14{
15 fn from_input(input: Self) -> Self {
17 input
18 }
19
20 fn to_json(&self) -> serde_json::Value {
22 serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
23 }
24}
25
26#[derive(Debug, Clone, Serialize)]
31pub struct StateUpdate<S: StateSchema> {
32 pub update: Option<S>,
34
35 pub metadata: HashMap<String, serde_json::Value>,
37}
38
39impl<S: StateSchema> StateUpdate<S> {
40 pub fn full(state: S) -> Self {
42 Self {
43 update: Some(state),
44 metadata: HashMap::new(),
45 }
46 }
47
48 pub fn with_metadata(state: S, metadata: HashMap<String, serde_json::Value>) -> Self {
50 Self {
51 update: Some(state),
52 metadata,
53 }
54 }
55
56 pub fn unchanged() -> Self {
58 Self {
59 update: None,
60 metadata: HashMap::new(),
61 }
62 }
63
64 pub fn add_metadata(&mut self, key: String, value: serde_json::Value) {
66 self.metadata.insert(key, value);
67 }
68}
69
70pub trait Reducer<S: StateSchema>: Send + Sync {
75 fn reduce(&self, current: &S, update: &S) -> S;
77}
78
79pub struct ReplaceReducer;
81
82impl<S: StateSchema> Reducer<S> for ReplaceReducer {
83 fn reduce(&self, _current: &S, update: &S) -> S {
84 update.clone()
85 }
86}
87
88pub struct AppendReducer<S: StateSchema, T: Clone + Send + Sync> {
93 pub field_accessor: fn(&S) -> &[T],
94 pub field_mutator: fn(&mut S, Vec<T>),
95}
96
97impl<S: StateSchema, T: Clone + Send + Sync> Reducer<S> for AppendReducer<S, T> {
98 fn reduce(&self, current: &S, update: &S) -> S {
99 let current_items = (self.field_accessor)(current);
100 let update_items = (self.field_accessor)(update);
101
102 let mut merged: Vec<T> = current_items.to_vec();
103 merged.extend(update_items.iter().cloned());
104
105 let mut result = current.clone();
106 (self.field_mutator)(&mut result, merged);
107 result
108 }
109}
110
111pub struct AppendMessagesReducer;
113
114impl Reducer<AgentState> for AppendMessagesReducer {
115 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
116 let mut result = update.clone();
117 result.messages = current.messages.clone();
118 result.messages.extend(update.messages.iter().cloned());
119 result
120 }
121}
122
123pub struct AppendStepsReducer;
125
126impl Reducer<AgentState> for AppendStepsReducer {
127 fn reduce(&self, current: &AgentState, update: &AgentState) -> AgentState {
128 let mut result = update.clone();
129 result.steps = current.steps.clone();
130 result.steps.extend(update.steps.iter().cloned());
131 result
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct AgentState {
141 pub input: String,
143
144 pub messages: Vec<MessageEntry>,
146
147 pub steps: Vec<StepEntry>,
149
150 pub output: Option<String>,
152}
153
154impl StateSchema for AgentState {}
155
156impl AgentState {
157 pub fn new(input: String) -> Self {
159 let msg = MessageEntry::human(input.clone());
160 Self {
161 input,
162 messages: vec![msg],
163 steps: vec![],
164 output: None,
165 }
166 }
167
168 pub fn add_message(&mut self, message: MessageEntry) {
170 self.messages.push(message);
171 }
172
173 pub fn add_step(&mut self, step: StepEntry) {
175 self.steps.push(step);
176 }
177
178 pub fn set_output(&mut self, output: String) {
180 self.output = Some(output);
181 }
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct MessageEntry {
187 pub role: MessageRole,
188 pub content: String,
189}
190
191impl MessageEntry {
192 pub fn human(content: String) -> Self {
193 Self {
194 role: MessageRole::Human,
195 content,
196 }
197 }
198
199 pub fn ai(content: String) -> Self {
200 Self {
201 role: MessageRole::AI,
202 content,
203 }
204 }
205
206 pub fn system(content: String) -> Self {
207 Self {
208 role: MessageRole::System,
209 content,
210 }
211 }
212
213 pub fn tool(content: String) -> Self {
214 Self {
215 role: MessageRole::Tool,
216 content,
217 }
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
223pub enum MessageRole {
224 System,
225 Human,
226 AI,
227 Tool,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct StepEntry {
233 pub action: String,
234 pub observation: String,
235}
236
237impl StepEntry {
238 pub fn new(action: String, observation: String) -> Self {
239 Self {
240 action,
241 observation,
242 }
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn test_append_messages_reducer() {
252 let mut current = AgentState::new("Hello".to_string());
253 current.add_message(MessageEntry::ai("Response 1".to_string()));
254
255 let mut update = AgentState::new("Hello".to_string());
256 update.add_message(MessageEntry::ai("Response 2".to_string()));
257 update.set_output("Done".to_string());
258
259 let reducer = AppendMessagesReducer;
260 let result = reducer.reduce(¤t, &update);
261
262 assert_eq!(result.messages.len(), 4);
263 assert_eq!(result.output, Some("Done".to_string()));
264 }
265
266 #[test]
267 fn test_append_steps_reducer() {
268 let mut current = AgentState::new("Test".to_string());
269 current.add_step(StepEntry::new(
270 "Action 1".to_string(),
271 "Result 1".to_string(),
272 ));
273
274 let mut update = AgentState::new("Test".to_string());
275 update.add_step(StepEntry::new(
276 "Action 2".to_string(),
277 "Result 2".to_string(),
278 ));
279
280 let reducer = AppendStepsReducer;
281 let result = reducer.reduce(¤t, &update);
282
283 assert_eq!(result.steps.len(), 2);
284 }
285}