use serde::{Deserialize, Serialize};
use super::types::{Emotion, MoodVector};
#[cfg(feature = "traits")]
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
#[must_use]
pub fn emotion_amplifier(
profile: &crate::traits::PersonalityProfile,
emotion: Emotion,
stimulus_valence: f32,
) -> f32 {
use crate::traits::TraitKind;
let patience = profile.get_trait(TraitKind::Patience).normalized();
let confidence = profile.get_trait(TraitKind::Confidence).normalized();
let empathy = profile.get_trait(TraitKind::Empathy).normalized();
let skepticism = profile.get_trait(TraitKind::Skepticism).normalized();
let base = 1.0;
let modifier = match emotion {
Emotion::Frustration | Emotion::Arousal if stimulus_valence < 0.0 => {
-patience * 0.3 - confidence * 0.2 + skepticism * 0.1
}
Emotion::Trust => empathy * 0.3 - skepticism * 0.2,
Emotion::Joy if stimulus_valence > 0.0 => empathy * 0.2,
Emotion::Joy => -patience * 0.2 - confidence * 0.1,
Emotion::Interest => {
let curiosity = profile.get_trait(TraitKind::Curiosity).normalized();
curiosity * 0.3
}
_ => 0.0,
};
(base + modifier).clamp(0.5, 2.0)
}
#[cfg(feature = "traits")]
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
pub fn mood_trait_influence(mood: &MoodVector, trait_kind: crate::traits::TraitKind) -> f32 {
use crate::traits::TraitKind;
match trait_kind {
TraitKind::Directness => mood.frustration * 0.5 + mood.dominance * 0.3,
TraitKind::Warmth => mood.joy * 0.4 + mood.trust * 0.3,
TraitKind::Patience => -mood.frustration * 0.5 - mood.arousal * 0.3,
TraitKind::Confidence => mood.dominance * 0.4 + mood.joy * 0.2,
TraitKind::Humor => mood.joy * 0.4 - mood.frustration * 0.3,
TraitKind::Empathy => mood.trust * 0.3 + mood.joy * 0.2,
TraitKind::Curiosity => mood.interest * 0.5 + mood.arousal * 0.2,
TraitKind::Creativity => mood.interest * 0.3 + mood.arousal * 0.2 + mood.joy * 0.2,
TraitKind::Formality => -mood.arousal * 0.3 - mood.frustration * 0.2,
TraitKind::Verbosity => mood.arousal * 0.3 + mood.interest * 0.2,
TraitKind::RiskTolerance => {
mood.dominance * 0.3 + mood.arousal * 0.2 - mood.frustration * 0.2
}
TraitKind::Skepticism => -mood.trust * 0.4 + mood.frustration * 0.2,
TraitKind::Autonomy => mood.dominance * 0.4 + mood.arousal * 0.2,
TraitKind::Pedagogy => mood.interest * 0.3 + mood.joy * 0.2 - mood.frustration * 0.2,
TraitKind::Precision => -mood.arousal * 0.3 + mood.dominance * 0.2,
}
.clamp(-1.0, 1.0)
}
#[cfg(feature = "traits")]
struct TraitMoodModifier {
valence: f32,
arousal: f32,
}
#[cfg(feature = "traits")]
fn trait_mood_modifier(
kind: crate::traits::TraitKind,
level: crate::traits::TraitLevel,
) -> TraitMoodModifier {
use crate::traits::{TraitKind as TK, TraitLevel as TL};
let (v, a) = match (kind, level) {
(TK::Formality, TL::Lowest) => (0.05, 0.15),
(TK::Formality, TL::Low) => (0.05, 0.05),
(TK::Formality, TL::High) => (-0.05, -0.1),
(TK::Formality, TL::Highest) => (-0.05, -0.15),
(TK::Humor, TL::Lowest) => (-0.1, -0.1),
(TK::Humor, TL::Low) => (-0.05, -0.05),
(TK::Humor, TL::High) => (0.15, 0.1),
(TK::Humor, TL::Highest) => (0.2, 0.15),
(TK::Warmth, TL::Lowest) => (-0.25, -0.15),
(TK::Warmth, TL::Low) => (-0.1, -0.1),
(TK::Warmth, TL::High) => (0.2, 0.1),
(TK::Warmth, TL::Highest) => (0.3, 0.2),
(TK::Empathy, TL::Lowest) => (-0.15, -0.1),
(TK::Empathy, TL::Low) => (-0.05, -0.05),
(TK::Empathy, TL::High) => (0.1, 0.0),
(TK::Empathy, TL::Highest) => (0.15, -0.05),
(TK::Patience, TL::Lowest) => (-0.1, 0.15),
(TK::Patience, TL::Low) => (-0.05, 0.05),
(TK::Patience, TL::High) => (0.1, -0.1),
(TK::Patience, TL::Highest) => (0.15, -0.15),
(TK::Confidence, TL::Lowest) => (-0.15, -0.1),
(TK::Confidence, TL::Low) => (-0.05, -0.05),
(TK::Confidence, TL::High) => (0.1, 0.1),
(TK::Confidence, TL::Highest) => (0.15, 0.15),
(TK::Creativity, TL::Lowest) => (-0.05, -0.1),
(TK::Creativity, TL::Low) => (0.0, -0.05),
(TK::Creativity, TL::High) => (0.1, 0.1),
(TK::Creativity, TL::Highest) => (0.15, 0.15),
(TK::RiskTolerance, TL::Lowest) => (-0.1, -0.15),
(TK::RiskTolerance, TL::Low) => (-0.05, -0.05),
(TK::RiskTolerance, TL::High) => (0.05, 0.1),
(TK::RiskTolerance, TL::Highest) => (0.1, 0.2),
(TK::Curiosity, TL::Lowest) => (-0.05, -0.1),
(TK::Curiosity, TL::Low) => (0.0, -0.05),
(TK::Curiosity, TL::High) => (0.1, 0.1),
(TK::Curiosity, TL::Highest) => (0.15, 0.15),
(TK::Verbosity, TL::Lowest) => (0.0, -0.05),
(TK::Verbosity, TL::Low) => (0.0, -0.02),
(TK::Verbosity, TL::High) => (0.0, 0.05),
(TK::Verbosity, TL::Highest) => (0.0, 0.1),
(TK::Directness, TL::Lowest) => (0.0, -0.05),
(TK::Directness, TL::Low) => (0.0, -0.02),
(TK::Directness, TL::High) => (0.0, 0.05),
(TK::Directness, TL::Highest) => (-0.05, 0.1),
(TK::Skepticism, TL::Lowest) => (0.05, -0.05),
(TK::Skepticism, TL::Low) => (0.02, -0.02),
(TK::Skepticism, TL::High) => (-0.05, 0.05),
(TK::Skepticism, TL::Highest) => (-0.1, 0.1),
(TK::Autonomy, TL::Lowest) => (-0.05, -0.1),
(TK::Autonomy, TL::Low) => (-0.02, -0.05),
(TK::Autonomy, TL::High) => (0.05, 0.1),
(TK::Autonomy, TL::Highest) => (0.1, 0.15),
(TK::Pedagogy, TL::Lowest) => (0.0, -0.05),
(TK::Pedagogy, TL::Low) => (0.0, -0.02),
(TK::Pedagogy, TL::High) => (0.05, 0.05),
(TK::Pedagogy, TL::Highest) => (0.1, 0.1),
(TK::Precision, TL::Lowest) => (0.0, -0.05),
(TK::Precision, TL::Low) => (0.0, -0.02),
(TK::Precision, TL::High) => (-0.02, 0.05),
(TK::Precision, TL::Highest) => (-0.05, 0.1),
(_, TL::Balanced) => (0.0, 0.0),
};
TraitMoodModifier {
valence: v,
arousal: a,
}
}
#[cfg(feature = "traits")]
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
#[must_use]
pub fn derive_mood_baseline(profile: &crate::traits::PersonalityProfile) -> MoodVector {
use crate::traits::TraitKind;
let mut total_v = 0.0f32;
let mut total_a = 0.0f32;
for &kind in TraitKind::ALL {
let level = profile.get_trait(kind);
let m = trait_mood_modifier(kind, level);
total_v += m.valence;
total_a += m.arousal;
}
let count = TraitKind::COUNT as f32;
let base_v = (total_v / count).clamp(-1.0, 1.0);
let base_a = (total_a / count).clamp(-1.0, 1.0);
let compound = compute_compound_effects(profile);
let final_v = (base_v + compound.0).clamp(-1.0, 1.0);
let final_a = (base_a + compound.1).clamp(-1.0, 1.0);
let mut baseline = MoodVector::neutral();
baseline.joy = final_v;
baseline.arousal = final_a;
baseline
}
#[cfg(feature = "traits")]
fn compute_compound_effects(profile: &crate::traits::PersonalityProfile) -> (f32, f32) {
use crate::traits::{TraitKind as TK, TraitLevel as TL};
let mut v = 0.0f32;
let mut a = 0.0f32;
let get = |k: TK| profile.get_trait(k);
let high_or_above = |l: TL| l >= TL::High;
let low_or_below = |l: TL| l <= TL::Low;
if high_or_above(get(TK::Warmth)) && high_or_above(get(TK::Humor)) {
v += 0.1;
a += 0.1;
}
if high_or_above(get(TK::Warmth)) && high_or_above(get(TK::Empathy)) {
v += 0.1;
a -= 0.05;
}
if high_or_above(get(TK::Patience)) && high_or_above(get(TK::Pedagogy)) {
v += 0.1;
a -= 0.1;
}
if high_or_above(get(TK::Confidence)) && high_or_above(get(TK::Autonomy)) {
v += 0.05;
a += 0.15;
}
if high_or_above(get(TK::Skepticism)) && low_or_below(get(TK::Warmth)) {
v -= 0.1;
a += 0.05;
}
if low_or_below(get(TK::Confidence)) && low_or_below(get(TK::RiskTolerance)) {
v -= 0.15;
a += 0.1;
}
if high_or_above(get(TK::Curiosity)) && high_or_above(get(TK::Precision)) {
v += 0.05;
a += 0.05;
}
(v, a)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdaptiveBaseline {
pub core: MoodVector,
pub adapted: MoodVector,
pub adaptation_rate: f32,
pub recovery_rate: f32,
}
impl AdaptiveBaseline {
#[must_use]
pub fn new(core: MoodVector) -> Self {
Self {
adapted: core.clone(),
core,
adaptation_rate: 0.01,
recovery_rate: 0.05,
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
pub fn adapt(&mut self, recent_mood: &MoodVector) {
self.adapted = self.adapted.blend(recent_mood, self.adaptation_rate);
self.adapted = self.adapted.blend(&self.core, self.recovery_rate);
}
#[must_use]
pub fn current(&self) -> &MoodVector {
&self.adapted
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
#[must_use]
pub fn drift(&self) -> f32 {
let dj = self.adapted.joy - self.core.joy;
let da = self.adapted.arousal - self.core.arousal;
let dd = self.adapted.dominance - self.core.dominance;
let dt = self.adapted.trust - self.core.trust;
let di = self.adapted.interest - self.core.interest;
let df = self.adapted.frustration - self.core.frustration;
(dj * dj + da * da + dd * dd + dt * dt + di * di + df * df).sqrt()
}
}