Skip to main content

mockforge_intelligence/ai_contract_diff/
mod.rs

1//! Pillars: [Contracts][AI]
2//!
3//! AI-powered contract diff analysis
4//!
5//! This module provides intelligent contract diff analysis that compares front-end requests
6//! against backend API contract specifications, detects mismatches, and generates AI-powered
7//! recommendations and correction proposals.
8//!
9//! # Features
10//!
11//! - **Structural Diff Analysis**: Detects mismatches between requests and contract specs
12//! - **AI-Powered Recommendations**: Uses LLM to generate contextual recommendations
13//! - **Correction Proposals**: Generates JSON Patch files for schema corrections
14//! - **Confidence Scoring**: Provides confidence scores for all suggestions
15//!
16//! # Example Usage
17//!
18//! ```rust,ignore
19//! use mockforge_core::ai_contract_diff::{
20//!     ContractDiffAnalyzer, ContractDiffConfig, CapturedRequest,
21//! };
22//! use mockforge_core::openapi::OpenApiSpec;
23//!
24//! async fn example() -> mockforge_core::Result<()> {
25//!     // Load contract specification
26//!     let spec = OpenApiSpec::from_file("api.yaml").await?;
27//!
28//! // Configure contract diff
29//! let config = ContractDiffConfig {
30//!     enabled: true,
31//!     llm_provider: "openai".to_string(),
32//!     llm_model: "gpt-4".to_string(),
33//!     confidence_threshold: 0.5,
34//!     ..Default::default()
35//! };
36//!
37//! // Create analyzer
38//! let analyzer = ContractDiffAnalyzer::new(config)?;
39//!
40//! // Capture a request
41//! let request = CapturedRequest::new("POST", "/api/users", "browser_extension")
42//!     .with_body(serde_json::json!({"name": "Alice", "email": "alice@example.com"}));
43//!
44//! // Analyze request against contract
45//! let result = analyzer.analyze(request, &spec).await?;
46//!
47//! // Check results
48//! if !result.matches {
49//!     println!("Found {} mismatches", result.mismatches.len());
50//!     for mismatch in &result.mismatches {
51//!         println!("  - {}: {}", mismatch.path, mismatch.description);
52//!     }
53//!
54//!     // Generate recommendations
55//!     for recommendation in &result.recommendations {
56//!         println!("  Recommendation: {}", recommendation.recommendation);
57//!     }
58//!
59//!     // Generate correction proposals
60//!     for correction in &result.corrections {
61//!         println!("  Correction: {}", correction.description);
62//!     }
63//!     }
64//!     Ok(())
65//! }
66//! ```
67
68pub mod confidence_scorer;
69pub mod correction_proposer;
70pub mod diff_analyzer;
71pub mod recommendation_engine;
72pub mod semantic_analyzer;
73pub mod types;
74
75// Re-export main types
76pub use confidence_scorer::{ConfidenceScorer, ScoringContext};
77pub use correction_proposer::CorrectionProposer;
78pub use diff_analyzer::DiffAnalyzer;
79pub use recommendation_engine::{RecommendationEngine, RequestContext};
80pub use semantic_analyzer::{SemanticAnalyzer, SemanticChangeType, SemanticDriftResult};
81pub use types::ConfidenceLevel;
82pub use types::{
83    CapturedRequest, ContractDiffConfig, ContractDiffResult, CorrectionProposal, DiffMetadata,
84    Mismatch, MismatchSeverity, MismatchType, PatchOperation, Recommendation,
85};
86
87/// Main contract diff analyzer that orchestrates all components
88pub struct ContractDiffAnalyzer {
89    /// Diff analyzer for structural comparison
90    diff_analyzer: DiffAnalyzer,
91
92    /// Recommendation engine for AI-powered suggestions
93    recommendation_engine: RecommendationEngine,
94
95    /// Semantic analyzer for Layer 2 semantic drift detection
96    semantic_analyzer: SemanticAnalyzer,
97
98    /// Correction proposer for generating patches
99    #[allow(dead_code)]
100    correction_proposer: CorrectionProposer,
101
102    /// Configuration
103    config: ContractDiffConfig,
104}
105
106impl ContractDiffAnalyzer {
107    /// Create a new contract diff analyzer
108    pub fn new(config: ContractDiffConfig) -> mockforge_foundation::Result<Self> {
109        let diff_analyzer = DiffAnalyzer::new(config.clone());
110        let recommendation_engine = RecommendationEngine::new(config.clone())?;
111        let semantic_analyzer = SemanticAnalyzer::new(config.clone())?;
112        let correction_proposer = CorrectionProposer;
113
114        Ok(Self {
115            diff_analyzer,
116            recommendation_engine,
117            semantic_analyzer,
118            correction_proposer,
119            config,
120        })
121    }
122
123    /// Analyze a captured request against a contract specification
124    pub async fn analyze(
125        &self,
126        request: &CapturedRequest,
127        spec: &mockforge_openapi::OpenApiSpec,
128    ) -> mockforge_foundation::Result<ContractDiffResult> {
129        // Step 1: Perform structural diff analysis
130        let mut result = self.diff_analyzer.analyze_request(request, spec).await?;
131
132        // Step 2: Generate AI-powered recommendations if enabled
133        if self.config.use_ai_recommendations && !result.mismatches.is_empty() {
134            let mut request_context = RequestContext::new(&request.method, &request.path);
135            if let Some(body) = &request.body {
136                request_context = request_context.with_body(body.clone());
137            }
138            request_context =
139                request_context.with_contract_format(&result.metadata.contract_format);
140
141            let recommendations = self
142                .recommendation_engine
143                .generate_recommendations(&result.mismatches, &request_context)
144                .await?;
145
146            // Filter by confidence threshold
147            result.recommendations = recommendations
148                .into_iter()
149                .filter(|r| r.confidence >= self.config.confidence_threshold)
150                .collect();
151        }
152
153        // Step 3: Generate correction proposals if enabled
154        if self.config.generate_corrections && !result.mismatches.is_empty() {
155            result.corrections = CorrectionProposer::generate_proposals(
156                &result.mismatches,
157                &result.recommendations,
158                spec,
159            );
160
161            // Filter by confidence threshold
162            result.corrections.retain(|c| c.confidence >= self.config.confidence_threshold);
163        }
164
165        // Recalculate overall confidence with recommendations and corrections
166        result.confidence = ConfidenceScorer::calculate_overall_confidence(&result.mismatches);
167
168        // Record AI pillar usage (ai_generation type=contract_diff) so the dashboard reflects contract-diff activity.
169        mockforge_foundation::pillar_tracking::record_ai_usage(
170            None,
171            None,
172            "ai_generation",
173            serde_json::json!({
174                "type": "contract_diff",
175                "mismatches": result.mismatches.len(),
176                "recommendations": result.recommendations.len(),
177                "corrections": result.corrections.len(),
178            }),
179        )
180        .await;
181
182        Ok(result)
183    }
184
185    /// Compare two contract specifications and detect semantic drift
186    ///
187    /// This method performs Layer 1 (structural) and Layer 2 (semantic) analysis
188    /// to detect both structural and meaning changes between contract versions.
189    pub async fn compare_specs(
190        &self,
191        before_spec: &mockforge_openapi::OpenApiSpec,
192        after_spec: &mockforge_openapi::OpenApiSpec,
193        endpoint_path: &str,
194        method: &str,
195    ) -> mockforge_foundation::Result<Option<SemanticDriftResult>> {
196        // Layer 2: Semantic analysis
197        if self.config.semantic_analysis_enabled {
198            let semantic_result = self
199                .semantic_analyzer
200                .analyze_semantic_drift(before_spec, after_spec, endpoint_path, method)
201                .await?;
202
203            // Filter by semantic confidence threshold
204            if let Some(ref result) = semantic_result {
205                if result.semantic_confidence >= self.config.semantic_confidence_threshold {
206                    return Ok(semantic_result);
207                }
208            }
209        }
210
211        Ok(None)
212    }
213
214    /// Generate a JSON Patch file from correction proposals
215    pub fn generate_patch_file(
216        &self,
217        corrections: &[CorrectionProposal],
218        spec_version: &str,
219    ) -> serde_json::Value {
220        CorrectionProposer::generate_patch_file(corrections, spec_version)
221    }
222
223    /// Get configuration
224    pub fn config(&self) -> &ContractDiffConfig {
225        &self.config
226    }
227}