mockforge_foundation/intelligent_behavior/
session_state.rs1use crate::clock::now as clock_now;
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct InteractionRecord {
14 pub timestamp: DateTime<Utc>,
16
17 pub method: String,
19
20 pub path: String,
22
23 #[serde(default)]
25 pub query_params: HashMap<String, String>,
26
27 #[serde(default)]
29 pub headers: HashMap<String, String>,
30
31 pub request: Option<serde_json::Value>,
33
34 pub status: u16,
36
37 pub response: Option<serde_json::Value>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub embedding: Option<Vec<f32>>,
43
44 #[serde(default)]
46 pub metadata: HashMap<String, String>,
47}
48
49impl InteractionRecord {
50 pub fn new(
52 method: impl Into<String>,
53 path: impl Into<String>,
54 request: Option<serde_json::Value>,
55 status: u16,
56 response: Option<serde_json::Value>,
57 ) -> Self {
58 Self {
59 timestamp: Utc::now(),
60 method: method.into(),
61 path: path.into(),
62 query_params: HashMap::new(),
63 headers: HashMap::new(),
64 request,
65 status,
66 response,
67 embedding: None,
68 metadata: HashMap::new(),
69 }
70 }
71
72 #[must_use]
74 pub fn with_query_params(mut self, params: HashMap<String, String>) -> Self {
75 self.query_params = params;
76 self
77 }
78
79 #[must_use]
81 pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
82 self.headers = headers;
83 self
84 }
85
86 #[must_use]
88 pub fn with_embedding(mut self, embedding: Vec<f32>) -> Self {
89 self.embedding = Some(embedding);
90 self
91 }
92
93 #[must_use]
95 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
96 self.metadata.insert(key.into(), value.into());
97 self
98 }
99
100 pub fn summary(&self) -> String {
102 let request_body = self
103 .request
104 .as_ref()
105 .map(|r| serde_json::to_string(r).unwrap_or_default())
106 .unwrap_or_default();
107
108 let response_body = self
109 .response
110 .as_ref()
111 .map(|r| serde_json::to_string(r).unwrap_or_default())
112 .unwrap_or_default();
113
114 format!(
115 "{} {} | Request: {} | Status: {} | Response: {}",
116 self.method, self.path, request_body, self.status, response_body
117 )
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct SessionState {
124 pub session_id: String,
126
127 pub state: HashMap<String, serde_json::Value>,
129
130 pub history: Vec<InteractionRecord>,
132
133 pub created_at: DateTime<Utc>,
135
136 pub last_activity: DateTime<Utc>,
138
139 #[serde(default)]
141 pub metadata: HashMap<String, String>,
142}
143
144impl SessionState {
145 pub fn new(session_id: impl Into<String>) -> Self {
150 let now = clock_now();
151 Self {
152 session_id: session_id.into(),
153 state: HashMap::new(),
154 history: Vec::new(),
155 created_at: now,
156 last_activity: now,
157 metadata: HashMap::new(),
158 }
159 }
160
161 pub fn touch(&mut self) {
165 self.last_activity = clock_now();
166 }
167
168 pub fn record_interaction(&mut self, interaction: InteractionRecord) {
170 self.history.push(interaction);
171 self.touch();
172 }
173
174 pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
176 self.state.get(key)
177 }
178
179 pub fn set(&mut self, key: impl Into<String>, value: serde_json::Value) {
181 self.state.insert(key.into(), value);
182 self.touch();
183 }
184
185 pub fn remove(&mut self, key: &str) -> Option<serde_json::Value> {
187 let result = self.state.remove(key);
188 self.touch();
189 result
190 }
191
192 pub fn is_inactive(&self, duration: chrono::Duration) -> bool {
196 clock_now().signed_duration_since(self.last_activity) > duration
197 }
198}