1use crate::retry::RetryPolicy;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::time::Duration;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
24#[non_exhaustive]
25pub struct GraphConfig {
26 pub thread_id: String,
28
29 pub checkpoint_id: Option<String>,
31
32 pub recursion_limit: u32,
37
38 pub max_concurrency: Option<usize>,
43
44 pub configurable: HashMap<String, serde_json::Value>,
46
47 #[serde(skip)]
53 pub max_execution_time: Option<Duration>,
54
55 #[serde(skip)]
63 pub retry_policy: Option<RetryPolicy>,
64}
65
66impl Default for GraphConfig {
67 fn default() -> Self {
68 Self {
69 thread_id: uuid::Uuid::new_v4().to_string(),
70 checkpoint_id: None,
71 recursion_limit: 25,
72 max_concurrency: None,
73 configurable: HashMap::new(),
74 max_execution_time: None,
75 retry_policy: None,
76 }
77 }
78}
79
80impl GraphConfig {
81 #[must_use = "builder methods return the modified config"]
83 pub fn with_thread_id(mut self, id: impl Into<String>) -> Self {
84 self.thread_id = id.into();
85 self
86 }
87
88 #[must_use = "builder methods return the modified config"]
90 pub fn with_recursion_limit(mut self, limit: u32) -> Self {
91 self.recursion_limit = limit;
92 self
93 }
94
95 #[must_use = "builder methods return the modified config"]
97 pub fn with_max_concurrency(mut self, n: usize) -> Self {
98 self.max_concurrency = Some(n);
99 self
100 }
101
102 #[must_use = "builder methods return the modified config"]
104 pub fn with_checkpoint_id(mut self, id: impl Into<String>) -> Self {
105 self.checkpoint_id = Some(id.into());
106 self
107 }
108
109 #[must_use = "builder methods return the modified config"]
114 pub fn with_max_execution_time(mut self, duration: Duration) -> Self {
115 self.max_execution_time = Some(duration);
116 self
117 }
118
119 #[must_use = "builder methods return the modified config"]
133 pub fn with_retry_policy(mut self, policy: RetryPolicy) -> Self {
134 self.retry_policy = Some(policy);
135 self
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_default_values() {
145 let config = GraphConfig::default();
146 assert_eq!(config.recursion_limit, 25);
147 assert!(config.max_concurrency.is_none());
148 assert!(config.checkpoint_id.is_none());
149 assert!(!config.thread_id.is_empty());
150 assert!(config.configurable.is_empty());
151 assert!(config.max_execution_time.is_none());
152 assert!(config.retry_policy.is_none());
153 }
154
155 #[test]
156 fn test_builder_methods() {
157 let config = GraphConfig::default()
158 .with_thread_id("test-thread")
159 .with_recursion_limit(100)
160 .with_max_concurrency(4)
161 .with_checkpoint_id("cp-123");
162
163 assert_eq!(config.thread_id, "test-thread");
164 assert_eq!(config.recursion_limit, 100);
165 assert_eq!(config.max_concurrency, Some(4));
166 assert_eq!(config.checkpoint_id.as_deref(), Some("cp-123"));
167 }
168
169 #[test]
170 fn test_with_retry_policy() {
171 let policy = RetryPolicy {
172 max_attempts: 5,
173 ..Default::default()
174 };
175 let config = GraphConfig::default().with_retry_policy(policy);
176 assert!(config.retry_policy.is_some());
177 assert_eq!(config.retry_policy.unwrap().max_attempts, 5);
178 }
179
180 #[test]
181 fn test_unique_thread_ids() {
182 let a = GraphConfig::default();
183 let b = GraphConfig::default();
184 assert_ne!(a.thread_id, b.thread_id);
185 }
186}