rdune71 commited on
Commit
8da6264
Β·
1 Parent(s): e7063a6

Fix session state initialization errors and enhance error handling

Browse files
Files changed (3) hide show
  1. app.py +44 -27
  2. src/analytics/session_analytics.py +28 -8
  3. src/ui/chat_handler.py +116 -64
app.py CHANGED
@@ -16,8 +16,6 @@ from core.errors import translate_error
16
  from core.personality import personality
17
  from src.analytics.user_logger import user_logger
18
  from src.analytics.session_analytics import session_analytics
19
- from src.llm.factory import llm_factory
20
- from src.services.hf_monitor import hf_monitor
21
  import logging
22
 
23
  # Set up logging
@@ -26,25 +24,49 @@ logger = logging.getLogger(__name__)
26
 
27
  st.set_page_config(page_title="CosmicCat AI Assistant", page_icon="🐱", layout="wide")
28
 
29
- # Initialize session state properly
30
- if "messages" not in st.session_state:
31
- st.session_state.messages = []
32
- if "is_processing" not in st.session_state:
33
- st.session_state.is_processing = False
34
- if "ngrok_url_temp" not in st.session_state:
35
- st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
36
- if "cosmic_mode" not in st.session_state:
37
- st.session_state.cosmic_mode = True
38
- if "show_welcome" not in st.session_state:
39
- st.session_state.show_welcome = True
40
- if "last_processed_message" not in st.session_state:
41
- st.session_state.last_processed_message = ""
42
- if "session_id" not in st.session_state:
43
- st.session_state.session_id = f"sess_{int(time.time())}_{abs(hash(str(time.time()))) % 10000}"
44
- if "selected_model_value" not in st.session_state:
45
- st.session_state.selected_model_value = "auto"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- # Start session tracking
48
  try:
49
  session_analytics.start_session_tracking("default_user", st.session_state.session_id)
50
  except Exception as e:
@@ -77,12 +99,8 @@ with st.sidebar:
77
  # Show which provider will actually be used
78
  actual_provider = "Unknown"
79
  if st.session_state.selected_model_value == "auto":
80
- if config.hf_token:
81
- status = hf_monitor.get_endpoint_status()
82
- if status["available"]:
83
- actual_provider = "πŸ€— HF Endpoint"
84
- elif config.ollama_host:
85
- actual_provider = "πŸ¦™ Ollama"
86
  elif config.ollama_host:
87
  actual_provider = "πŸ¦™ Ollama"
88
  else:
@@ -286,7 +304,6 @@ user_input = st.chat_input("Type your message here...", key="chat_input")
286
 
287
  # Process message when received
288
  if user_input and user_input.strip():
289
- # Handle user message display first
290
  if not st.session_state.get('is_processing', False):
291
  chat_handler.process_user_message(user_input, st.session_state.selected_model_value)
292
  else:
 
16
  from core.personality import personality
17
  from src.analytics.user_logger import user_logger
18
  from src.analytics.session_analytics import session_analytics
 
 
19
  import logging
20
 
21
  # Set up logging
 
24
 
25
  st.set_page_config(page_title="CosmicCat AI Assistant", page_icon="🐱", layout="wide")
26
 
