Jesús Pérez ac3f93fe1d fix: Pre-commit configuration and TOML syntax corrections
**Problems Fixed:**
- TOML syntax errors in workspace.toml (inline tables spanning multiple lines)
- TOML syntax errors in vapora.toml (invalid variable substitution syntax)
- YAML multi-document handling (kubernetes and provisioning files)
- Markdown linting issues (disabled temporarily pending review)
- Rust formatting with nightly toolchain

**Changes Made:**
1. Fixed provisioning/vapora-wrksp/workspace.toml:
   - Converted inline tables to proper nested sections
   - Lines 21-39: [storage.surrealdb], [storage.redis], [storage.nats]

2. Fixed config/vapora.toml:
   - Replaced shell-style ${VAR:-default} syntax with literal values
   - All environment-based config marked with comments for runtime override

3. Updated .pre-commit-config.yaml:
   - Added kubernetes/ and provisioning/ to check-yaml exclusions
   - Disabled markdownlint hook pending markdown file cleanup
   - Keep: rust-fmt, clippy, toml check, yaml check, end-of-file, trailing-whitespace

**All Passing Hooks:**
 Rust formatting (cargo +nightly fmt)
 Rust linting (cargo clippy)
 TOML validation
 YAML validation (with multi-document support)
 End-of-file formatting
 Trailing whitespace removal
2026-01-11 21:46:08 +00:00

