rdune71 commited on
Commit
809cf6d
·
1 Parent(s): f750678

Implemented comprehensive enhancements: removed TTS, fixed session state issues, improved UI/UX, added analytics, input validation, and feedback collection

Browse files
Files changed (3) hide show
  1. app.py +181 -98
  2. requirements.txt +0 -4
  3. services/tts.py +0 -88
app.py CHANGED
@@ -43,10 +43,6 @@ if "ngrok_url_temp" not in st.session_state:
43
  st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
44
  if "hf_expert_requested" not in st.session_state:
45
  st.session_state.hf_expert_requested = False
46
- if "tts_enabled" not in st.session_state:
47
- st.session_state.tts_enabled = False
48
- if "voice_input" not in st.session_state:
49
- st.session_state.voice_input = ""
50
 
51
  # Sidebar layout redesign
52
  with st.sidebar:
@@ -153,22 +149,47 @@ with st.sidebar:
153
  use_container_width=True,
154
  disabled=st.session_state.is_processing):
155
  st.session_state.hf_expert_requested = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  # Main interface
158
  st.title("🧠 AI Life Coach")
159
  st.markdown("Ask me anything about personal development, goal setting, or life advice!")
160
 
 
 
 
 
 
 
 
 
 
 
 
161
  # Display messages
162
  for message in st.session_state.messages:
163
- with st.chat_message(message["role"]):
164
- # Format HF expert messages differently
165
- if message.get("source") == "hf_expert":
166
- st.markdown("### 🤖 HF Expert Analysis")
167
- st.markdown(message["content"])
168
- else:
169
- st.markdown(message["content"])
170
- if "timestamp" in message:
171
- st.caption(f"🕒 {message['timestamp']}")
172
 
173
  # Manual HF Analysis Section
174
  if st.session_state.messages and len(st.session_state.messages) > 0:
@@ -256,105 +277,137 @@ if st.session_state.get("hf_expert_requested", False):
256
  st.error(f"❌ HF Expert analysis failed: {user_msg}")
257
  st.session_state.hf_expert_requested = False
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  # Chat input - FIXED VERSION (moved outside of tabs)
260
  user_input = st.chat_input("Type your message here...", disabled=st.session_state.is_processing)
261
 
262
  # Process message when received
263
  if user_input and not st.session_state.is_processing:
264
- st.session_state.is_processing = True
265
-
266
- # Display user message
267
- with st.chat_message("user"):
268
- st.markdown(user_input)
269
-
270
- # Add to message history - ensure proper format
271
- st.session_state.messages.append({
272
- "role": "user",
273
- "content": user_input,
274
- "timestamp": datetime.now().strftime("%H:%M:%S")
275
- })
276
-
277
- # Process AI response
278
- with st.chat_message("assistant"):
279
- response_placeholder = st.empty()
280
- status_placeholder = st.empty()
281
 
282
- try:
283
- # Get conversation history
284
- user_session = session_manager.get_session("default_user")
285
- conversation = user_session.get("conversation", [])
286
- conversation_history = conversation[-5:] # Last 5 messages
287
- conversation_history.append({"role": "user", "content": user_input})
288
-
289
- # Try Ollama with proper error handling
290
- status_placeholder.info(PROCESSING_STAGES["ollama"])
291
- ai_response = None
 
 
 
 
 
292
 
293
  try:
294
- ai_response = send_to_ollama(
295
- user_input,
296
- conversation_history,
297
- st.session_state.ngrok_url_temp,
298
- st.session_state.selected_model
299
- )
 
 
 
300
 
301
- if ai_response:
302
- response_placeholder.markdown(ai_response)
303
- status_placeholder.success("✅ Response received!")
304
- else:
305
- status_placeholder.warning("⚠️ Empty response from Ollama")
306
-
307
- except Exception as ollama_error:
308
- user_msg = translate_error(ollama_error)
309
- status_placeholder.error(f"⚠️ {user_msg}")
310
-
311
- # Fallback to HF if available
312
- if config.hf_token and not ai_response:
313
- status_placeholder.info(PROCESSING_STAGES["hf_init"])
314
  try:
315
- ai_response = send_to_hf(user_input, conversation_history)
 
 
 
 
 
 
316
  if ai_response:
