Platform restructured into crates/, added AI service and detector,
migrated control-center-ui to Leptos 0.8
206 lines
7.2 KiB
Rust
206 lines
7.2 KiB
Rust
/// Inference engine
|
|
/// Converts technology detections into infrastructure requirements
|
|
/// Uses rule-based system to suggest taskservs based on what was detected
|
|
use crate::models::{Detection, Requirement, Technology};
|
|
|
|
/// Inference rule: detect pattern → infer requirement
|
|
pub struct InferenceRule {
|
|
pub name: &'static str,
|
|
pub condition: fn(&[Detection]) -> bool,
|
|
pub infer: fn(&[Detection]) -> Vec<Requirement>,
|
|
}
|
|
|
|
/// Main inference engine
|
|
pub struct InferenceEngine {
|
|
rules: Vec<InferenceRule>,
|
|
}
|
|
|
|
impl InferenceEngine {
|
|
pub fn new(rules: Vec<InferenceRule>) -> Self {
|
|
Self { rules }
|
|
}
|
|
|
|
/// Create engine with default rules
|
|
pub fn with_default_rules() -> Self {
|
|
let rules = vec![
|
|
// Rule 1: Node.js + Express → Redis (API caching)
|
|
InferenceRule {
|
|
name: "NodeJS API recommends Redis",
|
|
condition: |detections| {
|
|
detections
|
|
.iter()
|
|
.any(|d| d.technology == Technology::NodeJs)
|
|
&& detections
|
|
.iter()
|
|
.any(|d| d.technology == Technology::Express)
|
|
},
|
|
infer: |_| {
|
|
vec![Requirement::new(
|
|
"redis",
|
|
"Express.js APIs benefit from caching layer",
|
|
0.85,
|
|
false,
|
|
)]
|
|
},
|
|
},
|
|
// Rule 2: Any database detected → needs backups
|
|
InferenceRule {
|
|
name: "Databases need backup strategy",
|
|
condition: |detections| {
|
|
detections.iter().any(|d| {
|
|
matches!(
|
|
d.technology,
|
|
Technology::Postgres | Technology::Mysql | Technology::Mongodb
|
|
)
|
|
})
|
|
},
|
|
infer: |detections| {
|
|
detections
|
|
.iter()
|
|
.filter(|d| {
|
|
matches!(
|
|
d.technology,
|
|
Technology::Postgres | Technology::Mysql | Technology::Mongodb
|
|
)
|
|
})
|
|
.map(|d| {
|
|
let taskserv = match d.technology {
|
|
Technology::Postgres => "postgres-backup",
|
|
Technology::Mysql => "mysql-backup",
|
|
Technology::Mongodb => "mongodb-backup",
|
|
_ => "backup",
|
|
};
|
|
Requirement::new(
|
|
taskserv,
|
|
"Production databases require backup strategy",
|
|
0.90,
|
|
false,
|
|
)
|
|
})
|
|
.collect()
|
|
},
|
|
},
|
|
// Rule 3: Containerized apps need reverse proxy
|
|
InferenceRule {
|
|
name: "Docker apps need reverse proxy",
|
|
condition: |detections| {
|
|
detections
|
|
.iter()
|
|
.any(|d| d.technology == Technology::Docker)
|
|
},
|
|
infer: |_| {
|
|
vec![Requirement::new(
|
|
"nginx",
|
|
"Containerized applications should run behind reverse proxy",
|
|
0.75,
|
|
false,
|
|
)]
|
|
},
|
|
},
|
|
// Rule 4: Any language detected → needs runtime
|
|
InferenceRule {
|
|
name: "Languages need runtime",
|
|
condition: |detections| {
|
|
detections.iter().any(|d| {
|
|
matches!(
|
|
d.technology,
|
|
Technology::NodeJs | Technology::Python | Technology::Rust
|
|
)
|
|
})
|
|
},
|
|
infer: |detections| {
|
|
let lang = detections.iter().find(|d| {
|
|
matches!(
|
|
d.technology,
|
|
Technology::NodeJs | Technology::Python | Technology::Rust
|
|
)
|
|
});
|
|
|
|
match lang.map(|d| d.technology) {
|
|
Some(Technology::NodeJs) => vec![], // Already detected
|
|
Some(Technology::Python) => vec![], // Already detected
|
|
Some(Technology::Rust) => vec![], // Already detected
|
|
_ => vec![],
|
|
}
|
|
},
|
|
},
|
|
// Rule 5: PostgreSQL detected → add monitoring
|
|
InferenceRule {
|
|
name: "PostgreSQL should have monitoring",
|
|
condition: |detections| {
|
|
detections
|
|
.iter()
|
|
.any(|d| d.technology == Technology::Postgres)
|
|
},
|
|
infer: |_| {
|
|
vec![Requirement::new(
|
|
"pg-monitoring",
|
|
"Monitor PostgreSQL performance in production",
|
|
0.70,
|
|
false,
|
|
)
|
|
.with_min_version("14.0".to_string())]
|
|
},
|
|
},
|
|
];
|
|
|
|
Self::new(rules)
|
|
}
|
|
|
|
/// Infer requirements from detections
|
|
pub fn infer_requirements(&self, detections: &[Detection]) -> Vec<Requirement> {
|
|
let mut requirements = Vec::new();
|
|
|
|
for rule in &self.rules {
|
|
if (rule.condition)(detections) {
|
|
let inferred = (rule.infer)(detections);
|
|
requirements.extend(inferred);
|
|
}
|
|
}
|
|
|
|
// Deduplicate and keep highest confidence
|
|
requirements.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap());
|
|
requirements.dedup_by(|a, b| a.taskserv == b.taskserv);
|
|
|
|
requirements
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_nodejs_express_recommends_redis() {
|
|
let detections = vec![
|
|
Detection::new(Technology::NodeJs),
|
|
Detection::new(Technology::Express),
|
|
];
|
|
|
|
let engine = InferenceEngine::with_default_rules();
|
|
let requirements = engine.infer_requirements(&detections);
|
|
|
|
assert!(requirements.iter().any(|r| r.taskserv == "redis"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_docker_recommends_nginx() {
|
|
let detections = vec![Detection::new(Technology::Docker)];
|
|
|
|
let engine = InferenceEngine::with_default_rules();
|
|
let requirements = engine.infer_requirements(&detections);
|
|
|
|
assert!(requirements.iter().any(|r| r.taskserv == "nginx"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_postgres_detected() {
|
|
let detections = vec![Detection::new(Technology::Postgres)];
|
|
|
|
let engine = InferenceEngine::with_default_rules();
|
|
let requirements = engine.infer_requirements(&detections);
|
|
|
|
assert!(!requirements.is_empty());
|
|
}
|
|
}
|