use anyhow::Result;
use serde_json::Value;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::observation::Observation;
use crate::ports::ObservationStore;
use crate::types::ToolId;
pub struct ObservationManager {
session_id: String,
observations: Vec<Observation>,
}
impl ObservationManager {
pub fn persist(&self, _path: &std::path::Path) -> anyhow::Result<()> {
unimplemented!("persist observations to SQLite via SessionStore port")
}
pub fn load_from(_session_id: &str, _path: &std::path::Path) -> anyhow::Result<Self> {
unimplemented!("load observations from SQLite for cross-session analytics")
}
pub fn new() -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let session_id = format!("sess-{timestamp}");
ObservationManager {
session_id,
observations: Vec::new(),
}
}
pub fn session_id(&self) -> &str {
&self.session_id
}
pub fn capture(
&mut self,
tool_name: String,
input: Value,
output: String,
tool_use_id: Option<ToolId>,
) {
let obs = Observation::new(
tool_name,
input,
output,
tool_use_id,
self.session_id.clone(),
);
self.observations.push(obs);
}
pub fn observations(&self) -> &[Observation] {
&self.observations
}
pub fn count(&self) -> usize {
self.observations.len()
}
pub fn save(&self, store: &dyn ObservationStore) -> Result<()> {
store.save(&self.observations)
}
pub fn clear(&mut self) {
self.observations.clear();
}
}
impl Default for ObservationManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_observation_manager_creation() {
let mgr = ObservationManager::new();
assert_eq!(mgr.count(), 0);
assert!(mgr.session_id().starts_with("sess-"));
}
#[test]
fn test_observation_capture() {
let mut mgr = ObservationManager::new();
mgr.capture(
"bash".to_string(),
serde_json::json!({"command": "test"}),
"output".to_string(),
Some(ToolId::new("tool_1")),
);
assert_eq!(mgr.count(), 1);
assert_eq!(mgr.observations()[0].tool_name, "bash");
assert_eq!(
mgr.observations()[0]
.tool_use_id
.as_ref()
.map(|id| id.as_str()),
Some("tool_1")
);
}
#[test]
fn test_multiple_observations() {
let mut mgr = ObservationManager::new();
mgr.capture(
"bash".to_string(),
serde_json::json!({}),
"out1".to_string(),
None,
);
mgr.capture(
"grep".to_string(),
serde_json::json!({}),
"out2".to_string(),
Some(ToolId::new("tool_2")),
);
assert_eq!(mgr.count(), 2);
}
#[test]
fn test_observation_manager_clear() {
let mut mgr = ObservationManager::new();
mgr.capture(
"bash".to_string(),
serde_json::json!({}),
"out".to_string(),
None,
);
assert_eq!(mgr.count(), 1);
mgr.clear();
assert_eq!(mgr.count(), 0);
}
}