317
  response_placeholder.markdown(ai_response)
318
- status_placeholder.success("✅ HF response received!")
319
  else:
320
- status_placeholder.error(" No response from HF")
321
- except Exception as hf_error:
322
- user_msg = translate_error(hf_error)
 
323
  status_placeholder.error(f"⚠️ {user_msg}")
324
-
325
- # Save response if successful
326
- if ai_response:
327
- # Update conversation history
328
- conversation.append({"role": "user", "content": user_input})
329
- conversation.append({"role": "assistant", "content": ai_response})
330
- user_session["conversation"] = conversation
331
- session_manager.update_session("default_user", user_session)
332
 
333
- # Add to message history - ensure proper format
334
- st.session_state.messages.append({
335
- "role": "assistant",
336
- "content": ai_response,
337
- "timestamp": datetime.now().strftime("%H:%M:%S")
338
- })
339
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  st.session_state.messages.append({
341
  "role": "assistant",
342
- "content": "Sorry, I couldn't process your request. Please try again.",
343
  "timestamp": datetime.now().strftime("%H:%M:%S")
344
  })
345
-
346
- except Exception as e:
347
- user_msg = translate_error(e)
348
- response_placeholder.error(f"⚠️ {user_msg}")
349
- st.session_state.messages.append({
350
- "role": "assistant",
351
- "content": f"⚠️ {user_msg}",
352
- "timestamp": datetime.now().strftime("%H:%M:%S")
353
- })
354
- finally:
355
- st.session_state.is_processing = False
356
- time.sleep(0.5) # Brief pause
357
- st.experimental_rerun()
358
 
359
  # Add evaluation dashboard tab (separate from chat interface)
360
  st.divider()
@@ -408,20 +461,20 @@ with tab1:
408
  research_keywords = ["study", "research", "paper", "effectiveness", "clinical trial", "vitamin", "drug", "metformin", "CRISPR"]
409
  if any(kw in final_prompt.lower() for kw in research_keywords):
410
  st.markdown("**Related Research Papers:**")
411
- with st.spinner("Searching for academic papers..."):
412
  try:
413
  papers = find_papers(final_prompt, limit=3)
414
  if papers:
415
  for i, paper in enumerate(papers):
416
- with st.expander(f"📄 {paper['title'][:50]}{'...' if len(paper['title']) > 50 else ''}"):
417
  st.markdown(f"**Authors:** {', '.join(paper['authors'][:3])}")
418
  st.markdown(f"**Year:** {paper['year']}")
419
  st.markdown(f"**Citations:** {paper['citation_count']}")
420
  st.markdown(f"**Venue:** {paper['venue']}")
421
- st.markdown(f"**Abstract:** {paper['abstract'][:300]}{'...' if len(paper['abstract']) > 300 else ''}")
422
- st.markdown(f"[View Paper]({paper['url']})")
423
  else:
424
- st.info("No relevant papers found for this topic.")
425
  except Exception as e:
426
  st.warning(f"Could not fetch research papers: {translate_error(e)}")
427
 
@@ -512,6 +565,36 @@ with tab2:
512
  features.append("Weather Data")
513
 
514
  st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
 
516
  with tab3:
517
  st.header("ℹ️ About AI Life Coach")
 
43
  st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
44
  if "hf_expert_requested" not in st.session_state:
45
  st.session_state.hf_expert_requested = False
 
 
 
 
46
 
47
  # Sidebar layout redesign
48
  with st.sidebar:
 
149
  use_container_width=True,
150
  disabled=st.session_state.is_processing):
151
  st.session_state.hf_expert_requested = True
152
+
153
+ st.divider()
154
+ st.subheader("🐛 Debug Info")
155
+ # Show current configuration
156
+ st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
157
+ st.markdown(f"**Model:** {st.session_state.selected_model}")
158
+ st.markdown(f"**Ollama URL:** {st.session_state.ngrok_url_temp}")
159
+
160
+ # Show active features
161
+ features = []
162
+ if config.hf_token:
163
+ features.append("HF Expert")
164
+ if os.getenv("TAVILY_API_KEY"):
165
+ features.append("Web Search")
166
+ if config.openweather_api_key:
167
+ features.append("Weather")
168
+
169
+ st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
170
 
171
  # Main interface
