use crate::clock::now as clock_now;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InteractionRecord {
pub timestamp: DateTime<Utc>,
pub method: String,
pub path: String,
#[serde(default)]
pub query_params: HashMap<String, String>,
#[serde(default)]
pub headers: HashMap<String, String>,
pub request: Option<serde_json::Value>,
pub status: u16,
pub response: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding: Option<Vec<f32>>,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
impl InteractionRecord {
pub fn new(
method: impl Into<String>,
path: impl Into<String>,
request: Option<serde_json::Value>,
status: u16,
response: Option<serde_json::Value>,
) -> Self {
Self {
timestamp: Utc::now(),
method: method.into(),
path: path.into(),
query_params: HashMap::new(),
headers: HashMap::new(),
request,
status,
response,
embedding: None,
metadata: HashMap::new(),
}
}
#[must_use]
pub fn with_query_params(mut self, params: HashMap<String, String>) -> Self {
self.query_params = params;
self
}
#[must_use]
pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
self.headers = headers;
self
}
#[must_use]
pub fn with_embedding(mut self, embedding: Vec<f32>) -> Self {
self.embedding = Some(embedding);
self
}
#[must_use]
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn summary(&self) -> String {
let request_body = self
.request
.as_ref()
.map(|r| serde_json::to_string(r).unwrap_or_default())
.unwrap_or_default();
let response_body = self
.response
.as_ref()
.map(|r| serde_json::to_string(r).unwrap_or_default())
.unwrap_or_default();
format!(
"{} {} | Request: {} | Status: {} | Response: {}",
self.method, self.path, request_body, self.status, response_body
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionState {
pub session_id: String,
pub state: HashMap<String, serde_json::Value>,
pub history: Vec<InteractionRecord>,
pub created_at: DateTime<Utc>,
pub last_activity: DateTime<Utc>,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
impl SessionState {
pub fn new(session_id: impl Into<String>) -> Self {
let now = clock_now();
Self {
session_id: session_id.into(),
state: HashMap::new(),
history: Vec::new(),
created_at: now,
last_activity: now,
metadata: HashMap::new(),
}
}
pub fn touch(&mut self) {
self.last_activity = clock_now();
}
pub fn record_interaction(&mut self, interaction: InteractionRecord) {
self.history.push(interaction);
self.touch();
}
pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
self.state.get(key)
}
pub fn set(&mut self, key: impl Into<String>, value: serde_json::Value) {
self.state.insert(key.into(), value);
self.touch();
}
pub fn remove(&mut self, key: &str) -> Option<serde_json::Value> {
let result = self.state.remove(key);
self.touch();
result
}
pub fn is_inactive(&self, duration: chrono::Duration) -> bool {
clock_now().signed_duration_since(self.last_activity) > duration
}
}