cel_context/
confidence.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct ConfidenceThresholds {
6 pub act_immediately: f64,
8 pub act_and_log: f64,
10 pub act_cautiously: f64,
13}
14
15impl Default for ConfidenceThresholds {
16 fn default() -> Self {
17 Self {
18 act_immediately: 0.9,
19 act_and_log: 0.7,
20 act_cautiously: 0.5,
21 }
22 }
23}
24
25#[derive(Debug, Clone, PartialEq)]
27pub enum ConfidenceBehavior {
28 ActImmediately,
30 ActAndLog,
32 ActCautiously,
34 PauseAndNotify,
36}
37
38impl ConfidenceThresholds {
39 pub fn behavior_for(&self, confidence: f64) -> ConfidenceBehavior {
41 if confidence >= self.act_immediately {
42 ConfidenceBehavior::ActImmediately
43 } else if confidence >= self.act_and_log {
44 ConfidenceBehavior::ActAndLog
45 } else if confidence >= self.act_cautiously {
46 ConfidenceBehavior::ActCautiously
47 } else {
48 ConfidenceBehavior::PauseAndNotify
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_confidence_thresholds_default_values() {
59 let thresholds = ConfidenceThresholds::default();
60 assert_eq!(thresholds.act_immediately, 0.9);
61 assert_eq!(thresholds.act_and_log, 0.7);
62 assert_eq!(thresholds.act_cautiously, 0.5);
63 }
64
65 #[test]
66 fn test_confidence_behavior_mapping() {
67 let thresholds = ConfidenceThresholds::default();
68
69 assert_eq!(
70 thresholds.behavior_for(0.95),
71 ConfidenceBehavior::ActImmediately
72 );
73 assert_eq!(
74 thresholds.behavior_for(0.9),
75 ConfidenceBehavior::ActImmediately
76 );
77 assert_eq!(thresholds.behavior_for(0.85), ConfidenceBehavior::ActAndLog);
78 assert_eq!(thresholds.behavior_for(0.7), ConfidenceBehavior::ActAndLog);
79 assert_eq!(
80 thresholds.behavior_for(0.6),
81 ConfidenceBehavior::ActCautiously
82 );
83 assert_eq!(
84 thresholds.behavior_for(0.5),
85 ConfidenceBehavior::ActCautiously
86 );
87 assert_eq!(
88 thresholds.behavior_for(0.3),
89 ConfidenceBehavior::PauseAndNotify
90 );
91 assert_eq!(
92 thresholds.behavior_for(0.0),
93 ConfidenceBehavior::PauseAndNotify
94 );
95 }
96
97 #[test]
98 fn test_boundary_values_exact() {
99 let thresholds = ConfidenceThresholds::default();
100
101 assert_eq!(
103 thresholds.behavior_for(0.9),
104 ConfidenceBehavior::ActImmediately
105 );
106 assert_eq!(thresholds.behavior_for(0.7), ConfidenceBehavior::ActAndLog);
107 assert_eq!(
108 thresholds.behavior_for(0.5),
109 ConfidenceBehavior::ActCautiously
110 );
111
112 assert_eq!(
114 thresholds.behavior_for(0.8999),
115 ConfidenceBehavior::ActAndLog
116 );
117 assert_eq!(
118 thresholds.behavior_for(0.6999),
119 ConfidenceBehavior::ActCautiously
120 );
121 assert_eq!(
122 thresholds.behavior_for(0.4999),
123 ConfidenceBehavior::PauseAndNotify
124 );
125 }
126
127 #[test]
128 fn test_extreme_values() {
129 let thresholds = ConfidenceThresholds::default();
130
131 assert_eq!(
132 thresholds.behavior_for(1.0),
133 ConfidenceBehavior::ActImmediately
134 );
135 assert_eq!(
136 thresholds.behavior_for(0.0),
137 ConfidenceBehavior::PauseAndNotify
138 );
139 }
140
141 #[test]
142 fn test_negative_confidence() {
143 let thresholds = ConfidenceThresholds::default();
144 assert_eq!(
146 thresholds.behavior_for(-0.1),
147 ConfidenceBehavior::PauseAndNotify
148 );
149 assert_eq!(
150 thresholds.behavior_for(-1.0),
151 ConfidenceBehavior::PauseAndNotify
152 );
153 }
154
155 #[test]
156 fn test_over_one_confidence() {
157 let thresholds = ConfidenceThresholds::default();
158 assert_eq!(
160 thresholds.behavior_for(1.5),
161 ConfidenceBehavior::ActImmediately
162 );
163 assert_eq!(
164 thresholds.behavior_for(100.0),
165 ConfidenceBehavior::ActImmediately
166 );
167 }
168
169 #[test]
170 fn test_custom_thresholds() {
171 let thresholds = ConfidenceThresholds {
172 act_immediately: 0.95,
173 act_and_log: 0.8,
174 act_cautiously: 0.6,
175 };
176
177 assert_eq!(
178 thresholds.behavior_for(0.96),
179 ConfidenceBehavior::ActImmediately
180 );
181 assert_eq!(thresholds.behavior_for(0.94), ConfidenceBehavior::ActAndLog);
182 assert_eq!(
183 thresholds.behavior_for(0.79),
184 ConfidenceBehavior::ActCautiously
185 );
186 assert_eq!(
187 thresholds.behavior_for(0.59),
188 ConfidenceBehavior::PauseAndNotify
189 );
190 }
191
192 #[test]
193 fn test_serialization_roundtrip() {
194 let thresholds = ConfidenceThresholds::default();
195 let json = serde_json::to_string(&thresholds).unwrap();
196 let deserialized: ConfidenceThresholds = serde_json::from_str(&json).unwrap();
197
198 assert_eq!(deserialized.act_immediately, thresholds.act_immediately);
199 assert_eq!(deserialized.act_and_log, thresholds.act_and_log);
200 assert_eq!(deserialized.act_cautiously, thresholds.act_cautiously);
201 }
202
203 #[test]
204 fn test_behavior_is_monotonic() {
205 let thresholds = ConfidenceThresholds::default();
207 let behaviors: Vec<ConfidenceBehavior> = (0..=100)
208 .map(|i| thresholds.behavior_for(i as f64 / 100.0))
209 .collect();
210
211 let rank = |b: &ConfidenceBehavior| -> u8 {
212 match b {
213 ConfidenceBehavior::PauseAndNotify => 0,
214 ConfidenceBehavior::ActCautiously => 1,
215 ConfidenceBehavior::ActAndLog => 2,
216 ConfidenceBehavior::ActImmediately => 3,
217 }
218 };
219
220 for i in 1..behaviors.len() {
221 assert!(
222 rank(&behaviors[i]) >= rank(&behaviors[i - 1]),
223 "Behavior should be monotonically non-decreasing with confidence. \
224 At {}/100: {:?}, at {}/100: {:?}",
225 i - 1,
226 behaviors[i - 1],
227 i,
228 behaviors[i]
229 );
230 }
231 }
232}