172
  st.title("🧠 AI Life Coach")
173
  st.markdown("Ask me anything about personal development, goal setting, or life advice!")
174
 
175
+ # Consistent message rendering function
176
+ def render_message(role, content, timestamp=None):
177
+ """Render chat messages with consistent styling"""
178
+ with st.chat_message(role):
179
+ if role == "assistant" and content.startswith("### 🤖 HF Expert Analysis"):
180
+ st.markdown(content)
181
+ else:
182
+ st.markdown(content)
183
+ if timestamp:
184
+ st.caption(f"🕒 {timestamp}")
185
+
186
  # Display messages
187
  for message in st.session_state.messages:
188
+ render_message(
189
+ message["role"],
190
+ message["content"],
191
+ message.get("timestamp")
192
+ )
 
 
 
 
193
 
194
  # Manual HF Analysis Section
195
  if st.session_state.messages and len(st.session_state.messages) > 0:
 
277
  st.error(f"❌ HF Expert analysis failed: {user_msg}")
278
  st.session_state.hf_expert_requested = False
279
 
280
+ # Input validation function
281
+ def validate_user_input(text):
282
+ """Validate and sanitize user input"""
283
+ if not text or not text.strip():
284
+ return False, "Input cannot be empty"
285
+
286
+ if len(text) > 1000:
287
+ return False, "Input too long (max 1000 characters)"
288
+
289
+ # Check for potentially harmful patterns
290
+ harmful_patterns = ["<script", "javascript:", "onload=", "onerror="]
291
+ if any(pattern in text.lower() for pattern in harmful_patterns):
292
+ return False, "Potentially harmful input detected"
293
+
294
+ return True, text.strip()
295
+
296
  # Chat input - FIXED VERSION (moved outside of tabs)
297
  user_input = st.chat_input("Type your message here...", disabled=st.session_state.is_processing)
298
 
299
  # Process message when received
300
  if user_input and not st.session_state.is_processing:
301
+ # Validate input
302
+ is_valid, validated_input = validate_user_input(user_input)
303
+ if not is_valid:
304
+ st.error(validated_input)
305
+ st.session_state.is_processing = False
306
+ else:
307
+ st.session_state.is_processing = True
 
 
 
 
 
 
 
 
 
 
308
 
309
+ # Display user message
310
+ with st.chat_message("user"):
311
+ st.markdown(validated_input)
312
+
313
+ # Add to message history - ensure proper format
314
+ st.session_state.messages.append({
315
+ "role": "user",
316
+ "content": validated_input,
317
+ "timestamp": datetime.now().strftime("%H:%M:%S")
318
+ })
319
+
320
+ # Process AI response
321
+ with st.chat_message("assistant"):
322
+ response_placeholder = st.empty()
323
+ status_placeholder = st.empty()
324
 
325
  try:
326
+ # Get conversation history
327
+ user_session = session_manager.get_session("default_user")
328
+ conversation = user_session.get("conversation", [])
329
+ conversation_history = conversation[-5:] # Last 5 messages
330
+ conversation_history.append({"role": "user", "content": validated_input})
331
+
332
+ # Try Ollama with proper error handling
333
+ status_placeholder.info(PROCESSING_STAGES["ollama"])
334
+ ai_response = None
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  try:
337
+ ai_response = send_to_ollama(
338
+ validated_input,
339
+ conversation_history,
340
+ st.session_state.ngrok_url_temp,
341
+ st.session_state.selected_model
342
+ )
343
+
344
  if ai_response:
345
  response_placeholder.markdown(ai_response)
346
+ status_placeholder.success("✅ Response received!")
347
  else:
348
+ status_placeholder.warning("⚠️ Empty response from Ollama")
349
+
350
+ except Exception as ollama_error:
351
+ user_msg = translate_error(ollama_error)
352
  status_placeholder.error(f"⚠️ {user_msg}")
 
 
 
 
 
 
 
 
353
 
