use regex::Regex;
use std::sync::LazyLock;
use crate::types::gate::{GateContext, GateContextWithFeedback, GateResult, GateType};
const ROUTINE_MAX_LENGTH: usize = 20;
const AMYGDALA_AROUSAL_THRESHOLD: f64 = 0.6;
const CAPS_MIN_LENGTH: usize = 8;
const CAPS_RATIO_THRESHOLD: f64 = 0.5;
static ROUTINE_PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
vec![
Regex::new(r"(?i)^(ok|okay|yes|yep|yeah|sure|thanks|thank you|got it|understood|right|alright|cool|nice|great|good|fine|perfect|exactly|correct|agreed|noted|i see)\.?!?$").expect("valid regex"),
Regex::new(r"^[\p{Emoji}\s]{1,4}$").expect("valid regex"),
Regex::new(r"^\d{1,2}\.?$").expect("valid regex"),
]
});
static URGENT_PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
vec![
Regex::new(r"(?i)\b(error|crash|bug|fail|broke|broken|block)\w*\b").expect("valid regex"),
Regex::new(r"(?i)\b(urgent|asap|deadline|immediately)\b").expect("valid regex"),
Regex::new(r"(?i)\b(actually|wait|no i meant|that'?s wrong)\b").expect("valid regex"),
Regex::new(r"(?i)\b(help!|please fix|fix now|need help)\b").expect("valid regex"),
]
});
pub fn classify_gate(ctx: &GateContext) -> GateResult {
let message = ctx.message.trim();
if check_urgent(message, ctx.arousal) {
return GateResult {
gate: GateType::Urgent,
confidence: if ctx.arousal >= AMYGDALA_AROUSAL_THRESHOLD {
0.85
} else {
0.7
},
reason: String::from("urgent: error/crisis/correction pattern detected"),
};
}
if check_routine(message) {
return GateResult {
gate: GateType::Routine,
confidence: 0.95,
reason: String::from("routine: short acknowledgment"),
};
}
GateResult {
gate: GateType::Novel,
confidence: 0.5,
reason: String::from("novel: full pipeline"),
}
}
pub fn classify_gate_with_feedback(ctx: &GateContextWithFeedback) -> GateResult {
let mut result = classify_gate(&ctx.base);
if let Some(prev) = ctx.previous_gate {
if prev.gate == GateType::Urgent && ctx.base.arousal >= 0.4 {
if result.gate != GateType::Urgent {
result.gate = GateType::Urgent;
result.confidence = 0.7;
result.reason = String::from("urgent: arousal amplification from previous gate");
}
}
}
if ctx.resource_pressure > 0.7 && result.gate == GateType::Urgent {
result.confidence = (result.confidence - 0.15).max(0.5);
result.reason = format!("{} [pressure-damped]", result.reason);
}
result
}
fn check_urgent(message: &str, arousal: f64) -> bool {
if arousal >= AMYGDALA_AROUSAL_THRESHOLD {
return true;
}
let alpha_chars: Vec<char> = message.chars().filter(|c| c.is_alphabetic()).collect();
if alpha_chars.len() >= CAPS_MIN_LENGTH {
let upper = alpha_chars.iter().filter(|c| c.is_uppercase()).count();
if upper as f64 / alpha_chars.len() as f64 >= CAPS_RATIO_THRESHOLD {
return true;
}
}
URGENT_PATTERNS.iter().any(|p| p.is_match(message))
}
fn check_routine(message: &str) -> bool {
if message.len() > ROUTINE_MAX_LENGTH {
return false;
}
ROUTINE_PATTERNS.iter().any(|p| p.is_match(message))
}
pub fn classify_problem_type(
message: &str,
dimension_count: usize,
arousal: f64,
) -> crate::types::gate::ProblemType {
use crate::types::gate::ProblemType;
if dimension_count >= 4 && arousal >= 0.3 {
return ProblemType::SystemDilemma;
}
if dimension_count >= 2 {
return ProblemType::Dilemma;
}
if message.contains('?') {
return ProblemType::Question;
}
static TASK_PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
vec![
Regex::new(r"(?i)\b(create|build|implement|write|fix|update|add|remove|refactor|deploy)\b").expect("valid regex"),
]
});
if TASK_PATTERNS.iter().any(|p| p.is_match(message)) {
return ProblemType::Task;
}
ProblemType::SimpleChat
}
#[cfg(test)]
mod tests {
use super::*;
fn make_ctx<'a>(message: &'a str, arousal: f64) -> GateContext<'a> {
GateContext {
message,
recent_messages: &[],
arousal,
}
}
#[test]
fn routine_ok() {
let ctx = make_ctx("ok", 0.0);
let r = classify_gate(&ctx);
assert_eq!(r.gate, GateType::Routine);
assert!(r.confidence > 0.9);
}
#[test]
fn routine_emoji() {
let ctx = make_ctx("👍", 0.0);
let r = classify_gate(&ctx);
assert_eq!(r.gate, GateType::Routine);
}
#[test]
fn urgent_error_keyword() {
let ctx = make_ctx("I got an error in the build", 0.0);
let r = classify_gate(&ctx);
assert_eq!(r.gate, GateType::Urgent);
}
#[test]
fn urgent_high_arousal() {
let ctx = make_ctx("something happened", 0.7);
let r = classify_gate(&ctx);
assert_eq!(r.gate, GateType::Urgent);
}
#[test]
fn urgent_caps() {
let ctx = make_ctx("WHY IS THIS BROKEN", 0.0);
let r = classify_gate(&ctx);
assert_eq!(r.gate, GateType::Urgent);
}
#[test]
fn novel_default() {
let ctx = make_ctx("Let's discuss quantum computing approaches", 0.0);
let r = classify_gate(&ctx);
assert_eq!(r.gate, GateType::Novel);
}
#[test]
fn feedback_pressure_damps_urgent_confidence() {
let base = make_ctx("error in the build", 0.0);
let ctx = GateContextWithFeedback {
base,
resource_pressure: 0.8,
previous_gate: None,
};
let r = classify_gate_with_feedback(&ctx);
assert_eq!(r.gate, GateType::Urgent);
assert!(r.reason.contains("pressure-damped"));
}
#[test]
fn feedback_arousal_amplification() {
let base = make_ctx("still dealing with this", 0.5);
let prev = GateResult {
gate: GateType::Urgent,
confidence: 0.85,
reason: String::from("prev urgent"),
};
let ctx = GateContextWithFeedback {
base,
resource_pressure: 0.3,
previous_gate: Some(&prev),
};
let r = classify_gate_with_feedback(&ctx);
assert_eq!(r.gate, GateType::Urgent);
}
#[test]
fn problem_type_question() {
let pt = classify_problem_type("What is the best approach?", 0, 0.0);
assert_eq!(pt, crate::types::gate::ProblemType::Question);
}
#[test]
fn problem_type_task() {
let pt = classify_problem_type("Create a new user registration flow", 0, 0.0);
assert_eq!(pt, crate::types::gate::ProblemType::Task);
}
#[test]
fn problem_type_dilemma() {
let pt = classify_problem_type("Should we use microservices or monolith", 2, 0.0);
assert_eq!(pt, crate::types::gate::ProblemType::Dilemma);
}
}