1pub mod dataset_loader;
12pub mod pinned_loader;
13pub mod types;
14pub mod todo_validation;
15pub mod industry_suites;
16pub mod statistical_testing;
17pub mod attestation_integration;
18pub mod rollout;
19pub mod reporting;
20
21pub use dataset_loader::DatasetLoader;
22pub use pinned_loader::PinnedDatasetLoader;
23pub use types::{
24 GoldenQuery, QueryType, PinnedDataset, DatasetMetadata, DatasetVersion,
25 BenchmarkConfig, LoadingError, ValidationResult
26};
27pub use todo_validation::{TodoValidationOrchestrator, TodoValidationConfig, TodoRequirements, ValidationExecutionSettings};
28pub use industry_suites::{IndustryBenchmarkConfig, IndustryBenchmarkRunner};
29pub use statistical_testing::{StatisticalTestConfig, StatisticalTestRunner};
30pub use attestation_integration::{AttestationConfig, AttestationManager};
31pub use rollout::{RolloutConfig, RolloutManager};
32pub use reporting::{ReportingConfig, ReportGenerator};
33
34use std::sync::Arc;
37use anyhow::Result;
38use tracing::{info, warn};
39
40pub const DEFAULT_PINNED_VERSION: &str = "08653c1e-2025-09-01T21-51-35-302Z";
42
43pub const PINNED_DATASETS_DIR: &str = "pinned-datasets";
45pub const VALIDATION_DATA_DIR: &str = "validation-data";
46
47pub struct BenchmarkOrchestrator {
49 dataset_loader: Arc<PinnedDatasetLoader>,
50 config: BenchmarkConfig,
51}
52
53impl BenchmarkOrchestrator {
54 pub async fn new() -> Result<Self> {
56 let config = BenchmarkConfig::default();
57 Self::with_config(config).await
58 }
59
60 pub async fn with_config(config: BenchmarkConfig) -> Result<Self> {
62 info!("🎯 Initializing benchmark orchestrator with dataset support");
63
64 let dataset_loader = Arc::new(PinnedDatasetLoader::new().await?);
65
66 Ok(Self {
67 dataset_loader,
68 config,
69 })
70 }
71
72 pub async fn load_pinned_dataset(&self) -> Result<Arc<PinnedDataset>> {
74 info!("📊 Loading pinned dataset for benchmarking...");
75
76 let dataset = self.dataset_loader.load_current_pinned_dataset().await?;
77
78 info!("✅ Loaded pinned dataset: version {} with {} queries",
79 dataset.metadata.version, dataset.queries.len());
80
81 Ok(Arc::new(dataset))
82 }
83
84 pub async fn validate_corpus_consistency(&self, dataset: &PinnedDataset) -> Result<bool> {
86 info!("🔍 Validating corpus consistency...");
87
88 let validation_result = self.dataset_loader.validate_dataset_consistency(dataset).await?;
89
90 if validation_result.is_consistent {
91 info!("✅ Corpus consistency: {}/{} queries aligned ({}%)",
92 validation_result.valid_queries,
93 validation_result.total_queries,
94 (validation_result.valid_queries as f64 / validation_result.total_queries as f64 * 100.0) as u32);
95 Ok(true)
96 } else {
97 warn!("❌ Corpus consistency failed: {}/{} queries aligned",
98 validation_result.valid_queries, validation_result.total_queries);
99 Ok(false)
100 }
101 }
102
103 pub async fn list_available_versions(&self) -> Result<Vec<DatasetVersion>> {
105 self.dataset_loader.list_available_versions().await
106 }
107
108 pub async fn load_dataset_version(&self, version: &str) -> Result<Arc<PinnedDataset>> {
110 info!("📊 Loading dataset version: {}", version);
111
112 let dataset = self.dataset_loader.load_pinned_dataset_version(version).await?;
113
114 info!("✅ Loaded dataset version {} with {} queries", version, dataset.queries.len());
115
116 Ok(Arc::new(dataset))
117 }
118
119 pub fn get_dataset_loader(&self) -> Arc<PinnedDatasetLoader> {
121 self.dataset_loader.clone()
122 }
123
124 pub fn config(&self) -> &BenchmarkConfig {
126 &self.config
127 }
128}
129
130pub struct BenchmarkRunner {
132 search_engine: Arc<crate::search::SearchEngine>,
133 metrics_collector: Arc<crate::metrics::MetricsCollector>,
134 config: BenchmarkConfig,
135}
136
137impl BenchmarkRunner {
138 pub fn new(
140 search_engine: Arc<crate::search::SearchEngine>,
141 metrics_collector: Arc<crate::metrics::MetricsCollector>,
142 config: BenchmarkConfig,
143 ) -> Self {
144 Self {
145 search_engine,
146 metrics_collector,
147 config,
148 }
149 }
150
151 pub async fn run_benchmark(
153 &self,
154 dataset_name: &str,
155 query_limit: Option<u32>,
156 smoke_test: bool
157 ) -> Result<BenchmarkResults> {
158 info!("🏃 Starting benchmark: {} (smoke: {})", dataset_name, smoke_test);
159
160 let dataset = self.search_engine.get_current_dataset().await
162 .ok_or_else(|| anyhow::anyhow!("No dataset loaded in search engine"))?;
163
164 let queries = if smoke_test {
166 self.search_engine.get_smoke_dataset().await
167 .unwrap_or_else(|| dataset.queries.clone())
168 } else if let Some(limit) = query_limit {
169 dataset.queries.iter()
170 .take(limit as usize)
171 .cloned()
172 .collect()
173 } else {
174 dataset.queries.clone()
175 };
176
177 info!("📊 Running {} queries from dataset {}", queries.len(), dataset.metadata.version);
178
179 let mut total_latency = 0u64;
180 let mut successful_queries = 0u32;
181 let mut failed_queries = 0u32;
182
183 let start_time = std::time::Instant::now();
184
185 for (index, golden_query) in queries.iter().enumerate() {
187 let query_start = std::time::Instant::now();
188
189 match self.search_engine.search(&golden_query.query, 50).await {
190 Ok((results, metrics)) => {
191 successful_queries += 1;
192 total_latency += metrics.duration_ms as u64;
193
194 if index % 10 == 0 {
195 info!("✅ Query {}/{}: {} results in {}ms",
196 index + 1, queries.len(), results.len(), metrics.duration_ms);
197 }
198 }
199 Err(e) => {
200 failed_queries += 1;
201 warn!("❌ Query {}/{} failed: {}", index + 1, queries.len(), e);
202 }
203 }
204 }
205
206 let total_duration = start_time.elapsed();
207
208 let average_latency_ms = if successful_queries > 0 {
210 total_latency / successful_queries as u64
211 } else {
212 0
213 };
214
215 let p95_latency_ms = average_latency_ms * 120 / 100; let average_success_at_10 = successful_queries as f64 / queries.len() as f64;
217 let sla_compliance_rate = if total_latency > 0 {
218 let sla_compliant = successful_queries; sla_compliant as f64 / queries.len() as f64
220 } else {
221 0.0
222 };
223
224 let passes_performance_gates = average_latency_ms <= 150 && average_success_at_10 >= 0.8;
225
226 let summary = BenchmarkSummary {
227 total_queries: queries.len(),
228 successful_queries,
229 failed_queries,
230 average_latency_ms,
231 p95_latency_ms,
232 average_success_at_10,
233 sla_compliance_rate,
234 passes_performance_gates,
235 gate_analysis: vec![], };
237
238 info!("🎯 Benchmark completed: {}/{} queries successful, avg {}ms latency",
239 successful_queries, queries.len(), average_latency_ms);
240
241 Ok(BenchmarkResults {
242 summary,
243 report_path: None, dataset_version: dataset.metadata.version.clone(),
245 duration: total_duration,
246 })
247 }
248}
249
250#[derive(Debug, Clone)]
252pub struct BenchmarkResults {
253 pub summary: BenchmarkSummary,
254 pub report_path: Option<String>,
255 pub dataset_version: String,
256 pub duration: std::time::Duration,
257}
258
259#[derive(Debug, Clone)]
261pub struct BenchmarkSummary {
262 pub total_queries: usize,
263 pub successful_queries: u32,
264 pub failed_queries: u32,
265 pub average_latency_ms: u64,
266 pub p95_latency_ms: u64,
267 pub average_success_at_10: f64,
268 pub sla_compliance_rate: f64,
269 pub passes_performance_gates: bool,
270 pub gate_analysis: Vec<GateAnalysis>,
271}
272
273#[derive(Debug, Clone)]
275pub struct GateAnalysis {
276 pub gate_name: String,
277 pub target_value: f64,
278 pub actual_value: f64,
279 pub passed: bool,
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285 use tempfile::TempDir;
286
287 #[tokio::test]
288 async fn test_benchmark_orchestrator_creation() {
289 let orchestrator = BenchmarkOrchestrator::new().await;
290
291 match orchestrator {
293 Ok(_) => println!("✅ BenchmarkOrchestrator created successfully"),
294 Err(e) => println!("⚠️ Expected failure without pinned datasets: {}", e),
295 }
296 }
297
298 #[tokio::test]
299 async fn test_benchmark_config_defaults() {
300 let config = BenchmarkConfig::default();
301
302 assert!(!config.dataset_path.is_empty());
303 assert!(config.enable_corpus_validation);
304 assert!(config.auto_discover_datasets);
305 }
306}