vios_app 0.1.1

Small JSON vaults: Argon2id + AES-GCM, with optional AAD binding.
Documentation
//! Congruence Scoring – Whisper Vision™ + Whisper Loop™ Emotion Alignment

use crate::vision::whisper_vision::FacialEmotion;

/// Matches the dominant voice tone classification with facial emotion
pub fn calculate_congruence(tone: &str, face: &FacialEmotion) -> f32 {
    let tone_emotion = tone_to_emotion_map(tone);

    match tone_emotion {
        Some(te) => {
            if te == *face {
                1.0 // perfect match
            } else {
                let distance = emotional_distance(&te, face);
                (1.0 - distance).max(0.0)
            }
        }
        None => 0.5, // neutral fallback
    }
}

/// Converts a tone classification into an approximate facial emotion
fn tone_to_emotion_map(tone: &str) -> Option<FacialEmotion> {
    match tone.to_lowercase().as_str() {
        "joy" | "happy" => Some(FacialEmotion::Happy),
        "sad" | "grief" => Some(FacialEmotion::Sad),
        "angry" | "frustrated" => Some(FacialEmotion::Angry),
        "fear" | "anxious" => Some(FacialEmotion::Fearful),
        "surprised" => Some(FacialEmotion::Surprised),
        "disgusted" => Some(FacialEmotion::Disgusted),
        "neutral" => Some(FacialEmotion::Neutral),
        _ => None,
    }
}

/// Returns emotional distance between tone and facial expression
fn emotional_distance(a: &FacialEmotion, b: &FacialEmotion) -> f32 {
    use FacialEmotion::*;

    match (a, b) {
        (Happy, Sad) | (Sad, Happy) => 0.9,
        (Angry, Happy) | (Happy, Angry) => 0.8,
        (Fearful, Happy) | (Happy, Fearful) => 0.7,
        (Neutral, Neutral) => 0.0,
        (a, b) if a == b => 0.0,
        _ => 0.5, // default medium distance
    }
}