ai-agents-reasoning 1.0.0-rc.15

Reasoning and reflection capabilities for AI Agents framework
Documentation
use serde::{Deserialize, Serialize};

use crate::evaluation::{EvaluationResult, ReflectionAttempt};
use crate::mode::ReasoningMode;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningMetadata {
    pub mode_used: ReasoningMode,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thinking: Option<String>,
    pub iterations: u32,
    pub auto_detected: bool,
}

impl ReasoningMetadata {
    pub fn new(mode_used: ReasoningMode) -> Self {
        Self {
            mode_used,
            thinking: None,
            iterations: 1,
            auto_detected: false,
        }
    }

    pub fn with_thinking(mut self, thinking: impl Into<String>) -> Self {
        self.thinking = Some(thinking.into());
        self
    }

    pub fn with_iterations(mut self, iterations: u32) -> Self {
        self.iterations = iterations;
        self
    }

    pub fn with_auto_detected(mut self, auto_detected: bool) -> Self {
        self.auto_detected = auto_detected;
        self
    }

    pub fn has_thinking(&self) -> bool {
        self.thinking.is_some()
    }
}

impl Default for ReasoningMetadata {
    fn default() -> Self {
        Self::new(ReasoningMode::None)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReflectionMetadata {
    pub attempts: u32,
    pub final_evaluation: EvaluationResult,
    pub history: Vec<ReflectionAttempt>,
}

impl ReflectionMetadata {
    pub fn new(final_evaluation: EvaluationResult) -> Self {
        Self {
            attempts: 1,
            final_evaluation,
            history: Vec::new(),
        }
    }

    pub fn with_attempts(mut self, attempts: u32) -> Self {
        self.attempts = attempts;
        self
    }

    pub fn with_history(mut self, history: Vec<ReflectionAttempt>) -> Self {
        self.history = history;
        self
    }

    pub fn add_attempt(&mut self, attempt: ReflectionAttempt) {
        self.history.push(attempt);
        self.attempts = self.history.len() as u32;
    }

    pub fn passed(&self) -> bool {
        self.final_evaluation.passed
    }

    pub fn required_retries(&self) -> bool {
        self.attempts > 1
    }
}

impl Default for ReflectionMetadata {
    fn default() -> Self {
        Self {
            attempts: 0,
            final_evaluation: EvaluationResult::default(),
            history: Vec::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::evaluation::CriterionResult;

    #[test]
    fn test_reasoning_metadata_creation() {
        let metadata = ReasoningMetadata::new(ReasoningMode::CoT);
        assert_eq!(metadata.mode_used, ReasoningMode::CoT);
        assert!(metadata.thinking.is_none());
        assert_eq!(metadata.iterations, 1);
        assert!(!metadata.auto_detected);
    }

    #[test]
    fn test_reasoning_metadata_with_thinking() {
        let metadata = ReasoningMetadata::new(ReasoningMode::CoT)
            .with_thinking("Step 1: Analyze the problem\nStep 2: Find solution");

        assert!(metadata.has_thinking());
        assert!(metadata.thinking.unwrap().contains("Step 1"));
    }

    #[test]
    fn test_reasoning_metadata_auto_detected() {
        let metadata = ReasoningMetadata::new(ReasoningMode::Auto)
            .with_auto_detected(true)
            .with_iterations(3);

        assert!(metadata.auto_detected);
        assert_eq!(metadata.iterations, 3);
    }

    #[test]
    fn test_reasoning_metadata_default() {
        let metadata = ReasoningMetadata::default();
        assert_eq!(metadata.mode_used, ReasoningMode::None);
        assert!(!metadata.has_thinking());
        assert_eq!(metadata.iterations, 1);
    }

    #[test]
    fn test_reflection_metadata_creation() {
        let evaluation = EvaluationResult::new(true, 0.95);
        let metadata = ReflectionMetadata::new(evaluation);

        assert!(metadata.passed());
        assert_eq!(metadata.attempts, 1);
        assert!(metadata.history.is_empty());
    }

    #[test]
    fn test_reflection_metadata_with_history() {
        let eval1 = EvaluationResult::new(false, 0.5);
        let attempt1 = ReflectionAttempt::new("First response", eval1);

        let eval2 = EvaluationResult::new(true, 0.9);
        let attempt2 = ReflectionAttempt::new("Second response", eval2.clone());

        let metadata = ReflectionMetadata::new(eval2)
            .with_attempts(2)
            .with_history(vec![attempt1, attempt2]);

        assert!(metadata.passed());
        assert!(metadata.required_retries());
        assert_eq!(metadata.history.len(), 2);
    }

    #[test]
    fn test_reflection_metadata_add_attempt() {
        let eval = EvaluationResult::new(false, 0.4);
        let mut metadata = ReflectionMetadata::default();

        metadata.add_attempt(ReflectionAttempt::new("Response 1", eval.clone()));
        assert_eq!(metadata.attempts, 1);

        metadata.add_attempt(ReflectionAttempt::new("Response 2", eval));
        assert_eq!(metadata.attempts, 2);
    }

    #[test]
    fn test_reflection_metadata_default() {
        let metadata = ReflectionMetadata::default();
        assert_eq!(metadata.attempts, 0);
        assert!(!metadata.passed());
        assert!(metadata.history.is_empty());
    }

    #[test]
    fn test_reasoning_metadata_serde() {
        let metadata = ReasoningMetadata::new(ReasoningMode::React)
            .with_thinking("Thought process")
            .with_iterations(5)
            .with_auto_detected(true);

        let json = serde_json::to_string(&metadata).unwrap();
        let parsed: ReasoningMetadata = serde_json::from_str(&json).unwrap();

        assert_eq!(parsed.mode_used, ReasoningMode::React);
        assert_eq!(parsed.thinking, Some("Thought process".to_string()));
        assert_eq!(parsed.iterations, 5);
        assert!(parsed.auto_detected);
    }

    #[test]
    fn test_reflection_metadata_serde() {
        let criteria = vec![CriterionResult::pass("Clear response")];
        let evaluation = EvaluationResult::new(true, 0.85).with_criteria(criteria);
        let metadata = ReflectionMetadata::new(evaluation).with_attempts(2);

        let json = serde_json::to_string(&metadata).unwrap();
        let parsed: ReflectionMetadata = serde_json::from_str(&json).unwrap();

        assert!(parsed.passed());
        assert_eq!(parsed.attempts, 2);
        assert_eq!(parsed.final_evaluation.criteria_results.len(), 1);
    }
}