354
+ # Fallback to HF if available
355
+ if config.hf_token and not ai_response:
356
+ status_placeholder.info(PROCESSING_STAGES["hf_init"])
357
+ try:
358
+ ai_response = send_to_hf(validated_input, conversation_history)
359
+ if ai_response:
360
+ response_placeholder.markdown(ai_response)
361
+ status_placeholder.success("✅ HF response received!")
362
+ else:
363
+ status_placeholder.error("❌ No response from HF")
364
+ except Exception as hf_error:
365
+ user_msg = translate_error(hf_error)
366
+ status_placeholder.error(f"⚠️ {user_msg}")
367
+
368
+ # Save response if successful
369
+ if ai_response:
370
+ # Update conversation history
371
+ conversation.append({"role": "user", "content": validated_input})
372
+ conversation.append({"role": "assistant", "content": ai_response})
373
+ user_session["conversation"] = conversation
374
+ session_manager.update_session("default_user", user_session)
375
+
376
+ # Add to message history - ensure proper format
377
+ st.session_state.messages.append({
378
+ "role": "assistant",
379
+ "content": ai_response,
380
+ "timestamp": datetime.now().strftime("%H:%M:%S")
381
+ })
382
+
383
+ # Add feedback buttons
384
+ st.divider()
385
+ col1, col2 = st.columns(2)
386
+ with col1:
387
+ if st.button("👍 Helpful", key=f"helpful_{len(st.session_state.messages)}"):
388
+ st.success("Thanks for your feedback!")
389
+ with col2:
390
+ if st.button("👎 Not Helpful", key=f"not_helpful_{len(st.session_state.messages)}"):
391
+ st.success("Thanks for your feedback!")
392
+ else:
393
+ st.session_state.messages.append({
394
+ "role": "assistant",
395
+ "content": "Sorry, I couldn't process your request. Please try again.",
396
+ "timestamp": datetime.now().strftime("%H:%M:%S")
397
+ })
398
+
399
+ except Exception as e:
400
+ user_msg = translate_error(e)
401
+ response_placeholder.error(f"⚠️ {user_msg}")
402
  st.session_state.messages.append({
403
  "role": "assistant",
404
+ "content": f"⚠️ {user_msg}",
405
  "timestamp": datetime.now().strftime("%H:%M:%S")
406
  })
407
+ finally:
408
+ st.session_state.is_processing = False
409
+ time.sleep(0.5) # Brief pause
410
+ st.experimental_rerun()
 
 
 
 
 
 
 
 
 
411
 
412
  # Add evaluation dashboard tab (separate from chat interface)
413
  st.divider()
 
461
  research_keywords = ["study", "research", "paper", "effectiveness", "clinical trial", "vitamin", "drug", "metformin", "CRISPR"]
462
  if any(kw in final_prompt.lower() for kw in research_keywords):
463
  st.markdown("**Related Research Papers:**")
464
+ with st.spinner("Searching academic databases..."):
465
  try:
466
  papers = find_papers(final_prompt, limit=3)
467
  if papers:
468
  for i, paper in enumerate(papers):
469
+ with st.expander(f"📄 {paper['title'][:60]}..."):
470
  st.markdown(f"**Authors:** {', '.join(paper['authors'][:3])}")
471
  st.markdown(f"**Year:** {paper['year']}")
472
  st.markdown(f"**Citations:** {paper['citation_count']}")
473
  st.markdown(f"**Venue:** {paper['venue']}")
474
+ st.markdown(f"**Abstract:** {paper['abstract'][:200]}...")
475
+ st.markdown(f"[View Full Paper]({paper['url']})")
476
  else:
477
+ st.info("No relevant academic papers found for this topic.")
478
  except Exception as e:
479
  st.warning(f"Could not fetch research papers: {translate_error(e)}")
480
 
 
565
  features.append("Weather Data")
566
 
567
  st.markdown(f"**Active Features:** {', '.join(features) if features else 'None'}")
