1use crate::error::Result;
4use crate::messages::{Message, MessageRole};
5use crate::session_manager::{SessionData, SessionMetrics, SessionState};
6use chrono::{DateTime, Duration, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::sync::Arc;
10
11pub struct ChatSession {
13 pub id: String,
15 pub messages: Vec<Message>,
17 pub created_at: DateTime<Utc>,
19 pub last_activity: DateTime<Utc>,
21 pub metadata: HashMap<String, serde_json::Value>,
23 pub state: SessionState,
25 pub store: Arc<dyn oxirs_core::Store>,
27 pub metrics: SessionMetrics,
29}
30
31impl ChatSession {
32 pub fn new(id: String, store: Arc<dyn oxirs_core::Store>) -> Self {
34 Self {
35 id,
36 messages: Vec::new(),
37 created_at: Utc::now(),
38 last_activity: Utc::now(),
39 metadata: HashMap::new(),
40 state: SessionState::Active,
41 store,
42 metrics: SessionMetrics::default(),
43 }
44 }
45
46 pub fn add_message(&mut self, message: Message) -> Result<()> {
48 self.metrics.total_messages += 1;
50 match message.role {
51 MessageRole::User => self.metrics.user_messages += 1,
52 MessageRole::Assistant => self.metrics.assistant_messages += 1,
53 _ => {}
54 }
55
56 if let Some(tokens) = message.token_count {
57 self.metrics.total_tokens_used += tokens;
58 }
59
60 self.messages.push(message);
61 self.last_activity = Utc::now();
62 self.metrics.last_updated = Utc::now();
63 Ok(())
64 }
65
66 pub fn get_messages(&self) -> &[Message] {
68 &self.messages
69 }
70
71 pub fn get_recent_messages(&self, count: usize) -> &[Message] {
73 let start = self.messages.len().saturating_sub(count);
74 &self.messages[start..]
75 }
76
77 pub fn get_statistics(&self) -> SessionStatistics {
79 let duration = (Utc::now() - self.created_at).num_seconds() as u64;
80
81 SessionStatistics {
82 session_id: self.id.clone(),
83 total_messages: self.messages.len(),
84 user_messages: self.metrics.user_messages,
85 assistant_messages: self.metrics.assistant_messages,
86 total_tokens: self.metrics.total_tokens_used,
87 avg_response_time_ms: self.metrics.average_response_time * 1000.0, session_duration_seconds: duration,
89 rag_retrievals: self.metrics.successful_queries, sparql_queries: self.metrics.successful_queries,
91 error_count: self.metrics.error_count,
92 created_at: self.created_at,
93 last_activity: self.last_activity,
94 }
95 }
96
97 pub fn should_expire(&self, timeout: Duration) -> bool {
99 Utc::now() - self.last_activity > timeout
100 }
101
102 pub fn export_data(&self) -> SessionData {
104 use crate::session_manager::ChatConfig;
105 use std::collections::HashSet;
106
107 SessionData {
108 id: self.id.clone(),
109 config: ChatConfig::default(),
110 created_at: self.created_at,
111 last_activity: self.last_activity,
112 messages: self.messages.clone(),
113 user_preferences: self
114 .metadata
115 .iter()
116 .map(|(k, v)| (k.clone(), v.to_string()))
117 .collect(),
118 session_state: self.state.clone(),
119 context_summary: None,
120 pinned_messages: HashSet::new(),
121 current_topics: Vec::new(),
122 topic_history: Vec::new(),
123 performance_metrics: self.metrics.clone(),
124 }
125 }
126
127 pub fn to_data(&self) -> SessionData {
129 self.export_data()
130 }
131
132 pub fn from_data(data: SessionData, store: Arc<dyn oxirs_core::Store>) -> Self {
134 let metadata: HashMap<String, serde_json::Value> = data
135 .user_preferences
136 .iter()
137 .map(|(k, v)| (k.clone(), serde_json::Value::String(v.clone())))
138 .collect();
139
140 Self {
141 id: data.id,
142 messages: data.messages,
143 created_at: data.created_at,
144 last_activity: data.last_activity,
145 metadata,
146 state: data.session_state,
147 store,
148 metrics: data.performance_metrics,
149 }
150 }
151
152 pub fn record_rag_retrieval(&mut self) {
154 self.metrics.successful_queries += 1;
155 self.metrics.last_updated = Utc::now();
156 }
157
158 pub fn record_sparql_query(&mut self) {
159 self.metrics.successful_queries += 1;
160 self.metrics.last_updated = Utc::now();
161 }
162
163 pub fn record_error(&mut self) {
164 self.metrics.error_count += 1;
165 self.metrics.failed_queries += 1;
166 self.metrics.last_updated = Utc::now();
167 }
168
169 pub fn record_response_time(&mut self, response_time_ms: u64) {
170 self.metrics.update_response_time(response_time_ms);
171 self.metrics.last_updated = Utc::now();
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct SessionStatistics {
178 pub session_id: String,
179 pub total_messages: usize,
180 pub user_messages: usize,
181 pub assistant_messages: usize,
182 pub total_tokens: usize,
183 pub avg_response_time_ms: f64,
184 pub session_duration_seconds: u64,
185 pub rag_retrievals: usize,
186 pub sparql_queries: usize,
187 pub error_count: usize,
188 pub created_at: DateTime<Utc>,
189 pub last_activity: DateTime<Utc>,
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_session_creation() {
198 let store = Arc::new(oxirs_core::ConcreteStore::new().unwrap());
199 let session = ChatSession::new("test-session".to_string(), store);
200 assert_eq!(session.id, "test-session");
201 assert_eq!(session.messages.len(), 0);
202 assert_eq!(session.state, SessionState::Active);
203 }
204}