Girish Jeswani commited on
Commit
6071dfa
·
1 Parent(s): 6e93148

add better session management

Browse files
multi_llm_chatbot_backend/app/core/session_manager.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Optional, Any
2
+ from datetime import datetime, timedelta
3
+ import uuid
4
+ from dataclasses import dataclass, field
5
+ import asyncio
6
+ from threading import Lock
7
+
8
+ @dataclass
9
+ class ConversationContext:
10
+ """Individual conversation context for a session"""
11
+ session_id: str
12
+ messages: List[dict] = field(default_factory=list)
13
+ uploaded_files: List[str] = field(default_factory=list)
14
+ total_upload_size: int = 0
15
+ metadata: Dict[str, Any] = field(default_factory=dict)
16
+ created_at: datetime = field(default_factory=datetime.now)
17
+ last_accessed: datetime = field(default_factory=datetime.now)
18
+
19
+ def append_message(self, role: str, content: str):
20
+ """Add a message to the conversation"""
21
+ self.messages.append({
22
+ "role": role,
23
+ "content": content,
24
+ "timestamp": datetime.now().isoformat()
25
+ })
26
+ self.last_accessed = datetime.now()
27
+
28
+ def get_recent_messages(self, limit: int = 20) -> List[dict]:
29
+ """Get recent messages with limit"""
30
+ return self.messages[-limit:] if len(self.messages) > limit else self.messages
31
+
32
+ def get_messages_by_role(self, role: str) -> List[dict]:
33
+ """Get all messages from a specific role"""
34
+ return [msg for msg in self.messages if msg['role'] == role]
35
+
36
+ def clear_messages(self):
37
+ """Clear all messages but preserve metadata"""
38
+ self.messages = []
39
+ self.last_accessed = datetime.now()
40
+
41
+ def add_uploaded_file(self, filename: str, content: str, file_size: int):
42
+ """Add uploaded file content to context"""
43
+ self.uploaded_files.append(filename)
44
+ self.total_upload_size += file_size
45
+ self.append_message("document", f"[Uploaded: {filename}]\n{content}")
46
+
47
+ def get_context_size(self) -> int:
48
+ """Calculate total context size in characters"""
49
+ return sum(len(msg['content']) for msg in self.messages)
50
+
51
+ class SessionManager:
52
+ """Thread-safe session manager for handling multiple user conversations"""
53
+
54
+ def __init__(self, session_timeout_hours: int = 24, cleanup_interval_minutes: int = 60):
55
+ self.sessions: Dict[str, ConversationContext] = {}
56
+ self.session_timeout = timedelta(hours=session_timeout_hours)
57
+ self.cleanup_interval = timedelta(minutes=cleanup_interval_minutes)
58
+ self.lock = Lock()
59
+ self.last_cleanup = datetime.now()
60
+
61
+ def create_session(self) -> str:
62
+ """Create a new session and return session ID"""
63
+ session_id = str(uuid.uuid4())
64
+ with self.lock:
65
+ self.sessions[session_id] = ConversationContext(session_id=session_id)
66
+ return session_id
67
+
68
+ def get_session(self, session_id: Optional[str] = None) -> ConversationContext:
69
+ """Get existing session or create new one"""
70
+ if not session_id:
71
+ session_id = self.create_session()
72
+
73
+ with self.lock:
74
+ if session_id not in self.sessions:
75
+ self.sessions[session_id] = ConversationContext(session_id=session_id)
76
+
77
+ session = self.sessions[session_id]
78
+ session.last_accessed = datetime.now()
79
+
80
+ # Trigger cleanup if needed
81
+ self._cleanup_expired_sessions()
82
+
83
+ return session
84
+
85
+ def delete_session(self, session_id: str) -> bool:
86
+ """Delete a specific session"""
87
+ with self.lock:
88
+ if session_id in self.sessions:
89
+ del self.sessions[session_id]
90
+ return True
91
+ return False
92
+
93
+ def get_active_session_count(self) -> int:
94
+ """Get number of active sessions"""
95
+ with self.lock:
96
+ return len(self.sessions)
97
+
98
+ def _cleanup_expired_sessions(self):
99
+ """Remove expired sessions (called periodically)"""
100
+ now = datetime.now()
101
+
102
+ # Only run cleanup periodically to avoid overhead
103
+ if now - self.last_cleanup < self.cleanup_interval:
104
+ return
105
+
106
+ expired_sessions = []
107
+ for session_id, session in self.sessions.items():
108
+ if now - session.last_accessed > self.session_timeout:
109
+ expired_sessions.append(session_id)
110
+
111
+ for session_id in expired_sessions:
112
+ del self.sessions[session_id]
113
+
114
+ self.last_cleanup = now
115
+
116
+ if expired_sessions:
117
+ print(f"Cleaned up {len(expired_sessions)} expired sessions")
118
+
119
+ # Global session manager instance
120
+ session_manager = SessionManager()
121
+
122
+ def get_session_manager() -> SessionManager:
123
+ """Get the global session manager instance"""
124
+ return session_manager