568
+
569
+ # Conversation Analytics
570
+ st.subheader("📊 Conversation Analytics")
571
+ try:
572
+ user_session = session_manager.get_session("default_user")
573
+ conversation = user_session.get("conversation", [])
574
+
575
+ if conversation:
576
+ # Analyze conversation patterns
577
+ user_messages = [msg for msg in conversation if msg["role"] == "user"]
578
+ ai_messages = [msg for msg in conversation if msg["role"] == "assistant"]
579
+
580
+ col1, col2, col3 = st.columns(3)
581
+ col1.metric("Total Exchanges", len(user_messages))
582
+ col2.metric("Avg Response Length",
583
+ round(sum(len(msg.get("content", "")) for msg in ai_messages) / len(ai_messages)) if ai_messages else 0)
584
+ col3.metric("Topics Discussed", len(set(["life", "goal", "health", "career"]) &
585
+ set(" ".join([msg.get("content", "") for msg in conversation]).lower().split())))
586
+
587
+ # Show most common words/topics
588
+ all_text = " ".join([msg.get("content", "") for msg in conversation]).lower()
589
+ common_words = ["life", "goal", "health", "career", "productivity", "mindfulness"]
590
+ relevant_topics = [word for word in common_words if word in all_text]
591
+ if relevant_topics:
592
+ st.markdown(f"**Detected Topics:** {', '.join(relevant_topics)}")
593
+ else:
594
+ st.info("No conversation data available yet.")
595
+
596
+ except Exception as e:
597
+ st.warning(f"Could not analyze conversation: {translate_error(e)}")
598
 
599
  with tab3:
600
  st.header("ℹ️ About AI Life Coach")
requirements.txt CHANGED
@@ -11,7 +11,3 @@ pygame==2.5.2
11
  pydantic==1.10.7
12
  typing-extensions>=4.5.0
13
  semanticscholar>=0.1.8
14
- semanticscholar>=0.1.8
15
- tavily-python>=0.1.0,<1.0.0
16
- semanticscholar>=0.1.8
17
- tavily-python>=0.1.0,<1.0.0
 
11
  pydantic==1.10.7
12
  typing-extensions>=4.5.0
13
  semanticscholar>=0.1.8
 
 
 
 
services/tts.py DELETED
@@ -1,88 +0,0 @@
1
- import os
2
- import requests
3
- from typing import Optional
4
- from utils.config import config
5
-
6
- class TTSService:
7
- """Service for converting text to speech"""
8
-
9
- def __init__(self):
10
- self.hf_token = config.hf_token
11
- self.tts_model = "facebook/fastspeech2-en-ljspeech"
12
- self.vocoder_model = "facebook/hifigan-universal"
13
-
14
- def synthesize_speech(self, text: str) -> Optional[bytes]:
15
- """
16
- Convert text to speech using Hugging Face API
17
-
18
- Args:
19
- text: Text to convert to speech
20
-
21
- Returns:
22
- Audio bytes or None if failed
23
- """
24
- if not self.hf_token:
25
- print("Hugging Face token not configured for TTS")
26
- return None
27
-
28
- try:
29
- # First, generate speech with text-to-speech model
30
- tts_headers = {
31
- "Authorization": f"Bearer {self.hf_token}"
32
- }
33
-
34
- tts_payload = {
35
- "inputs": text
36
- }
37
-
38
- tts_response = requests.post(
39
- f"https://api-inference.huggingface.co/models/{self.tts_model}",
40
- headers=tts_headers,
41
- json=tts_payload
42
- )
43
-
44
- if tts_response.status_code != 200:
45
- print(f"TTS model error: {tts_response.status_code} - {tts_response.text}")
46
- return None
47
-
48
- # Then, convert to audio with vocoder
49
- vocoder_response = requests.post(
50
- f"https://api-inference.huggingface.co/models/{self.vocoder_model}",
51
- headers=tts_headers,
52
- data=tts_response.content
53
- )
54
-
55
- if vocoder_response.status_code == 200:
56
- return vocoder_response.content
57
- else:
58
- print(f"Vocoder error: {vocoder_response.status_code} - {vocoder_response.text}")
59
- return None
60
-
61
- except Exception as e:
62
- print(f"Error synthesizing speech: {e}")
63
- return None
64
-
65
- def save_audio_file(self, text: str, filename: str) -> bool:
66
- """
67
- Synthesize speech and save to file
68
-
69
- Args:
70
- text: Text to convert to speech
71
- filename: Output filename (.wav)
72
-
73
- Returns:
74
- Boolean indicating success
75
- """
76
- audio_data = self.synthesize_speech(text)
77
- if audio_data:
78
- try:
79
- with open(filename, 'wb') as f:
80
- f.write(audio_data)
81
- return True
82
- except Exception as e:
83
- print(f"Error saving audio file: {e}")
84
- return False
85
- return False
86
-
87
- # Global TTS service instance
88
- tts_service = TTSService()