""" Flare – Session Management ~~~~~~~~~~~~~~~~~~~~~~~~~~ • thread-safe SessionStore • state-machine alanları """ from __future__ import annotations import threading, uuid, time from datetime import datetime, timedelta from typing import Dict, List from utils import log class Session: """Single chat session.""" def __init__(self, project_name: str, version_config=None): self.session_id: str = str(uuid.uuid4()) self.project_name: str = project_name self.version_number: int = version_config.id if version_config else None self.version_config = version_config # Version config'i sakla # flow state self.state: str = "idle" # idle | await_param | call_api | humanize self.last_intent: str | None = None self.awaiting_parameters: List[str] = [] self.missing_ask_count: int = 0 # data self.variables: Dict[str, str] = {} self.auth_tokens: Dict[str, Dict] = {} # api_name -> {token, expiry} # history self.chat_history: List[Dict[str, str]] = [] # smart parameter collection tracking self.asked_parameters: Dict[str, int] = {} # parametre_adı -> kaç_kez_soruldu self.unanswered_parameters: List[str] = [] # sorulduğu halde cevaplanmayan parametreler self.parameter_ask_rounds: int = 0 # toplam kaç tur soru soruldu self.created_at: datetime = datetime.now() self.last_activity: datetime = datetime.now() def get_version_config(self): """Get stored version config""" return self.version_config def update_activity(self): """Update last activity timestamp""" self.last_activity = datetime.now() # -------- helper ---------- def add_turn(self, role: str, content: str): self.chat_history.append({"role": role, "content": content}) self.update_activity() # Activity güncellemesi eklendi if len(self.chat_history) > 20: self.chat_history.pop(0) # -------- reset flow ------ def reset_flow(self): self.state = "idle" self.last_intent = None self.awaiting_parameters.clear() self.missing_ask_count = 0 # Smart collection tracking'i reset etme, # çünkü aynı session'da başka intent için kullanılabilir # -------- smart parameter collection methods ------ def record_parameter_question(self, param_names: List[str]): """Sorulan parametreleri kaydet""" self.parameter_ask_rounds += 1 log(f"📝 Recording parameter question round {self.parameter_ask_rounds} for: {param_names}") for param in param_names: self.asked_parameters[param] = self.asked_parameters.get(param, 0) + 1 log(f" - {param} asked {self.asked_parameters[param]} time(s)") def mark_parameters_unanswered(self, param_names: List[str]): """Cevaplanmayan parametreleri işaretle""" for param in param_names: if param not in self.unanswered_parameters: self.unanswered_parameters.append(param) log(f"❓ Parameter marked as unanswered: {param}") def mark_parameter_answered(self, param_name: str): """Parametre cevaplandığında işareti kaldır""" if param_name in self.unanswered_parameters: self.unanswered_parameters.remove(param_name) log(f"✅ Parameter marked as answered: {param_name}") def get_parameter_ask_count(self, param_name: str) -> int: """Bir parametrenin kaç kez sorulduğunu döndür""" return self.asked_parameters.get(param_name, 0) def reset_parameter_tracking(self): """Parametre takibini sıfırla (yeni intent için)""" self.asked_parameters.clear() self.unanswered_parameters.clear() self.parameter_ask_rounds = 0 log("🔄 Parameter tracking reset for new intent") class SessionStore: """Thread-safe global store.""" def __init__(self): self._lock = threading.Lock() self._sessions: Dict[str, Session] = {} def create_session(self, project_name: str, version_config=None) -> Session: with self._lock: s = Session(project_name, version_config) self._sessions[s.session_id] = s log(f"🆕 Session created {s.session_id} with project {project_name} version {s.version_number}") return s def get_session(self, sid: str) -> Session | None: with self._lock: return self._sessions.get(sid) def __contains__(self, sid: str) -> bool: return self.get_session(sid) is not None def cleanup_expired_sessions(self, timeout_minutes: int = 30): """Remove expired sessions""" with self._lock: now = datetime.now() expired = [] for sid, session in self._sessions.items(): if (now - session.last_activity).total_seconds() > timeout_minutes * 60: expired.append(sid) for sid in expired: del self._sessions[sid] log(f"🗑️ Expired session removed: {sid[:8]}...") if expired: log(f"📊 Active sessions: {len(self._sessions)}") session_store = SessionStore() # Cleanup thread başlat def start_session_cleanup(): def cleanup_loop(): while True: try: session_store.cleanup_expired_sessions() except Exception as e: log(f"❌ Session cleanup error: {e}") time.sleep(300) # 5 dakikada bir thread = threading.Thread(target=cleanup_loop, daemon=True) thread.start() log("🧹 Session cleanup thread started")