use crate::action::{Action, ActionResult};
use crate::agent::{Agent, AgentId, AgentState, SimpleAgent};
use crate::config::AgentConfig;
use crate::error::Result;
use crate::observation::Observation;
use titans_memory::{MemoryConfig, MemoryEntry, MemoryQuery, TitansMemory};
pub struct MemoryAgent {
inner: SimpleAgent,
memory: TitansMemory,
}
impl MemoryAgent {
pub fn new(name: &str) -> Self {
Self {
inner: SimpleAgent::new(name),
memory: TitansMemory::iot_mode(),
}
}
pub fn with_config(name: &str, agent_config: AgentConfig, memory_config: MemoryConfig) -> Self {
Self {
inner: SimpleAgent::with_config(name, agent_config),
memory: TitansMemory::new(memory_config),
}
}
pub fn memory(&self) -> &TitansMemory {
&self.memory
}
pub fn memory_mut(&mut self) -> &mut TitansMemory {
&mut self.memory
}
pub fn remember_observation(&mut self, obs: &Observation) -> Result<()> {
let entry = MemoryEntry::new("observation", serde_json::to_value(obs).unwrap_or_default())
.with_tags(&["observation", &format!("{:?}", obs.obs_type)]);
self.memory
.remember(entry)
.map_err(|e| crate::error::Error::Memory(e.to_string()))?;
Ok(())
}
pub fn remember_action(&mut self, action: &Action, result: &ActionResult) -> Result<()> {
let entry = MemoryEntry::new(
"action",
serde_json::json!({
"action": action,
"result": result
}),
)
.with_tags(&["action", &format!("{:?}", action.action_type)])
.with_importance(if result.success { 0.6 } else { 0.8 });
self.memory
.remember(entry)
.map_err(|e| crate::error::Error::Memory(e.to_string()))?;
Ok(())
}
pub fn recall_similar(&self, _obs: &Observation, limit: usize) -> Vec<Observation> {
let query = MemoryQuery::tags(&["observation"]).with_limit(limit);
self.memory
.recall(&query)
.unwrap_or_default()
.into_iter()
.filter_map(|result| serde_json::from_value(result.entry.data).ok())
.collect()
}
pub fn recall_past_actions(&self, limit: usize) -> Vec<(Action, ActionResult)> {
let query = MemoryQuery::tags(&["action"]).with_limit(limit);
self.memory
.recall(&query)
.unwrap_or_default()
.into_iter()
.filter_map(|result| {
let data = result.entry.data;
let action: Action = serde_json::from_value(data.get("action")?.clone()).ok()?;
let result: ActionResult =
serde_json::from_value(data.get("result")?.clone()).ok()?;
Some((action, result))
})
.collect()
}
pub fn consolidate(&mut self) -> Result<usize> {
self.memory
.consolidate()
.map_err(|e| crate::error::Error::Memory(e.to_string()))
}
pub fn maintenance(&mut self) -> Result<()> {
self.memory
.decay()
.map_err(|e| crate::error::Error::Memory(e.to_string()))?;
let _ = self.consolidate();
Ok(())
}
pub fn memory_stats(&self) -> titans_memory::MemoryStats {
self.memory.stats()
}
}
impl Agent for MemoryAgent {
fn id(&self) -> &AgentId {
self.inner.id()
}
fn name(&self) -> &str {
self.inner.name()
}
fn state(&self) -> AgentState {
self.inner.state()
}
fn observe(&mut self, observation: Observation) {
let _ = self.remember_observation(&observation);
self.inner.observe(observation);
}
fn decide(&self) -> Action {
self.inner.decide()
}
fn execute(&mut self, action: Action) -> ActionResult {
let result = self.inner.execute(action.clone());
let _ = self.remember_action(&action, &result);
result
}
fn learn(&mut self, observation: &Observation, action: &Action, result: &ActionResult) {
self.inner.learn(observation, action, result);
if self.inner.stats().actions_executed % 10 == 0 {
let _ = self.maintenance();
}
}
fn config(&self) -> &AgentConfig {
self.inner.config()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_agent() {
let agent = MemoryAgent::new("test");
assert_eq!(agent.name(), "test");
assert_eq!(agent.memory_stats().stm_count, 0);
}
#[test]
fn test_remember_observation() {
let mut agent = MemoryAgent::new("test");
let obs = Observation::sensor("temp", 25.0);
agent.remember_observation(&obs).unwrap();
assert_eq!(agent.memory_stats().stm_count, 1);
}
}