use std::collections::HashMap;
use std::time::{Duration, Instant};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use crate::agent::AgentContext;
use crate::oxyde_game::emotion::EmotionalState;
use crate::oxyde_game::intent::Intent;
use crate::Result;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EmotionTrigger {
AnyEmotion { min_intensity: f32 },
SpecificEmotion { emotion: String, min_value: f32 },
ValenceRange { min: f32, max: f32 },
HighArousal { min_arousal: f32 },
Positive,
Negative,
None,
}
impl EmotionTrigger {
pub fn matches(&self, state: &EmotionalState) -> bool {
match self {
EmotionTrigger::AnyEmotion { min_intensity } => {
state.arousal() >= *min_intensity
}
EmotionTrigger::SpecificEmotion { emotion, min_value } => {
let (dominant, value) = state.dominant_emotion();
dominant == emotion && value >= *min_value
}
EmotionTrigger::ValenceRange { min, max } => {
let valence = state.valence();
valence >= *min && valence <= *max
}
EmotionTrigger::HighArousal { min_arousal } => {
state.arousal() >= *min_arousal
}
EmotionTrigger::Positive => state.is_positive(),
EmotionTrigger::Negative => state.is_negative(),
EmotionTrigger::None => true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmotionInfluence {
pub emotion: String,
pub delta: f32,
}
impl EmotionInfluence {
pub fn new(emotion: &str, delta: f32) -> Self {
Self {
emotion: emotion.to_string(),
delta: delta.clamp(-1.0, 1.0),
}
}
}
#[derive(Debug, Clone)]
pub enum BehaviorResult {
Response(String),
Action(String),
None,
}
#[async_trait]
pub trait Behavior: Send + Sync + std::fmt::Debug {
async fn matches_intent(&self, intent: &Intent) -> bool;
async fn execute(&self, intent: &Intent, context: &AgentContext) -> Result<BehaviorResult>;
fn emotion_trigger(&self) -> Option<EmotionTrigger> {
Some(EmotionTrigger::None)
}
fn emotion_influences(&self) -> Vec<EmotionInfluence> {
Vec::new()
}
fn priority(&self) -> u32 {
50 }
fn emotional_priority_modifier(&self, _emotional_state: &EmotionalState) -> i32 {
0
}
}
#[derive(Debug)]
pub struct BaseBehavior {
name: String,
description: String,
priority: u32,
#[allow(dead_code)]
intent_types: Vec<String>,
cooldown_seconds: u64,
last_execution: RwLock<Option<Instant>>,
parameters: HashMap<String, serde_json::Value>,
}
impl BaseBehavior {
pub fn new(
name: &str,
description: &str,
priority: u32,
intent_types: Vec<String>,
cooldown_seconds: u64,
) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
priority,
intent_types,
cooldown_seconds,
last_execution: RwLock::new(None),
parameters: HashMap::new(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn description(&self) -> &str {
&self.description
}
pub fn priority(&self) -> u32 {
self.priority
}
pub async fn is_on_cooldown(&self) -> bool {
let last_execution = self.last_execution.read().await;
if let Some(time) = *last_execution {
let elapsed = time.elapsed();
elapsed < Duration::from_secs(self.cooldown_seconds)
} else {
false
}
}
pub async fn mark_executed(&self) {
let mut last_execution = self.last_execution.write().await;
*last_execution = Some(Instant::now());
}
pub fn set_parameter<T: Serialize>(&mut self, key: &str, value: T) -> Result<()> {
let json_value = serde_json::to_value(value)?;
self.parameters.insert(key.to_string(), json_value);
Ok(())
}
pub fn get_parameter<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<Option<T>> {
if let Some(value) = self.parameters.get(key) {
let typed_value = serde_json::from_value(value.clone())?;
Ok(Some(typed_value))
} else {
Ok(None)
}
}
}