27
+ # Initialize session state properly with error handling
28
+ def initialize_session_state():
29
+ """Initialize all session state variables safely"""
30
+ session_vars = {
31
+ "messages": [],
32
+ "is_processing": False,
33
+ "ngrok_url_temp": st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app"),
34
+ "cosmic_mode": True,
35
+ "show_welcome": True,
36
+ "last_processed_message": "",
37
+ "selected_model_value": "auto",
38
+ "session_id": f"sess_{int(time.time())}_{abs(hash(str(time.time()))) % 10000}"
39
+ }
40
+
41
+ for var_name, default_value in session_vars.items():
42
+ if var_name not in st.session_state:
43
+ st.session_state[var_name] = default_value
44
+ logger.info(f"Initialized session variable: {var_name}")
45
+
46
+ # Initialize session state
47
+ try:
48
+ initialize_session_state()
49
+ except Exception as e:
50
+ logger.error(f"Session state initialization failed: {e}")
51
+ # Fallback initialization
52
+ if "messages" not in st.session_state:
53
+ st.session_state.messages = []
54
+ if "is_processing" not in st.session_state:
55
+ st.session_state.is_processing = False
56
+ if "ngrok_url_temp" not in st.session_state:
57
+ st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
58
+ if "cosmic_mode" not in st.session_state:
59
+ st.session_state.cosmic_mode = True
60
+ if "show_welcome" not in st.session_state:
61
+ st.session_state.show_welcome = True
62
+ if "last_processed_message" not in st.session_state:
63
+ st.session_state.last_processed_message = ""
64
+ if "selected_model_value" not in st.session_state:
65
+ st.session_state.selected_model_value = "auto"
66
+ if "session_id" not in st.session_state:
67
+ st.session_state.session_id = f"sess_{int(time.time())}_{abs(hash(str(time.time()))) % 10000}"
68
 
69
+ # Start session tracking with error handling
70
  try:
71
  session_analytics.start_session_tracking("default_user", st.session_state.session_id)
72
  except Exception as e:
 
99
  # Show which provider will actually be used
100
  actual_provider = "Unknown"
101
  if st.session_state.selected_model_value == "auto":
102
+ if config.hf_token and hf_monitor.get_endpoint_status()["available"]:
103
+ actual_provider = "πŸ€— HF Endpoint"
 
 
 
 
104
  elif config.ollama_host:
105
  actual_provider = "πŸ¦™ Ollama"
106
  else:
 
304
 
305
  # Process message when received
306
  if user_input and user_input.strip():
 
307
  if not st.session_state.get('is_processing', False):
308
  chat_handler.process_user_message(user_input, st.session_state.selected_model_value)
309
  else:
src/analytics/session_analytics.py CHANGED
@@ -9,14 +9,18 @@ from src.analytics.user_logger import user_logger
9
  logger = logging.getLogger(__name__)
10
 
11
  class SessionAnalytics:
12
- """Session-level tracking and analytics"""
13
 
14
  def __init__(self):
15
  self.redis_client = redis_client.get_client()
16
 
17
  def start_session_tracking(self, user_id: str, session_id: str):
18
- """Start tracking a user session"""
19
  try:
 
 
 
 
