ciyidogan commited on
Commit
9e188ed
·
verified ·
1 Parent(s): 088fb5d

Delete session.py

Browse files
Files changed (1) hide show
  1. session.py +0 -311
session.py DELETED
@@ -1,311 +0,0 @@
1
- """
2
- Optimized Session Management for Flare Platform
3
- """
4
- from dataclasses import dataclass, field
5
- from typing import Dict, List, Optional, Any
6
- from datetime import datetime
7
- import json
8
- import secrets
9
- import hashlib
10
- import time
11
-
12
- from config.config_models import VersionConfig, IntentConfig
13
- from utils.logger import log_debug, log_info
14
-
15
- @dataclass
16
- class Session:
17
- """Optimized session for future Redis storage"""
18
-
19
- MAX_CHAT_HISTORY: int = field(default=20, init=False, repr=False)
20
-
21
- session_id: str
22
- project_name: str
23
- version_no: int
24
- is_realtime: Optional[bool] = False
25
- locale: Optional[str] = "tr"
26
-
27
- # State management - string for better debugging
28
- state: str = "idle" # idle | collect_params | call_api | humanize
29
-
30
- # Minimal stored data
31
- current_intent: Optional[str] = None
32
- variables: Dict[str, str] = field(default_factory=dict)
33
- project_id: Optional[int] = None
34
- version_id: Optional[int] = None
35
-
36
- # Chat history - limited to recent messages
37
- chat_history: List[Dict[str, str]] = field(default_factory=list)
38
-
39
- # Metadata
40
- created_at: str = field(default_factory=lambda: datetime.utcnow().isoformat())
41
- last_activity: str = field(default_factory=lambda: datetime.utcnow().isoformat())
42
-
43
- # Parameter collection state
44
- awaiting_parameters: List[str] = field(default_factory=list)
45
- asked_parameters: Dict[str, int] = field(default_factory=dict)
46
- unanswered_parameters: List[str] = field(default_factory=list)
47
- parameter_ask_rounds: int = 0
48
-
49
- # Transient data (not serialized to Redis)
50
- _version_config: Optional[VersionConfig] = field(default=None, init=False, repr=False)
51
- _intent_config: Optional[IntentConfig] = field(default=None, init=False, repr=False)
52
- _auth_tokens: Dict[str, Dict] = field(default_factory=dict, init=False, repr=False)
53
-
54
- def add_message(self, role: str, content: str) -> None:
55
- """Add message to chat history with size limit"""
56
- message = {
57
- "role": role,
58
- "content": content,
59
- "timestamp": datetime.utcnow().isoformat()
60
- }
61
-
62
- self.chat_history.append(message)
63
-
64
- # Keep only recent messages
65
- if len(self.chat_history) > self.MAX_CHAT_HISTORY:
66
- self.chat_history = self.chat_history[-self.MAX_CHAT_HISTORY:]
67
-
68
- # Update activity
69
- self.last_activity = datetime.utcnow().isoformat()
70
-
71
- log_debug(
72
- f"Message added to session",
73
- session_id=self.session_id,
74
- role=role,
75
- history_size=len(self.chat_history)
76
- )
77
-
78
- def add_turn(self, role: str, content: str) -> None:
79
- """Alias for add_message for compatibility"""
80
- self.add_message(role, content)
81
-
82
- def set_version_config(self, config: VersionConfig) -> None:
83
- """Set transient version config"""
84
- self._version_config = config
85
-
86
- def get_version_config(self) -> Optional[VersionConfig]:
87
- """Get transient version config"""
88
- return self._version_config
89
-
90
- def set_intent_config(self, config: IntentConfig) -> None:
91
- """Set current intent config"""
92
- self._intent_config = config
93
- self.current_intent = config.name if config else None
94
-
95
- def get_intent_config(self) -> Optional[IntentConfig]:
96
- """Get current intent config"""
97
- return self._intent_config
98
-
99
- def reset_flow(self) -> None:
100
- """Reset conversation flow to idle"""
101
- self.state = "idle"
102
- self.current_intent = None
103
- self._intent_config = None
104
- self.awaiting_parameters = []
105
- self.asked_parameters = {}
106
- self.unanswered_parameters = []
107
- self.parameter_ask_rounds = 0
108
-
109
- log_debug(
110
- f"Session flow reset",
111
- session_id=self.session_id
112
- )
113
-
114
- def to_redis(self) -> str:
115
- """Serialize for Redis storage"""
116
- data = {
117
- 'session_id': self.session_id,
118
- 'project_name': self.project_name,
119
- 'version_no': self.version_no,
120
- 'state': self.state,
121
- 'current_intent': self.current_intent,
122
- 'variables': self.variables,
123
- 'project_id': self.project_id,
124
- 'version_id': self.version_id,
125
- 'chat_history': self.chat_history[-self.MAX_CHAT_HISTORY:],
126
- 'created_at': self.created_at,
127
- 'last_activity': self.last_activity,
128
- 'awaiting_parameters': self.awaiting_parameters,
129
- 'asked_parameters': self.asked_parameters,
130
- 'unanswered_parameters': self.unanswered_parameters,
131
- 'parameter_ask_rounds': self.parameter_ask_rounds,
132
- 'is_realtime': self.is_realtime
133
- }
134
- return json.dumps(data, ensure_ascii=False)
135
-
136
- @classmethod
137
- def from_redis(cls, data: str) -> 'Session':
138
- """Deserialize from Redis"""
139
- obj = json.loads(data)
140
- return cls(**obj)
141
-
142
- def get_state_info(self) -> dict:
143
- """Get debug info about current state"""
144
- return {
145
- 'state': self.state,
146
- 'intent': self.current_intent,
147
- 'variables': list(self.variables.keys()),
148
- 'history_length': len(self.chat_history),
149
- 'awaiting_params': self.awaiting_parameters,
150
- 'last_activity': self.last_activity
151
- }
152
-
153
- def get_auth_token(self, api_name: str) -> Optional[Dict]:
154
- """Get cached auth token for API"""
155
- return self._auth_tokens.get(api_name)
156
-
157
- def set_auth_token(self, api_name: str, token_data: Dict) -> None:
158
- """Cache auth token for API"""
159
- self._auth_tokens[api_name] = token_data
160
-
161
- def is_expired(self, timeout_minutes: int = 30) -> bool:
162
- """Check if session is expired"""
163
- last_activity_time = datetime.fromisoformat(self.last_activity.replace('Z', '+00:00'))
164
- current_time = datetime.utcnow()
165
- elapsed_minutes = (current_time - last_activity_time).total_seconds() / 60
166
- return elapsed_minutes > timeout_minutes
167
-
168
-
169
- def generate_secure_session_id() -> str:
170
- """Generate cryptographically secure session ID"""
171
- # Use secrets for secure random generation
172
- random_bytes = secrets.token_bytes(32)
173
-
174
- # Add timestamp for uniqueness
175
- timestamp = str(int(time.time() * 1000000))
176
-
177
- # Combine and hash
178
- combined = random_bytes + timestamp.encode()
179
- session_id = hashlib.sha256(combined).hexdigest()
180
-
181
- return f"session_{session_id[:32]}"
182
-
183
- class SessionStore:
184
- """In-memory session store (to be replaced with Redis)"""
185
-
186
- def __init__(self):
187
- self._sessions: Dict[str, Session] = {}
188
- self._lock = threading.Lock()
189
-
190
- def create_session(
191
- self,
192
- project_name: str,
193
- version_no: int,
194
- is_realtime: bool = False,
195
- locale: str = "tr"
196
- ) -> Session:
197
- """Create new session"""
198
- session_id = generate_secure_session_id()
199
-
200
- session = Session(
201
- session_id=session_id,
202
- project_name=project_name,
203
- version_no=version_no,
204
- is_realtime=is_realtime,
205
- locale=locale
206
- )
207
-
208
- with self._lock:
209
- self._sessions[session_id] = session
210
-
211
- log_info(
212
- "Session created",
213
- session_id=session_id,
214
- project=project_name,
215
- version=version_no,
216
- is_realtime=is_realtime,
217
- locale=locale
218
- )
219
-
220
- return session
221
-
222
- def get_session(self, session_id: str) -> Optional[Session]:
223
- """Get session by ID"""
224
- with self._lock:
225
- session = self._sessions.get(session_id)
226
-
227
- if session and session.is_expired():
228
- log_info(f"Session expired", session_id=session_id)
229
- self.delete_session(session_id)
230
- return None
231
-
232
- return session
233
-
234
- def update_session(self, session: Session) -> None:
235
- """Update session in store"""
236
- session.last_activity = datetime.utcnow().isoformat()
237
-
238
- with self._lock:
239
- self._sessions[session.session_id] = session
240
-
241
- def delete_session(self, session_id: str) -> None:
242
- """Delete session"""
243
- with self._lock:
244
- if session_id in self._sessions:
245
- del self._sessions[session_id]
246
- log_info(f"Session deleted", session_id=session_id)
247
-
248
- def cleanup_expired_sessions(self, timeout_minutes: int = 30) -> int:
249
- """Clean up expired sessions"""
250
- expired_count = 0
251
-
252
- with self._lock:
253
- expired_ids = [
254
- sid for sid, session in self._sessions.items()
255
- if session.is_expired(timeout_minutes)
256
- ]
257
-
258
- for session_id in expired_ids:
259
- del self._sessions[session_id]
260
- expired_count += 1
261
-
262
- if expired_count > 0:
263
- log_info(
264
- f"Cleaned up expired sessions",
265
- count=expired_count
266
- )
267
-
268
- return expired_count
269
-
270
- def get_active_session_count(self) -> int:
271
- """Get count of active sessions"""
272
- with self._lock:
273
- return len(self._sessions)
274
-
275
- def get_session_stats(self) -> Dict[str, Any]:
276
- """Get session statistics"""
277
- with self._lock:
278
- realtime_count = sum(
279
- 1 for s in self._sessions.values()
280
- if s.is_realtime
281
- )
282
-
283
- return {
284
- 'total_sessions': len(self._sessions),
285
- 'realtime_sessions': realtime_count,
286
- 'regular_sessions': len(self._sessions) - realtime_count
287
- }
288
-
289
-
290
- # Global session store instance
291
- import threading
292
- session_store = SessionStore()
293
-
294
- # Session cleanup task
295
- def start_session_cleanup(interval_minutes: int = 5, timeout_minutes: int = 30):
296
- """Start background task to clean up expired sessions"""
297
- import asyncio
298
-
299
- async def cleanup_task():
300
- while True:
301
- try:
302
- expired = session_store.cleanup_expired_sessions(timeout_minutes)
303
- if expired > 0:
304
- log_info(f"Session cleanup completed", expired=expired)
305
- except Exception as e:
306
- log_error(f"Session cleanup error", error=str(e))
307
-
308
- await asyncio.sleep(interval_minutes * 60)
309
-
310
- # Run in background
311
- asyncio.create_task(cleanup_task())