do_memory_core/reward/
efficiency.rs1use crate::episode::Episode;
4use crate::reward::constants::{
5 EFFICIENT_DURATION_SECS, EFFICIENT_STEP_COUNT, MAX_EFFICIENCY_MULTIPLIER,
6 MIN_EFFICIENCY_MULTIPLIER,
7};
8use crate::types::TaskOutcome;
9
10const EARLY_ABSTENTION_THRESHOLD: usize = 3;
16const LATE_ABSTENTION_THRESHOLD: usize = 10;
17
18pub fn calculate_abstention_score(episode: &Episode) -> f32 {
19 match &episode.outcome {
20 Some(TaskOutcome::Abstained {
21 stopped_at_step, ..
22 }) => {
23 if *stopped_at_step <= EARLY_ABSTENTION_THRESHOLD {
24 0.5 } else if *stopped_at_step >= LATE_ABSTENTION_THRESHOLD {
26 -0.3 } else {
28 let range = (LATE_ABSTENTION_THRESHOLD - EARLY_ABSTENTION_THRESHOLD) as f32;
30 let pos = (*stopped_at_step - EARLY_ABSTENTION_THRESHOLD) as f32;
31 0.5 - (0.8 * pos / range)
32 }
33 }
34 Some(TaskOutcome::Failure { .. }) if episode.steps.len() > LATE_ABSTENTION_THRESHOLD => {
35 -0.2
37 }
38 _ => 0.0,
39 }
40}
41
42pub struct EfficiencyCalculator {
44 pub duration_weight: f32,
46 pub step_count_weight: f32,
48}
49
50impl EfficiencyCalculator {
51 pub fn new(duration_weight: f32, step_count_weight: f32) -> Self {
53 Self {
54 duration_weight,
55 step_count_weight,
56 }
57 }
58
59 pub fn calculate(&self, episode: &Episode) -> f32 {
61 let duration_score = self.calculate_duration_efficiency(episode);
62 let step_count_score = self.calculate_step_count_efficiency(episode);
63
64 let combined =
65 (duration_score * self.duration_weight) + (step_count_score * self.step_count_weight);
66
67 combined.clamp(MIN_EFFICIENCY_MULTIPLIER, MAX_EFFICIENCY_MULTIPLIER)
69 }
70
71 fn calculate_duration_efficiency(&self, episode: &Episode) -> f32 {
73 if let Some(duration) = episode.duration() {
74 let duration_secs = duration.num_seconds() as f32;
75
76 if duration_secs <= 0.0 {
77 return MAX_EFFICIENCY_MULTIPLIER;
78 }
79
80 let ratio = duration_secs / EFFICIENT_DURATION_SECS;
83 let score = (-ratio / 2.0).exp();
84
85 MIN_EFFICIENCY_MULTIPLIER
87 + (score * (MAX_EFFICIENCY_MULTIPLIER - MIN_EFFICIENCY_MULTIPLIER))
88 } else {
89 1.0 }
91 }
92
93 fn calculate_step_count_efficiency(&self, episode: &Episode) -> f32 {
95 let step_count = episode.steps.len();
96
97 if step_count == 0 {
98 return MIN_EFFICIENCY_MULTIPLIER;
99 }
100
101 let ratio = step_count as f32 / EFFICIENT_STEP_COUNT as f32;
103 let score = (-ratio / 2.0).exp();
104
105 MIN_EFFICIENCY_MULTIPLIER
107 + (score * (MAX_EFFICIENCY_MULTIPLIER - MIN_EFFICIENCY_MULTIPLIER))
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::episode::ExecutionStep;
115 use crate::types::{ComplexityLevel, ExecutionResult, TaskContext, TaskOutcome, TaskType};
116
117 fn create_test_episode() -> Episode {
118 let context = TaskContext {
119 language: Some("rust".to_string()),
120 framework: None,
121 complexity: ComplexityLevel::Simple,
122 domain: "testing".to_string(),
123 tags: vec![],
124 };
125 Episode::new("Test task".to_string(), context, TaskType::Testing)
126 }
127
128 #[test]
129 fn test_efficiency_fast_execution() {
130 let calculator = EfficiencyCalculator::new(0.5, 0.5);
131 let mut episode = create_test_episode();
132
133 for i in 0..3 {
135 let mut step = ExecutionStep::new(i + 1, format!("tool_{i}"), "Action".to_string());
136 step.result = Some(ExecutionResult::Success {
137 output: "OK".to_string(),
138 });
139 episode.add_step(step);
140 }
141
142 episode.complete(TaskOutcome::Success {
143 verdict: "Quick".to_string(),
144 artifacts: vec![],
145 });
146
147 let efficiency = calculator.calculate(&episode);
148 assert!(efficiency > 1.0);
149 }
150
151 #[test]
152 fn test_efficiency_slow_execution() {
153 let calculator = EfficiencyCalculator::new(0.5, 0.5);
154 let mut episode = create_test_episode();
155
156 episode.start_time = chrono::Utc::now() - chrono::Duration::minutes(5);
158
159 for i in 0..50 {
161 let mut step = ExecutionStep::new(i + 1, format!("tool_{i}"), "Action".to_string());
162 step.result = Some(ExecutionResult::Success {
163 output: "OK".to_string(),
164 });
165 episode.add_step(step);
166 }
167
168 episode.complete(TaskOutcome::Success {
169 verdict: "Slow".to_string(),
170 artifacts: vec![],
171 });
172
173 let efficiency = calculator.calculate(&episode);
174 assert!(efficiency < 1.0);
175 }
176}