use llm_output_parser::ParseTrace;
use stack_ids::{AttemptId, TrialId};
#[derive(Debug, Clone, Default)]
pub struct ParseDiagnostics {
pub strategy: Option<&'static str>,
pub parse_error: Option<String>,
pub retry_attempts: u32,
pub transport_retries: u32,
pub backoff_total_ms: u64,
pub repaired: bool,
pub auto_completed: bool,
pub parser_strategies: Vec<&'static str>,
pub parser_repair_actions: Vec<String>,
pub parser_extracted_span: Option<(usize, usize)>,
pub parser_warnings: Vec<String>,
pub attempt_id: Option<AttemptId>,
pub trial_id: Option<TrialId>,
}
impl ParseDiagnostics {
pub fn ok(&self) -> bool {
self.parse_error.is_none()
}
pub fn apply_trace(&mut self, trace: ParseTrace) {
self.repaired = trace.repaired;
self.parser_strategies = trace.strategies_tried;
self.parser_repair_actions = trace.repair_actions;
self.parser_extracted_span = trace.extracted_span;
self.parser_warnings = trace.warnings;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diagnostics_default_is_ok() {
let d = ParseDiagnostics::default();
assert!(d.ok());
assert!(d.strategy.is_none());
assert_eq!(d.retry_attempts, 0);
assert_eq!(d.transport_retries, 0);
assert_eq!(d.backoff_total_ms, 0);
assert!(!d.repaired);
assert!(!d.auto_completed);
assert!(d.parser_strategies.is_empty());
assert!(d.parser_repair_actions.is_empty());
assert!(d.parser_extracted_span.is_none());
assert!(d.parser_warnings.is_empty());
assert!(d.attempt_id.is_none());
assert!(d.trial_id.is_none());
}
#[test]
fn test_diagnostics_with_error_is_not_ok() {
let d = ParseDiagnostics {
parse_error: Some("bad json".to_string()),
..Default::default()
};
assert!(!d.ok());
}
#[test]
fn test_apply_trace_copies_parser_details() {
let mut d = ParseDiagnostics::default();
d.apply_trace(ParseTrace {
strategies_tried: vec!["direct_parse", "repair_candidate"],
repaired: true,
repair_actions: vec!["repaired_candidate".to_string()],
extracted_span: Some((2, 8)),
warnings: vec!["fallback path".to_string()],
});
assert!(d.repaired);
assert_eq!(
d.parser_strategies,
vec!["direct_parse", "repair_candidate"]
);
assert_eq!(d.parser_repair_actions, vec!["repaired_candidate"]);
assert_eq!(d.parser_extracted_span, Some((2, 8)));
assert_eq!(d.parser_warnings, vec!["fallback path"]);
}
#[test]
fn test_diagnostics_retry_ids() {
let d = ParseDiagnostics {
attempt_id: Some(AttemptId::generate()),
trial_id: Some(TrialId::generate()),
..Default::default()
};
assert!(d.attempt_id.is_some());
assert!(d.trial_id.is_some());
}
}