1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Types for retry, fallback, and rate-limiting policies.
//!
//! These declarative policy structs ([`RetryPolicy`], [`FallbackPolicy`],
//! [`RateLimiter`]) are the durability knobs the recursive harness applies to
//! the model call that every level of the recursion ultimately reduces to, so
//! the same resilience settings govern parent and nested calls alike. Each is
//! deterministically testable: backoff and the limiter take an explicit
//! `rand01` / `now` rather than reading a global clock or RNG.
/// Configures how a harness call is retried on transient failure.
///
/// Backoff grows exponentially: `initial_backoff_ms * multiplier^attempt`, then
/// capped at `max_backoff_ms`. When `jitter` is `true` the caller should use
/// [`RetryPolicy::backoff_for_attempt_with`] and supply a `[0, 1)` random
/// value; this avoids thundering-herd without making the implementation
/// non-deterministic for tests.
///
/// # Examples
///
/// ```
/// use tinyagents::harness::retry::RetryPolicy;
///
/// let policy = RetryPolicy::default();
/// assert!(policy.should_retry(0));
/// assert!(!policy.should_retry(3));
/// ```
/// Ordered list of model identifiers to try in sequence when the current model
/// fails.
///
/// The harness will move to `next_after` the current model on non-retryable
/// errors (or after retries are exhausted).
///
/// # Examples
///
/// ```
/// use tinyagents::harness::retry::FallbackPolicy;
///
/// let policy = FallbackPolicy { models: vec!["claude-3-5-sonnet".into(), "claude-3-haiku".into()] };
/// assert_eq!(policy.next_after("claude-3-5-sonnet"), Some("claude-3-haiku"));
/// assert_eq!(policy.next_after("claude-3-haiku"), None);
/// ```
/// A simple token-bucket rate limiter.
///
/// State is kept inside the struct behind a [`std::sync::Mutex`] so the same
/// instance can be shared across threads without wrapping in an `Arc<Mutex<...>>`
/// by the caller.
///
/// The caller must supply the current time (`now: Instant`) to every method so
/// the limiter is fully testable without real sleeping or timer injection.
///
/// # Examples
///
/// ```
/// use std::time::Instant;
/// use tinyagents::harness::retry::RateLimiter;
///
/// let mut limiter = RateLimiter::new(10, 5.0);
/// let now = Instant::now();
/// assert!(limiter.try_acquire(1, now));
/// ```
/// Inner mutable state of a [`RateLimiter`].
pub
/// Provider-failure class used for retry, fallback, and telemetry decisions.
///
/// The class is intentionally provider-neutral: callers can layer product,
/// billing, or account-specific rules on top, while TinyAgents supplies the
/// generic HTTP/status/error-message behavior shared by hosted model adapters.