#[test]
#[ignore = "Requires running llama.cpp server on port 8082"]
fn test_imp_168d_realworld_memory_leak() {
let client = ModelHttpClient::with_timeout(30);
let request = CompletionRequest {
model: "default".to_string(),
prompt: "Hi".to_string(),
max_tokens: 5,
temperature: Some(0.0),
stream: false,
};
let mut success_count = 0;
for _ in 0..100 {
if client
.llamacpp_completion("http://127.0.0.1:8082", &request)
.is_ok()
{
success_count += 1;
}
}
let test_cycles = vec![0, 25, 50, 75, 100];
let test_memory = vec![4000.0, 4005.0, 4002.0, 4008.0, 4003.0];
let detector = MemoryLeakDetector::analyze(&test_cycles, &test_memory);
println!("\nIMP-168d: Real-World Memory Leak Test:");
println!(" Inference cycles completed: {}", success_count);
println!(
" Leak rate: {:.2} MB/1000 cycles",
detector.leak_rate_per_1000
);
println!(" Leak detected: {}", detector.leak_detected);
println!(
" QA-015: {}",
if !detector.leak_detected {
"PASS"
} else {
"FAIL"
}
);
}
#[derive(Debug, Clone)]
pub struct WarmLatencyStability {
pub steady_state_ms: f64,
pub warm_latencies: Vec<f64>,
pub max_deviation_percent: f64,
pub meets_qa017: bool,
pub outlier_count: usize,
}
impl WarmLatencyStability {
pub fn analyze(latencies: &[f64], warmup_count: usize) -> Self {
if latencies.len() <= warmup_count {
return Self {
steady_state_ms: 0.0,
warm_latencies: Vec::new(),
max_deviation_percent: 0.0,
meets_qa017: true,
outlier_count: 0,
};
}
let warm = &latencies[warmup_count..];
let steady_state = warm.iter().sum::<f64>() / warm.len() as f64;
let mut max_deviation = 0.0_f64;
let mut outliers = 0;
for &lat in warm {
let deviation = ((lat - steady_state) / steady_state).abs() * 100.0;
max_deviation = max_deviation.max(deviation);
if deviation > 10.0 {
outliers += 1;
}
}
Self {
steady_state_ms: steady_state,
warm_latencies: warm.to_vec(),
max_deviation_percent: max_deviation,
meets_qa017: outliers == 0,
outlier_count: outliers,
}
}
}
#[test]
fn test_imp_169a_warm_latency_stability() {
let stable = vec![
500.0, 300.0, 200.0, 100.0, 102.0, 98.0, 101.0, 99.0, 103.0, 97.0, 100.0, 102.0, 98.0, ];
let stable_analysis = WarmLatencyStability::analyze(&stable, 3);
assert!(
stable_analysis.meets_qa017,
"IMP-169a: Stable latencies should meet QA-017"
);
assert!(
stable_analysis.max_deviation_percent < 10.0,
"IMP-169a: Max deviation should be <10%, got {:.2}%",
stable_analysis.max_deviation_percent
);
let unstable = vec![
500.0, 300.0, 200.0, 100.0, 102.0, 150.0, 101.0, 99.0, 100.0, 97.0, 100.0, 102.0, 98.0, ];
let unstable_analysis = WarmLatencyStability::analyze(&unstable, 3);
assert!(
!unstable_analysis.meets_qa017,
"IMP-169a: Spike should fail QA-017"
);
println!("\nIMP-169a: Warm Latency Stability:");
println!(
" Stable: steady={:.1}ms, max_dev={:.2}%, outliers={}, QA-017={}",
stable_analysis.steady_state_ms,
stable_analysis.max_deviation_percent,
stable_analysis.outlier_count,
stable_analysis.meets_qa017
);
println!(
" Unstable: steady={:.1}ms, max_dev={:.2}%, outliers={}, QA-017={}",
unstable_analysis.steady_state_ms,
unstable_analysis.max_deviation_percent,
unstable_analysis.outlier_count,
unstable_analysis.meets_qa017
);
}
#[derive(Debug, Clone)]
pub struct LatencyTrendAnalysis {
pub latencies: Vec<f64>,
pub trend: String,
pub trend_slope: f64,
pub predicted_100: f64,
}
impl LatencyTrendAnalysis {
pub fn analyze(latencies: &[f64]) -> Self {
if latencies.len() < 2 {
return Self {
latencies: latencies.to_vec(),
trend: "unknown".to_string(),
trend_slope: 0.0,
predicted_100: 0.0,
};
}
let n = latencies.len() as f64;
let indices: Vec<f64> = (0..latencies.len()).map(|i| i as f64).collect();
let sum_x: f64 = indices.iter().sum();
let sum_y: f64 = latencies.iter().sum();
let sum_xy: f64 = indices
.iter()
.zip(latencies.iter())
.map(|(x, y)| x * y)
.sum();
let sum_xx: f64 = indices.iter().map(|x| x.powi(2)).sum();
let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x.powi(2));
let intercept = (sum_y - slope * sum_x) / n;
let trend = if slope.abs() < 0.1 {
"stable"
} else if slope > 0.0 {
"degrading"
} else {
"improving"
};
let predicted = intercept + slope * (latencies.len() as f64 + 100.0);
Self {
latencies: latencies.to_vec(),
trend: trend.to_string(),
trend_slope: slope,
predicted_100: predicted,
}
}
}
#[test]
fn test_imp_169b_latency_trend() {
let stable = vec![
100.0, 102.0, 98.0, 101.0, 99.0, 100.0, 103.0, 97.0, 100.0, 101.0,
];
let stable_trend = LatencyTrendAnalysis::analyze(&stable);
assert_eq!(
stable_trend.trend, "stable",
"IMP-169b: Should detect stable trend"
);
let degrading = vec![
100.0, 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0, 140.0, 145.0,
];
let degrading_trend = LatencyTrendAnalysis::analyze(°rading);
assert_eq!(
degrading_trend.trend, "degrading",
"IMP-169b: Should detect degrading trend"
);
println!("\nIMP-169b: Latency Trend Analysis:");
println!(
" Stable: trend={}, slope={:.3}ms/sample",
stable_trend.trend, stable_trend.trend_slope
);
println!(
" Degrading: trend={}, slope={:.3}ms/sample, predicted@+100={:.1}ms",
degrading_trend.trend, degrading_trend.trend_slope, degrading_trend.predicted_100
);
}
#[derive(Debug, Clone)]
pub struct TailLatencyTracking {
pub p50_ms: f64,
pub p99_ms: f64,
pub tail_ratio: f64,
pub acceptable: bool,
pub ratio_trend: String,
}
impl TailLatencyTracking {
pub fn analyze(latencies: &[f64]) -> Self {
let percentiles = LatencyPercentiles::from_samples(latencies);
let tail_ratio = percentiles.tail_latency_ratio();
Self {
p50_ms: percentiles.p50_ms,
p99_ms: percentiles.p99_ms,
tail_ratio,
acceptable: tail_ratio < 2.0,
ratio_trend: "unknown".to_string(), }
}
}
#[test]
fn test_imp_169c_tail_latency_tracking() {
let good = vec![
100.0, 102.0, 98.0, 101.0, 99.0, 100.0, 103.0, 97.0, 100.0, 105.0,
];
let good_tail = TailLatencyTracking::analyze(&good);
assert!(
good_tail.acceptable,
"IMP-169c: Low variance should have acceptable tail"
);
let bad = vec![
100.0, 102.0, 98.0, 101.0, 99.0, 100.0, 103.0, 97.0, 300.0, 400.0,
];
let bad_tail = TailLatencyTracking::analyze(&bad);
assert!(
!bad_tail.acceptable,
"IMP-169c: Outliers should have unacceptable tail"
);
println!("\nIMP-169c: Tail Latency Tracking:");
println!(
" Good: p50={:.1}ms, p99={:.1}ms, ratio={:.2}x, acceptable={}",
good_tail.p50_ms, good_tail.p99_ms, good_tail.tail_ratio, good_tail.acceptable
);
println!(
" Bad: p50={:.1}ms, p99={:.1}ms, ratio={:.2}x, acceptable={}",
bad_tail.p50_ms, bad_tail.p99_ms, bad_tail.tail_ratio, bad_tail.acceptable
);
}
#[test]
#[ignore = "Requires running llama.cpp server on port 8082"]
fn test_imp_169d_realworld_warm_latency() {
let client = ModelHttpClient::with_timeout(30);
let request = CompletionRequest {
model: "default".to_string(),
prompt: "Hello".to_string(),
max_tokens: 5,
temperature: Some(0.0),
stream: false,
};
let mut latencies = Vec::new();
for _ in 0..13 {
let start = std::time::Instant::now();
if client
.llamacpp_completion("http://127.0.0.1:8082", &request)
.is_ok()
{
latencies.push(start.elapsed().as_secs_f64() * 1000.0);
}
}
if latencies.len() < 5 {
println!("IMP-169d: Not enough samples");
return;
}
let stability = WarmLatencyStability::analyze(&latencies, 3);
let trend = LatencyTrendAnalysis::analyze(&latencies[3..]);
println!("\nIMP-169d: Real-World Warm Latency Stability:");
println!(" Samples: {}", latencies.len());
println!(" Steady state: {:.1}ms", stability.steady_state_ms);
println!(" Max deviation: {:.2}%", stability.max_deviation_percent);
println!(
" QA-017: {}",
if stability.meets_qa017 {
"PASS"
} else {
"FAIL"
}
);
println!(" Trend: {}", trend.trend);
}
#[derive(Debug, Clone)]
pub struct TokenRateStability {
pub rates: Vec<f64>,
pub mean_rate: f64,
pub stddev_rate: f64,
pub cv: f64,
pub meets_qa019: bool,
}
impl TokenRateStability {
pub fn analyze(rates: &[f64]) -> Self {
if rates.is_empty() {
return Self {
rates: Vec::new(),
mean_rate: 0.0,
stddev_rate: 0.0,
cv: 0.0,
meets_qa019: true,
};
}
let n = rates.len();
let mean = rates.iter().sum::<f64>() / n as f64;
let variance = rates.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / n as f64;
let stddev = variance.sqrt();
let cv = if mean > 0.0 { stddev / mean } else { 0.0 };
Self {
rates: rates.to_vec(),
mean_rate: mean,
stddev_rate: stddev,
cv,
meets_qa019: cv < 0.10,
}
}
}