326 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::collections::HashMap;
use crate::models::*;
/// Reasoning engine for inferring insights from execution history
pub struct ReasoningEngine;
impl ReasoningEngine {
/// Analyze execution records for patterns
pub fn find_patterns(records: &[ExecutionRecord]) -> Vec<String> {
let mut patterns = Vec::new();
// Pattern 1: Task type failure rate
let mut task_failure_rates: HashMap<String, (u32, u32)> = HashMap::new();
for record in records {
task_failure_rates
.entry(record.task_type.clone())
.or_insert((0, 0));
let entry = task_failure_rates.get_mut(&record.task_type).unwrap();
entry.0 += 1;
if !record.success {
entry.1 += 1;
}
}
for (task_type, (total, failures)) in task_failure_rates.iter() {
let failure_rate = *failures as f64 / *total as f64;
if failure_rate > 0.3 {
patterns.push(format!(
"High failure rate detected for '{}': {:.1}%",
task_type,
failure_rate * 100.0
));
}
}
// Pattern 2: Duration anomalies
if records.len() > 3 {
let mut durations: Vec<u64> = records.iter().map(|r| r.duration_ms).collect();
durations.sort_unstable();
let median = durations[durations.len() / 2];
let avg = durations.iter().sum::<u64>() as f64 / durations.len() as f64;
if avg > median as f64 * 2.0 {
patterns.push(format!(
"High latency variance detected: avg={:.0}ms, median={}ms",
avg, median
));
}
}
// Pattern 3: Agent expertise misalignment
let mut agent_task_types: HashMap<String, HashMap<String, u32>> = HashMap::new();
for record in records {
agent_task_types
.entry(record.agent_id.clone())
.or_default()
.entry(record.task_type.clone())
.and_modify(|c| *c += 1)
.or_insert(1);
}
for (agent_id, task_map) in agent_task_types {
if task_map.len() > 5 {
patterns.push(format!(
"Agent '{}' is assigned to {} different task types (possible overallocation)",
agent_id,
task_map.len()
));
}
}
patterns
}
/// Predict success probability for a task
pub fn predict_success(task_type: &str, similar_records: &[ExecutionRecord]) -> (f64, String) {
if similar_records.is_empty() {
return (0.5, "No historical data available".to_string());
}
let success_count = similar_records.iter().filter(|r| r.success).count() as f64;
let success_rate = success_count / similar_records.len() as f64;
let reasoning = if success_rate > 0.9 {
format!(
"High success probability based on {} successful similar tasks",
success_count as usize
)
} else if success_rate > 0.7 {
"Moderate success probability with some historical challenges".to_string()
} else if success_rate > 0.5 {
"Below-average success rate - recommend expert agent".to_string()
} else {
format!(
"Critical: {} only {} successful of {} similar tasks",
task_type,
success_count as usize,
similar_records.len()
)
};
(success_rate, reasoning)
}
/// Estimate task duration
pub fn estimate_duration(similar_records: &[ExecutionRecord]) -> (u64, String) {
if similar_records.is_empty() {
return (
300_000,
"No historical data - using default 5 minutes".to_string(),
);
}
let mut durations: Vec<u64> = similar_records.iter().map(|r| r.duration_ms).collect();
durations.sort_unstable();
let median = durations[durations.len() / 2];
let avg = durations.iter().sum::<u64>() / durations.len() as u64;
let max = durations[durations.len() - 1];
let estimate = (avg + median) / 2;
let reasoning = if max > estimate * 3 {
format!(
"High variance in execution time: avg={}ms, max={}ms",
avg, max
)
} else {
format!("Based on {} similar tasks: avg={}ms", durations.len(), avg)
};
(estimate, reasoning)
}
/// Recommend best agent for a task
pub fn recommend_agent(
task_type: &str,
agent_profiles: &[AgentProfile],
) -> Option<(String, String)> {
agent_profiles
.iter()
.filter(|profile| {
profile.primary_task_types.contains(&task_type.to_string())
|| profile.expertise_score > 75.0
})
.max_by(|a, b| {
a.expertise_score
.partial_cmp(&b.expertise_score)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|profile| {
let reasoning = format!(
"Agent '{}' has {:.1}% expertise in {} tasks",
profile.agent_id,
profile.expertise_score,
profile.primary_task_types.join(", ")
);
(profile.agent_id.clone(), reasoning)
})
}
/// Chain reasoning - find root cause chains
pub fn find_root_cause_chain(records: &[ExecutionRecord]) -> Vec<Vec<String>> {
let mut chains: Vec<Vec<String>> = Vec::new();
let mut visited = std::collections::HashSet::new();
for record in records
.iter()
.filter(|r| !r.success && r.root_cause.is_some())
{
if visited.contains(&record.id) {
continue;
}
let mut chain = vec![record.root_cause.clone().unwrap()];
visited.insert(record.id.clone());
// Find similar errors in history
for other in records
.iter()
.filter(|r| !visited.contains(&r.id) && r.solution.is_some())
{
if let Some(ref root) = other.root_cause {
if root.contains(chain.last().unwrap()) {
chain.push(other.solution.clone().unwrap());
visited.insert(other.id.clone());
break;
}
}
}
if chain.len() > 1 {
chains.push(chain);
}
}
chains
}
/// Generate actionable insights
pub fn generate_insights(
stats: &GraphStatistics,
patterns: &[String],
profiles: &[AgentProfile],
) -> Vec<String> {
let mut insights = Vec::new();
// Insight 1: Overall system health
if stats.success_rate > 0.95 {
insights.push("✓ System is very reliable (>95% success rate)".to_string());
} else if stats.success_rate < 0.75 {
insights.push(format!(
"⚠ System reliability is concerning ({:.1}% success rate)",
stats.success_rate * 100.0
));
}
// Insight 2: Performance metrics
if stats.avg_duration_ms > 60_000.0 {
insights.push(format!(
"⚠ Average task duration is high ({:.0}ms)",
stats.avg_duration_ms
));
}
// Insight 3: Coverage
insights.push(format!(
" Knowledge graph contains {} execution records across {} agents",
stats.total_records, stats.distinct_agents
));
// Insight 4: Agent expertise distribution
let avg_expertise =
profiles.iter().map(|p| p.expertise_score).sum::<f64>() / profiles.len().max(1) as f64;
if avg_expertise < 70.0 {
insights.push(format!(
"⚠ Average agent expertise is below target ({:.0}%)",
avg_expertise
));
}
// Insight 5: Patterns
insights.extend(patterns.iter().cloned());
insights
}
}
#[cfg(test)]
mod tests {
use chrono::Utc;
use super::*;
#[test]
fn test_predict_success() {
let records = vec![
ExecutionRecord {
id: "1".to_string(),
task_id: "t1".to_string(),
agent_id: "a1".to_string(),
agent_role: None,
task_type: "dev".to_string(),
description: "test".to_string(),
root_cause: None,
solution: None,
duration_ms: 1000,
input_tokens: 100,
output_tokens: 50,
cost_cents: 40,
provider: "claude".to_string(),
success: true,
error: None,
timestamp: Utc::now(),
},
ExecutionRecord {
id: "2".to_string(),
task_id: "t2".to_string(),
agent_id: "a1".to_string(),
agent_role: None,
task_type: "dev".to_string(),
description: "test".to_string(),
root_cause: None,
solution: None,
duration_ms: 1000,
input_tokens: 100,
output_tokens: 50,
cost_cents: 45,
provider: "claude".to_string(),
success: true,
error: None,
timestamp: Utc::now(),
},
];
let (prob, _) = ReasoningEngine::predict_success("dev", &records);
assert_eq!(prob, 1.0);
}
#[test]
fn test_estimate_duration() {
let records = vec![ExecutionRecord {
id: "1".to_string(),
task_id: "t1".to_string(),
agent_id: "a1".to_string(),
agent_role: None,
task_type: "dev".to_string(),
description: "test".to_string(),
root_cause: None,
solution: None,
duration_ms: 1000,
input_tokens: 100,
output_tokens: 50,
cost_cents: 50,
provider: "claude".to_string(),
success: true,
error: None,
timestamp: Utc::now(),
}];
let (duration, _) = ReasoningEngine::estimate_duration(&records);
assert_eq!(duration, 1000);
}
}