1use crate::llm_codegen::{GeneratedCode, LlmError};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct VerificationResult {
12 pub compiles: bool,
14 pub tests_pass: bool,
16 pub compile_errors: Vec<String>,
18 pub test_failures: Vec<String>,
20 pub clippy_warnings: usize,
22 pub success: bool,
24}
25
26impl VerificationResult {
27 pub fn success() -> Self {
29 Self {
30 compiles: true,
31 tests_pass: true,
32 compile_errors: Vec::new(),
33 test_failures: Vec::new(),
34 clippy_warnings: 0,
35 success: true,
36 }
37 }
38
39 pub fn compile_failure(errors: Vec<String>) -> Self {
41 Self {
42 compiles: false,
43 tests_pass: false,
44 compile_errors: errors,
45 test_failures: Vec::new(),
46 clippy_warnings: 0,
47 success: false,
48 }
49 }
50
51 pub fn test_failure(failures: Vec<String>) -> Self {
53 Self {
54 compiles: true,
55 tests_pass: false,
56 compile_errors: Vec::new(),
57 test_failures: failures,
58 clippy_warnings: 0,
59 success: false,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct IterationContext {
67 pub iteration: usize,
69 pub max_iterations: usize,
71 pub previous_code: Option<String>,
73 pub previous_errors: Vec<String>,
75 pub feedback: Vec<String>,
77}
78
79impl IterationContext {
80 pub fn new(max_iterations: usize) -> Self {
82 Self {
83 iteration: 1,
84 max_iterations,
85 previous_code: None,
86 previous_errors: Vec::new(),
87 feedback: Vec::new(),
88 }
89 }
90
91 pub fn can_retry(&self) -> bool {
93 self.iteration <= self.max_iterations
94 }
95
96 pub fn record_failure(&mut self, code: &str, errors: Vec<String>) {
98 self.previous_code = Some(code.to_string());
99 self.previous_errors = errors.clone();
100
101 for error in &errors {
103 self.feedback.push(format!("Iteration {}: {}", self.iteration, error));
104 }
105
106 self.iteration += 1;
107 }
108
109 pub fn get_feedback(&self) -> String {
111 let mut feedback = String::new();
112
113 feedback.push_str("## Previous Errors\n\n");
114
115 for error in &self.previous_errors {
116 feedback.push_str("- ");
117 feedback.push_str(error);
118 feedback.push('\n');
119 }
120
121 if let Some(ref code) = self.previous_code {
122 feedback.push_str("\n## Previous Code\n```rust\n");
123 feedback.push_str(code);
124 feedback.push_str("\n```\n");
125 }
126
127 feedback.push_str("\n## Instructions\n");
128 feedback.push_str("Please fix the errors above and generate corrected Rust code.\n");
129
130 feedback
131 }
132}
133
134#[derive(Debug)]
136pub struct CodeVerifier {
137 _temp_dir: Option<std::path::PathBuf>,
139}
140
141impl CodeVerifier {
142 pub fn new() -> Self {
144 Self { _temp_dir: None }
145 }
146
147 pub fn verify(&self, code: &GeneratedCode) -> Result<VerificationResult, LlmError> {
152 if code.code.trim().is_empty() {
154 return Ok(VerificationResult::compile_failure(vec!["Empty code".to_string()]));
155 }
156
157 let open = code.code.matches('{').count();
159 let close = code.code.matches('}').count();
160
161 if open != close {
162 return Ok(VerificationResult::compile_failure(vec![format!(
163 "Unbalanced braces: {} open, {} close",
164 open, close
165 )]));
166 }
167
168 Ok(VerificationResult::success())
170 }
171
172 pub fn compile(&self, code: &str) -> Result<(), Vec<String>> {
176 if code.trim().is_empty() {
177 return Err(vec!["Empty code".to_string()]);
178 }
179
180 let open_braces = code.matches('{').count();
182 let close_braces = code.matches('}').count();
183
184 if open_braces != close_braces {
185 return Err(vec![format!(
186 "Unbalanced braces: {} open, {} close",
187 open_braces, close_braces
188 )]);
189 }
190
191 Ok(())
192 }
193
194 pub fn lint(&self, code: &str) -> Result<usize, LlmError> {
198 let mut warnings = 0;
200
201 if code.contains("unwrap()") {
203 warnings += 1;
204 }
205 if code.contains("expect(") {
206 warnings += 1;
207 }
208 if code.contains("panic!") {
209 warnings += 1;
210 }
211
212 Ok(warnings)
213 }
214
215 pub fn run_tests(&self, code: &str) -> Result<(), Vec<String>> {
219 if code.contains("#[test]") {
221 Ok(())
223 } else {
224 Ok(())
226 }
227 }
228
229 fn _create_temp_project(&self, _code: &str) -> Result<std::path::PathBuf, LlmError> {
233 Err(LlmError::ApiError("Temporary project creation not implemented".to_string()))
234 }
235}
236
237impl Default for CodeVerifier {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243#[derive(Debug)]
245pub struct VerificationLoop {
246 max_iterations: usize,
248}
249
250impl VerificationLoop {
251 pub fn new(max_iterations: usize) -> Self {
253 Self { max_iterations }
254 }
255
256 pub fn max_iterations(&self) -> usize {
258 self.max_iterations
259 }
260
261 pub fn is_success(&self, result: &VerificationResult) -> bool {
263 result.success && result.compiles && result.tests_pass
264 }
265
266 pub fn format_feedback(&self, result: &VerificationResult) -> String {
268 let mut feedback = String::new();
269
270 if !result.compile_errors.is_empty() {
271 feedback.push_str("## Compilation Errors\n\n");
272 for error in &result.compile_errors {
273 feedback.push_str("- ");
274 feedback.push_str(error);
275 feedback.push('\n');
276 }
277 }
278
279 if !result.test_failures.is_empty() {
280 feedback.push_str("\n## Test Failures\n\n");
281 for failure in &result.test_failures {
282 feedback.push_str("- ");
283 feedback.push_str(failure);
284 feedback.push('\n');
285 }
286 }
287
288 if result.clippy_warnings > 0 {
289 feedback.push_str(&format!("\n## Clippy Warnings: {}\n", result.clippy_warnings));
290 }
291
292 feedback
293 }
294}
295
296impl Default for VerificationLoop {
297 fn default() -> Self {
298 Self::new(3)
299 }
300}
301
302#[derive(Debug, Clone, Default, Serialize, Deserialize)]
330pub struct CompilationMetrics {
331 total_attempts: u64,
333 first_try_successes: u64,
335 total_iterations: u64,
337 iteration_counts: std::collections::HashMap<usize, u64>,
339}
340
341impl CompilationMetrics {
342 pub const TARGET_RATE: f64 = 0.85;
344
345 pub fn new() -> Self {
347 Self {
348 total_attempts: 0,
349 first_try_successes: 0,
350 total_iterations: 0,
351 iteration_counts: std::collections::HashMap::new(),
352 }
353 }
354
355 pub fn record_attempt(&mut self, success: bool, iterations: usize) {
361 self.total_attempts += 1;
362 self.total_iterations += iterations as u64;
363
364 if success && iterations == 1 {
366 self.first_try_successes += 1;
367 }
368
369 *self.iteration_counts.entry(iterations).or_insert(0) += 1;
371 }
372
373 pub fn total_attempts(&self) -> u64 {
375 self.total_attempts
376 }
377
378 pub fn first_try_successes(&self) -> u64 {
380 self.first_try_successes
381 }
382
383 pub fn first_try_rate(&self) -> f64 {
385 if self.total_attempts == 0 {
386 return 0.0;
387 }
388 self.first_try_successes as f64 / self.total_attempts as f64
389 }
390
391 pub fn meets_target(&self, target: f64) -> bool {
393 self.first_try_rate() >= target
394 }
395
396 pub fn average_iterations(&self) -> f64 {
398 if self.total_attempts == 0 {
399 return 0.0;
400 }
401 self.total_iterations as f64 / self.total_attempts as f64
402 }
403
404 pub fn iteration_histogram(&self) -> &std::collections::HashMap<usize, u64> {
406 &self.iteration_counts
407 }
408
409 pub fn reset(&mut self) {
411 self.total_attempts = 0;
412 self.first_try_successes = 0;
413 self.total_iterations = 0;
414 self.iteration_counts.clear();
415 }
416}