20
  session_data = {
21
  "user_id": user_id,
22
  "session_id": session_id,
@@ -41,8 +45,12 @@ class SessionAnalytics:
41
 
42
  def track_interaction(self, user_id: str, session_id: str, interaction_type: str,
43
  details: Dict[str, Any] = None):
44
- """Track a user interaction within a session"""
45
  try:
 
 
 
 
46
  key = f"analytics:sessions:{session_id}"
47
  session_data_str = self.redis_client.get(key)
48
 
@@ -81,8 +89,12 @@ class SessionAnalytics:
81
  logger.error(f"Failed to track interaction: {e}")
82
 
83
  def end_session_tracking(self, user_id: str, session_id: str):
84
- """End session tracking and generate summary"""
85
  try:
 
 
 
 
86
  key = f"analytics:sessions:{session_id}"
87
  session_data_str = self.redis_client.get(key)
88
 
@@ -112,8 +124,12 @@ class SessionAnalytics:
112
  logger.error(f"Failed to end session tracking: {e}")
113
 
114
  def get_session_summary(self, session_id: str) -> Optional[Dict[str, Any]]:
115
- """Get session summary data"""
116
  try:
 
 
 
 
117
  key = f"analytics:sessions:{session_id}"
118
  session_data_str = self.redis_client.get(key)
119
 
@@ -121,17 +137,21 @@ class SessionAnalytics:
121
  return json.loads(session_data_str)
122
  return None
123
  except Exception as e:
124
- logger.error(f"Failed to get session summary: {e}")
125
  return None
126
 
127
  def get_user_sessions(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
128
- """Get recent sessions for a user"""
129
  try:
 
 
 
 
130
  # This would require a more complex indexing system
131
  # For now, we'll return an empty list as this requires additional implementation
132
  return []
133
  except Exception as e:
134
- logger.error(f"Failed to get user sessions: {e}")
135
  return []
136
 
137
  # Global instance
 
9
  logger = logging.getLogger(__name__)
10
 
11
  class SessionAnalytics:
12
+ """Session-level tracking and analytics with proper error handling"""
13
 
14
  def __init__(self):
15
  self.redis_client = redis_client.get_client()
16
 
17
  def start_session_tracking(self, user_id: str, session_id: str):
18
+ """Start tracking a user session with error handling"""
19
  try:
20
+ if not self.redis_client:
21
+ logger.warning("Redis client not available for session tracking")
22
+ return
23
+
24
  session_data = {
25
  "user_id": user_id,
26
  "session_id": session_id,
 
45
 
46
  def track_interaction(self, user_id: str, session_id: str, interaction_type: str,
47
  details: Dict[str, Any] = None):
48
+ """Track a user interaction within a session with error handling"""
49
  try:
50
+ if not self.redis_client:
51
+ logger.warning("Redis client not available for interaction tracking")
52
+ return
53
+
54
  key = f"analytics:sessions:{session_id}"
55
  session_data_str = self.redis_client.get(key)
56
 
 
89
  logger.error(f"Failed to track interaction: {e}")
90
 
91
  def end_session_tracking(self, user_id: str, session_id: str):
92
+ """End session tracking and generate summary with error handling"""
93
  try:
94
+ if not self.redis_client:
95
+ logger.warning("Redis client not available for session ending")
96
+ return
97
+
98
  key = f"analytics:sessions:{session_id}"
99
  session_data_str = self.redis_client.get(key)
100
 
 
124
  logger.error(f"Failed to end session tracking: {e}")
125
 
126
  def get_session_summary(self, session_id: str) -> Optional[Dict[str, Any]]:
127
+ """Get session summary data with error handling"""
128
  try:
129
+ if not self.redis_client:
130
+ logger.warning("Redis client not available for session summary")
131
+ return None
132
+
133
  key = f"analytics:sessions:{session_id}"
134
  session_data_str = self.redis_client.get(key)
135
 
 
137
  return json.loads(session_data_str)
138
  return None
139
  except Exception as e:
140
+ logger.error(f"Failed to get session summary for user {session_id}: {e}")
141
  return None
142
 
143
  def get_user_sessions(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
144
+ """Get recent sessions for a user with error handling"""
145
  try:
146
+ if not self.redis_client:
147
+ logger.warning("Redis client not available for user sessions")
148
+ return []
149
+
150
  # This would require a more complex indexing system
151
  # For now, we'll return an empty list as this requires additional implementation
152
  return []
153
  except Exception as e:
154
+ logger.error(f"Failed to get user sessions for user {user_id}: {e}")
155
  return []
156
 
157
  # Global instance
src/ui/chat_handler.py CHANGED
@@ -8,25 +8,34 @@ from core.session import session_manager
8
  logger = logging.getLogger(__name__)
9
 
10
  class ChatHandler:
11
- """Handles chat interactions with better timeout handling"""
12
 
13
  def __init__(self):
14
  self.is_processing = False
15
 
16
  def process_user_message(self, user_input: str, selected_model: str):
17
- """Process user message with immediate display"""
18
  if not user_input or not user_input.strip():
19
  st.warning("Please enter a message")
20
  return
21
 
22
- # Prevent duplicate processing
23
- if st.session_state.get('last_processed_message') == user_input:
24
- logger.info("Preventing duplicate message processing")
25
- return
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- st.session_state.is_processing = True
28
- st.session_state.last_processed_message = user_input
29
-
30
  try:
31
  # Show user message immediately
32
  timestamp = time.strftime("%H:%M:%S")
@@ -34,26 +43,33 @@ class ChatHandler:
34
  st.markdown(user_input)
35
  st.caption(f"πŸ•’ {timestamp}")
36
 
37
- # Add to session state history
38
- if "messages" not in st.session_state:
39
- st.session_state.messages = []
40
-
41
- st.session_state.messages.append({
42
- "role": "user",
43
- "content": user_input,
44
- "timestamp": timestamp
45
- })
 
 
 
46
 
47
  # Force UI update
48
  st.experimental_rerun()
49
 
50
  except Exception as e:
51
  logger.error(f"Error in initial message display: {e}", exc_info=True)
52
- st.session_state.is_processing = False
53
- st.session_state.last_processed_message = ""
 
 
 
 
54
 
55
  def process_ai_response(self, user_input: str, selected_model: str):
56
- """Process AI response with enhanced timeout handling"""
57
  if not user_input or not user_input.strip():
58
  return
59
 
@@ -70,7 +86,7 @@ class ChatHandler:
70
 
71
  status_placeholder.info(f"πŸš€ Contacting {provider_name}...")
72
 
73
- # Generate response with timeout handling
74
  response = None
75
  try:
76
  # Get session and conversation history
@@ -79,75 +95,111 @@ class ChatHandler:
79
  conversation_history.append({"role": "user", "content": user_input})
80
 
81
  response = provider.generate(user_input, conversation_history)
82
-
83
  except Exception as e:
84
  logger.error(f"AI response error: {e}")
85
-
86
- # Better error messages for timeout issues
87
- if "timeout" in str(e).lower() or "500" in str(e) or "60" in str(e):
88
- if provider_name == "Ollama":
89
- error_message = ("⏰ Ollama request timed out (60+ seconds).\n\n"
90
- "**Recommendation:** Switch to HF Endpoint which is more reliable.\n"
91
- "Click the 'πŸ€– HF Expert Analysis' button below for deep analysis.")
92
- else:
93
- error_message = ("⏰ HF Endpoint is initializing (may take 2-4 minutes).\n\n"
94
- "Please try again in a moment or use Ollama for faster responses.")
95
- else:
96
- error_message = f"Sorry, I encountered an error: {str(e)[:100]}..."
97
-
98
- status_placeholder.error(f"❌ {provider_name} Error")
99
- response_placeholder.markdown(error_message)
100
- return
101
 
102
  if response and response.strip():
103
  status_placeholder.success("βœ… Response received!")
104
  response_placeholder.markdown(response)
105
 
106
- # Add to session history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  timestamp = time.strftime("%H:%M:%S")
108
  st.session_state.messages.append({
109
  "role": "assistant",
110
- "content": response,
111
- "timestamp": timestamp,
112
- "provider": provider_name.lower().replace(" ", "_")
113
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  else:
115
- status_placeholder.warning("⚠️ Empty response received")
116
- response_placeholder.markdown("*No response generated. Please try again.*")
 
 
117
  timestamp = time.strftime("%H:%M:%S")
118
  st.session_state.messages.append({
119
  "role": "assistant",
120
- "content": "*No response generated. Please try again.*",
121
  "timestamp": timestamp
122
  })
123
-
124
- except ProviderNotAvailableError as e:
125
- status_placeholder.error("❌ No AI providers available")
126
- response_placeholder.markdown("No AI providers are configured. Please check your settings.")
127
- timestamp = time.strftime("%H:%M:%S")
128
- st.session_state.messages.append({
129
- "role": "assistant",
130
- "content": "No AI providers are configured. Please check your settings.",
131
- "timestamp": timestamp
132
- })
133
- logger.error(f"Provider not available: {e}")
134
 
135
  except Exception as e:
136
- logger.error(f"Chat processing failed: {e}", exc_info=True)
137
  st.error("An unexpected error occurred. Please try again.")
138
  finally:
139
- st.session_state.is_processing = False
140
- st.session_state.last_processed_message = ""
 
 
 
 
141
  time.sleep(0.1)
142
 
143
  def _get_provider_display_name(self, provider_name: str) -> str:
144
  """Get display name for provider"""
145
  display_names = {
146
- "ollama": "πŸ¦™ Ollama (Local)",
147
- "huggingface": "πŸ€— HF Endpoint (Primary)",
148
- "hf_endpoint": "πŸ€— HF Endpoint (Primary)"
149
  }
150
- return display_names.get(provider_name.lower(), provider_name)
151
 
152
  # Global instance
153
  chat_handler = ChatHandler()
 
8
  logger = logging.getLogger(__name__)
9
 
10
  class ChatHandler:
11
+ """Handles chat interactions with better UI feedback and session state handling"""
12
 
13
  def __init__(self):
14
  self.is_processing = False
15
 
16
  def process_user_message(self, user_input: str, selected_model: str):
17
+ """Process user message with enhanced UI feedback and proper session state handling"""
18
  if not user_input or not user_input.strip():
19
  st.warning("Please enter a message")
20
  return
21
 
22
+ # Prevent duplicate processing with proper session state access
23
+ try:
24
+ last_processed = st.session_state.get('last_processed_message', '')
25
+ if last_processed == user_input:
26
+ logger.info("Preventing duplicate message processing")
27
+ return
28
+ except Exception as e:
29
+ logger.warning(f"Error accessing session state: {e}")
30
+ # Continue processing even if session state access fails
31
+
32
+ # Set processing flags with error handling
33
+ try:
34
+ st.session_state.is_processing = True
35
+ st.session_state.last_processed_message = user_input
36
+ except Exception as e:
37
+ logger.error(f"Error setting session state: {e}")
38
 
 
 
 
39
  try:
40
  # Show user message immediately
41
  timestamp = time.strftime("%H:%M:%S")
 
43
  st.markdown(user_input)
44
  st.caption(f"πŸ•’ {timestamp}")
45
 
46
+ # Add to session state history with error handling
47
+ try:
48
+ if "messages" not in st.session_state:
49
+ st.session_state.messages = []
50
+
51
+ st.session_state.messages.append({
52
+ "role": "user",
53
+ "content": user_input,
54
+ "timestamp": timestamp
55
+ })
56
+ except Exception as e:
57
+ logger.error(f"Error updating session state messages: {e}")
58
 
59
  # Force UI update
60
  st.experimental_rerun()
61
 
62
  except Exception as e:
63
  logger.error(f"Error in initial message display: {e}", exc_info=True)
64
+ # Clear processing flags on error
65
+ try:
66
+ st.session_state.is_processing = False
67
+ st.session_state.last_processed_message = ""
68
+ except Exception as state_error:
69
+ logger.error(f"Error clearing session state: {state_error}")
70
 
71
  def process_ai_response(self, user_input: str, selected_model: str):
72
+ """Process AI response with enhanced timeout handling and proper session state access"""
73
  if not user_input or not user_input.strip():
74
  return
75
 
 
86
 
87
  status_placeholder.info(f"πŸš€ Contacting {provider_name}...")
88
 
89
+ # Get response
90
  response = None
91
  try:
92
  # Get session and conversation history
 
95
  conversation_history.append({"role": "user", "content": user_input})
96
 
97
  response = provider.generate(user_input, conversation_history)
 
98
  except Exception as e:
99
  logger.error(f"AI response error: {e}")
100
+ raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  if response and response.strip():
103
  status_placeholder.success("βœ… Response received!")
104
  response_placeholder.markdown(response)
105
 
106
+ # Add to session history with error handling
107
+ try:
108
+ timestamp = time.strftime("%H:%M:%S")
109
+ provider_info = "hf_endpoint" if "huggingface" in str(type(provider)).lower() else "ollama"
110
+
111
+ st.session_state.messages.append({
112
+ "role": "assistant",
113
+ "content": response,
114
+ "timestamp": timestamp,
115
+ "provider": provider_info
116
+ })
117
+
118
+ # Update backend session
119
+ conversation = user_session.get("conversation", []).copy()
120
+ conversation.append({"role": "user", "content": user_input})
121
+ conversation.append({"role": "assistant", "content": response})
122
+ session_manager.update_session("default_user", {"conversation": conversation})
123
+
124
+ except Exception as session_error:
125
+ logger.error(f"Error updating session: {session_error}")
126
+ else:
127
+ status_placeholder.warning("⚠️ Empty response received")
128
+ response_placeholder.markdown("*No response generated. Please try again.*")
129
+ try:
130
+ timestamp = time.strftime("%H:%M:%S")
131
+ st.session_state.messages.append({
132
+ "role": "assistant",
133
+ "content": "*No response generated. Please try again.*",
134
+ "timestamp": timestamp
135
+ })
136
+ except Exception as state_error:
137
+ logger.error(f"Error updating session state: {state_error}")
138
+
139
+ except ProviderNotAvailableError as e:
140
+ status_placeholder.error("❌ No AI providers available")
141
+ response_placeholder.markdown("No AI providers are configured. Please check your settings.")
142
+ try:
143
  timestamp = time.strftime("%H:%M:%S")
144
  st.session_state.messages.append({
145
  "role": "assistant",
146
+ "content": "No AI providers are configured. Please check your settings.",
147
+ "timestamp": timestamp
 
148
  })
149
+ except Exception as state_error:
150
+ logger.error(f"Error updating session state: {state_error}")
151
+ logger.error(f"Provider not available: {e}")
152
+
153
+ except Exception as e:
154
+ # Better user-friendly error messages
155
+ status_placeholder.error("❌ Request failed")
156
+
157
+ # More specific error messages
158
+ if "timeout" in str(e).lower() or "500" in str(e):
159
+ error_message = ("⏰ Request timed out. This might be because:\n"
160
+ "β€’ Your Ollama server is not responding\n"
161
+ "β€’ Network connectivity issues\n"
162
+ "β€’ Try using the HF Endpoint instead (🟒 HF Endpoint: Available and ready)")
163
+ elif "connection" in str(e).lower():
164
+ error_message = ("πŸ”Œ Connection failed. This might be because:\n"
165
+ "β€’ Your Ollama server is offline\n"
166
+ "β€’ Incorrect Ollama URL\n"
167
+ "β€’ Network firewall blocking connection\n"
168
+ "β€’ Try using the HF Endpoint instead")
169
  else:
170
+ error_message = f"Sorry, I encountered an error: {str(e)}"
171
+
172
+ response_placeholder.markdown(error_message)
173
+ try:
174
  timestamp = time.strftime("%H:%M:%S")
175
  st.session_state.messages.append({
176
  "role": "assistant",
177
+ "content": error_message,
178
  "timestamp": timestamp
179
  })
180
+ except Exception as state_error:
181
+ logger.error(f"Error updating session state: {state_error}")
182
+ logger.error(f"Chat processing error: {e}", exc_info=True)
 
 
 
 
 
 
 
 
183
 
184
  except Exception as e:
185
+ logger.error(f"Unexpected error in process_ai_response: {e}", exc_info=True)
186
  st.error("An unexpected error occurred. Please try again.")
187
  finally:
188
+ # Clear processing flags with error handling
189
+ try:
190
+ st.session_state.is_processing = False
191
+ st.session_state.last_processed_message = ""
192
+ except Exception as state_error:
193
+ logger.error(f"Error clearing session state: {state_error}")
194
  time.sleep(0.1)
195
 
196
  def _get_provider_display_name(self, provider_name: str) -> str:
197
  """Get display name for provider"""
198
  display_names = {
199
+ "ollama": "πŸ¦™ Ollama",
200
+ "huggingface": "πŸ€— HF Endpoint"
 
201
  }
202
+ return display_names.get(provider_name, provider_name)
203
 
204
  # Global instance
205
  chat_handler = ChatHandler()