agentic_connect/types/
retry.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum FailureClass {
11 Transient,
13 Permanent,
15 RateLimit,
17 AuthFailure,
19 NetworkError,
21 ServerError,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum RetryStrategy {
29 ExponentialBackoff {
31 base_ms: u64,
32 max_ms: u64,
33 max_attempts: u32,
34 },
35 FixedDelay { delay_ms: u64, max_attempts: u32 },
37 RefreshAndRetry,
39 FailFast,
41 WaitRetryAfter,
43}
44
45impl Default for RetryStrategy {
46 fn default() -> Self {
47 RetryStrategy::ExponentialBackoff {
48 base_ms: 1000,
49 max_ms: 30_000,
50 max_attempts: 3,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct RetryPolicy {
58 pub strategies: HashMap<String, RetryStrategy>,
59 pub default_strategy: RetryStrategy,
60}
61
62impl Default for RetryPolicy {
63 fn default() -> Self {
64 let mut strategies = HashMap::new();
65 strategies.insert("transient".to_string(), RetryStrategy::default());
66 strategies.insert(
67 "rate_limit".to_string(),
68 RetryStrategy::WaitRetryAfter,
69 );
70 strategies.insert(
71 "auth_failure".to_string(),
72 RetryStrategy::RefreshAndRetry,
73 );
74 strategies.insert(
75 "permanent".to_string(),
76 RetryStrategy::FailFast,
77 );
78 Self {
79 strategies,
80 default_strategy: RetryStrategy::default(),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct CircuitBreaker {
88 pub endpoint: String,
89 pub state: CircuitState,
90 pub failure_count: u32,
91 pub failure_threshold: u32,
92 pub last_failure: Option<DateTime<Utc>>,
93 pub reset_after_secs: u64,
94 pub half_open_at: Option<DateTime<Utc>>,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(rename_all = "snake_case")]
100pub enum CircuitState {
101 Closed,
102 Open,
103 HalfOpen,
104}
105
106impl CircuitBreaker {
107 pub fn new(endpoint: &str, threshold: u32, reset_secs: u64) -> Self {
108 Self {
109 endpoint: endpoint.to_string(),
110 state: CircuitState::Closed,
111 failure_count: 0,
112 failure_threshold: threshold,
113 last_failure: None,
114 reset_after_secs: reset_secs,
115 half_open_at: None,
116 }
117 }
118
119 pub fn record_failure(&mut self) {
120 self.failure_count += 1;
121 self.last_failure = Some(Utc::now());
122 if self.failure_count >= self.failure_threshold {
123 self.state = CircuitState::Open;
124 self.half_open_at = Some(
125 Utc::now() + chrono::Duration::seconds(self.reset_after_secs as i64),
126 );
127 }
128 }
129
130 pub fn record_success(&mut self) {
131 self.failure_count = 0;
132 self.state = CircuitState::Closed;
133 self.half_open_at = None;
134 }
135
136 pub fn should_allow(&self) -> bool {
137 match self.state {
138 CircuitState::Closed => true,
139 CircuitState::Open => {
140 self.half_open_at.map_or(false, |t| Utc::now() >= t)
141 }
142 CircuitState::HalfOpen => true,
143 }
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct RateLimitWindow {
150 pub endpoint: String,
151 pub limit: u32,
152 pub remaining: u32,
153 pub resets_at: DateTime<Utc>,
154 pub window_secs: u64,
155}