File size: 5,841 Bytes
d8516ac
 
 
 
 
525e493
 
d8516ac
e17c344
 
525e493
b69c342
 
 
d8516ac
525e493
271d047
525e493
 
a074611
 
 
d8516ac
 
525e493
 
d8516ac
 
 
 
 
 
 
525e493
 
5b08759
 
 
 
 
0cdaf3a
 
 
4b72ada
 
 
 
0cdaf3a
 
 
 
d8516ac
 
525e493
e17c344
d8516ac
525e493
 
d8516ac
 
 
 
 
 
5b08759
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b69c342
 
d8516ac
b69c342
d8516ac
525e493
d8516ac
525e493
a074611
525e493
a074611
d8516ac
a074611
d8516ac
b69c342
d8516ac
525e493
d8516ac
525e493
d8516ac
 
525e493
0cdaf3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525e493
 
9b44949
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"""
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")