Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
Hybrid AI Assistant - General Purpose + Healthcare Billing Expert
|
4 |
-
|
5 |
"""
|
6 |
|
7 |
import os
|
@@ -9,7 +9,7 @@ import sys
|
|
9 |
import json
|
10 |
import logging
|
11 |
import re
|
12 |
-
from typing import Dict, Optional, Tuple, List, Any
|
13 |
from dataclasses import dataclass, field
|
14 |
from enum import Enum
|
15 |
import requests
|
@@ -40,6 +40,19 @@ class ConversationContext:
|
|
40 |
messages: List[Dict[str, str]] = field(default_factory=list)
|
41 |
detected_codes: List[str] = field(default_factory=list)
|
42 |
last_topic: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
# ============= Healthcare Billing Database =============
|
45 |
|
@@ -181,12 +194,50 @@ class BillingCodesDB:
|
|
181 |
|
182 |
return found_codes
|
183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
184 |
# ============= AI Assistant Class =============
|
185 |
|
186 |
class HybridAIAssistant:
|
187 |
def __init__(self):
|
188 |
self.api_key = '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'
|
189 |
self.billing_db = BillingCodesDB()
|
|
|
190 |
self.context = ConversationContext()
|
191 |
|
192 |
self.headers = {
|
@@ -239,15 +290,39 @@ class HybridAIAssistant:
|
|
239 |
else:
|
240 |
return self.get_general_response(message, billing_context=True)
|
241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
def get_general_response(self, message: str, billing_context: bool = False) -> str:
|
243 |
"""Get response from OpenRouter API for general queries"""
|
244 |
|
245 |
-
#
|
246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247 |
You can assist with any topic - from casual conversation to complex questions.
|
248 |
When discussing medical billing codes, you provide accurate, detailed information.
|
249 |
-
Be conversational, helpful, and engaging.
|
250 |
-
|
251 |
|
252 |
if billing_context:
|
253 |
system_prompt += "\nThe user is asking about medical billing. Provide helpful information even if you don't have specific code details."
|
@@ -280,6 +355,11 @@ class HybridAIAssistant:
|
|
280 |
result = response.json()
|
281 |
ai_response = result['choices'][0]['message']['content']
|
282 |
|
|
|
|
|
|
|
|
|
|
|
283 |
# Update context
|
284 |
self.context.messages.append({'role': 'user', 'content': message})
|
285 |
self.context.messages.append({'role': 'assistant', 'content': ai_response})
|
@@ -299,27 +379,32 @@ class HybridAIAssistant:
|
|
299 |
|
300 |
def get_fallback_response(self, message: str) -> str:
|
301 |
"""Fallback responses when API fails"""
|
|
|
|
|
|
|
302 |
fallbacks = [
|
303 |
"I'm having trouble connecting right now, but I'm still here to help! Could you rephrase your question?",
|
304 |
"Let me think about that differently. What specific aspect would you like to know more about?",
|
305 |
"That's an interesting question! While I process that, is there anything specific you'd like to explore?",
|
306 |
"I'm here to help! Could you provide a bit more detail about what you're looking for?"
|
307 |
]
|
308 |
-
return random.choice(fallbacks)
|
309 |
|
310 |
-
def process_message(self, message: str) -> str:
|
311 |
-
"""Main method to process any message"""
|
312 |
if not message.strip():
|
313 |
-
return "Feel free to ask me anything! I can help with general questions or healthcare billing codes. 😊"
|
314 |
|
315 |
# Detect intent
|
316 |
intent = self.detect_intent(message)
|
317 |
|
318 |
# Route to appropriate handler
|
319 |
if intent['is_billing'] and intent['codes_found']:
|
320 |
-
|
321 |
else:
|
322 |
-
|
|
|
|
|
323 |
|
324 |
def reset_context(self):
|
325 |
"""Reset conversation context"""
|
@@ -335,10 +420,21 @@ def respond(message, history):
|
|
335 |
if not message.strip():
|
336 |
return "Feel free to ask me anything! I can help with general questions or healthcare billing codes. 😊"
|
337 |
|
338 |
-
# Process message
|
339 |
-
response = assistant.process_message(message)
|
|
|
|
|
340 |
return response
|
341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
def reset_chat():
|
343 |
"""Reset the conversation context"""
|
344 |
assistant.reset_context()
|
@@ -348,22 +444,22 @@ def reset_chat():
|
|
348 |
|
349 |
examples = [
|
350 |
"What is healthcare billing code A0429?",
|
351 |
-
"Can you explain CPT code 99213 in detail?",
|
352 |
"Tell me about DRG 470",
|
|
|
|
|
|
|
353 |
"How does artificial intelligence work?",
|
354 |
"Give me a simple pasta recipe",
|
355 |
-
"
|
356 |
-
"Write a short poem about nature",
|
357 |
-
"Help me write a professional email template",
|
358 |
-
"Give me creative story ideas"
|
359 |
]
|
360 |
|
361 |
# ============= Create Interface =============
|
362 |
|
363 |
def create_interface():
|
364 |
-
"""Create the Gradio ChatInterface"""
|
365 |
|
366 |
-
# Enhanced CSS with
|
367 |
custom_css = """
|
368 |
/* Global Styles */
|
369 |
.gradio-container {
|
@@ -373,9 +469,20 @@ def create_interface():
|
|
373 |
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
|
374 |
min-height: 100vh !important;
|
375 |
padding: 1rem !important;
|
|
|
376 |
}
|
377 |
|
378 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
379 |
.header-text {
|
380 |
text-align: center;
|
381 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
@@ -386,6 +493,7 @@ def create_interface():
|
|
386 |
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
|
387 |
position: relative;
|
388 |
overflow: hidden;
|
|
|
389 |
}
|
390 |
|
391 |
.header-text::before {
|
@@ -404,6 +512,34 @@ def create_interface():
|
|
404 |
50% { opacity: 0.6; }
|
405 |
}
|
406 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
407 |
.header-text h1 {
|
408 |
margin: 0;
|
409 |
font-size: 3rem;
|
@@ -438,7 +574,39 @@ def create_interface():
|
|
438 |
to { box-shadow: 0 0 20px rgba(255,255,255,0.6); }
|
439 |
}
|
440 |
|
441 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
442 |
.gradio-chatinterface {
|
443 |
background: white !important;
|
444 |
border-radius: 20px !important;
|
@@ -446,6 +614,17 @@ def create_interface():
|
|
446 |
padding: 2rem !important;
|
447 |
margin: 1rem 0 !important;
|
448 |
backdrop-filter: blur(10px) !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
449 |
}
|
450 |
|
451 |
/* Enhanced Buttons */
|
@@ -539,6 +718,40 @@ def create_interface():
|
|
539 |
letter-spacing: 1px;
|
540 |
}
|
541 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
542 |
/* Enhanced Accordion */
|
543 |
.gradio-accordion {
|
544 |
background: rgba(255,255,255,0.9) !important;
|
@@ -564,6 +777,12 @@ def create_interface():
|
|
564 |
padding: 1.5rem;
|
565 |
border: 1px solid rgba(255,255,255,0.3);
|
566 |
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
|
|
|
|
|
|
|
|
|
|
|
|
567 |
}
|
568 |
|
569 |
.feature-icon {
|
@@ -612,6 +831,18 @@ def create_interface():
|
|
612 |
grid-template-columns: 1fr;
|
613 |
gap: 1rem;
|
614 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
615 |
}
|
616 |
|
617 |
/* Loading Animation */
|
@@ -625,71 +856,20 @@ def create_interface():
|
|
625 |
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
626 |
background-size: 400% 100%;
|
627 |
}
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
#
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
# Additional controls
|
647 |
-
with gr.Row():
|
648 |
-
with gr.Column(scale=1):
|
649 |
-
reset_context_btn = gr.Button("🔄 Reset Context", elem_classes="reset-btn", size="sm")
|
650 |
-
with gr.Column(scale=3):
|
651 |
-
gr.HTML("") # Spacer
|
652 |
-
with gr.Column(scale=1):
|
653 |
-
gr.HTML("""
|
654 |
-
<div style='text-align: right; color: #718096; font-size: 12px; margin-top: 0.5rem;'>
|
655 |
-
Powered by GPT-3.5 Turbo<br>
|
656 |
-
Healthcare Billing Database
|
657 |
-
</div>
|
658 |
-
""")
|
659 |
-
|
660 |
-
# Info section
|
661 |
-
with gr.Accordion("ℹ️ About This Assistant", open=False):
|
662 |
-
gr.HTML("""
|
663 |
-
<div style="padding: 1rem; background: #f7fafc; border-radius: 8px; margin: 0.5rem 0;">
|
664 |
-
<h4 style="color: #2d3748; margin-top: 0;">🏥 Healthcare Billing Expert</h4>
|
665 |
-
<p style="color: #4a5568; margin-bottom: 1rem;">I'm specialized in healthcare billing codes including:</p>
|
666 |
-
<ul style="color: #4a5568; margin: 0.5rem 0;">
|
667 |
-
<li><strong>CPT Codes</strong> - Current Procedural Terminology</li>
|
668 |
-
<li><strong>HCPCS</strong> - Healthcare Common Procedure Coding System</li>
|
669 |
-
<li><strong>ICD-10</strong> - International Classification of Diseases</li>
|
670 |
-
<li><strong>DRG</strong> - Diagnosis-Related Groups</li>
|
671 |
-
</ul>
|
672 |
-
|
673 |
-
<h4 style="color: #2d3748;">💬 General AI Assistant</h4>
|
674 |
-
<p style="color: #4a5568; margin: 0;">I can also help with general questions, writing, coding, learning, and creative tasks!</p>
|
675 |
-
</div>
|
676 |
-
""")
|
677 |
-
|
678 |
-
# Connect reset button
|
679 |
-
reset_context_btn.click(
|
680 |
-
fn=reset_chat,
|
681 |
-
outputs=chat_interface.chatbot
|
682 |
-
)
|
683 |
-
|
684 |
-
return demo
|
685 |
-
|
686 |
-
# ============= Launch =============
|
687 |
-
|
688 |
-
if __name__ == "__main__":
|
689 |
-
app = create_interface()
|
690 |
-
app.launch(
|
691 |
-
server_name="0.0.0.0",
|
692 |
-
server_port=7860,
|
693 |
-
share=False,
|
694 |
-
show_error=True
|
695 |
-
)
|
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
Hybrid AI Assistant - General Purpose + Healthcare Billing Expert
|
4 |
+
Enhanced with Emotional UI and Voice Input
|
5 |
"""
|
6 |
|
7 |
import os
|
|
|
9 |
import json
|
10 |
import logging
|
11 |
import re
|
12 |
+
from typing import Dict, Optional, Tuple, List, Any
|
13 |
from dataclasses import dataclass, field
|
14 |
from enum import Enum
|
15 |
import requests
|
|
|
40 |
messages: List[Dict[str, str]] = field(default_factory=list)
|
41 |
detected_codes: List[str] = field(default_factory=list)
|
42 |
last_topic: Optional[str] = None
|
43 |
+
current_sentiment: str = "neutral"
|
44 |
+
sentiment_history: List[str] = field(default_factory=list)
|
45 |
+
|
46 |
+
class SentimentType(Enum):
|
47 |
+
VERY_POSITIVE = "very_positive"
|
48 |
+
POSITIVE = "positive"
|
49 |
+
NEUTRAL = "neutral"
|
50 |
+
NEGATIVE = "negative"
|
51 |
+
VERY_NEGATIVE = "very_negative"
|
52 |
+
ANXIOUS = "anxious"
|
53 |
+
FRUSTRATED = "frustrated"
|
54 |
+
EXCITED = "excited"
|
55 |
+
CONFUSED = "confused"
|
56 |
|
57 |
# ============= Healthcare Billing Database =============
|
58 |
|
|
|
194 |
|
195 |
return found_codes
|
196 |
|
197 |
+
# ============= Sentiment Analysis =============
|
198 |
+
|
199 |
+
class SentimentAnalyzer:
|
200 |
+
def __init__(self):
|
201 |
+
self.positive_words = ['great', 'awesome', 'excellent', 'fantastic', 'wonderful', 'amazing', 'perfect', 'love', 'happy', 'excited', 'thank', 'thanks', 'good', 'nice', 'brilliant', 'outstanding']
|
202 |
+
self.negative_words = ['terrible', 'awful', 'horrible', 'bad', 'worst', 'hate', 'frustrated', 'angry', 'sad', 'disappointed', 'upset', 'confused', 'difficult', 'problem', 'issue', 'error', 'wrong']
|
203 |
+
self.anxious_words = ['worried', 'concerned', 'nervous', 'anxious', 'scared', 'afraid', 'stress', 'panic', 'uncertain', 'unsure']
|
204 |
+
self.excited_words = ['excited', 'thrilled', 'amazing', 'wow', 'incredible', 'fantastic', 'brilliant', 'awesome']
|
205 |
+
|
206 |
+
def analyze_sentiment(self, text: str) -> SentimentType:
|
207 |
+
text_lower = text.lower()
|
208 |
+
|
209 |
+
positive_count = sum(1 for word in self.positive_words if word in text_lower)
|
210 |
+
negative_count = sum(1 for word in self.negative_words if word in text_lower)
|
211 |
+
anxious_count = sum(1 for word in self.anxious_words if word in text_lower)
|
212 |
+
excited_count = sum(1 for word in self.excited_words if word in text_lower)
|
213 |
+
|
214 |
+
# Check for question marks (confusion indicator)
|
215 |
+
question_marks = text.count('?')
|
216 |
+
exclamation_marks = text.count('!')
|
217 |
+
|
218 |
+
# Determine sentiment
|
219 |
+
if excited_count > 0 or exclamation_marks > 1:
|
220 |
+
return SentimentType.EXCITED
|
221 |
+
elif anxious_count > 0:
|
222 |
+
return SentimentType.ANXIOUS
|
223 |
+
elif question_marks > 1 and negative_count > 0:
|
224 |
+
return SentimentType.CONFUSED
|
225 |
+
elif negative_count > positive_count and negative_count > 1:
|
226 |
+
return SentimentType.VERY_NEGATIVE if negative_count > 2 else SentimentType.NEGATIVE
|
227 |
+
elif positive_count > negative_count and positive_count > 1:
|
228 |
+
return SentimentType.VERY_POSITIVE if positive_count > 2 else SentimentType.POSITIVE
|
229 |
+
elif 'frustrated' in text_lower or 'frustrating' in text_lower:
|
230 |
+
return SentimentType.FRUSTRATED
|
231 |
+
else:
|
232 |
+
return SentimentType.NEUTRAL
|
233 |
+
|
234 |
# ============= AI Assistant Class =============
|
235 |
|
236 |
class HybridAIAssistant:
|
237 |
def __init__(self):
|
238 |
self.api_key = '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'
|
239 |
self.billing_db = BillingCodesDB()
|
240 |
+
self.sentiment_analyzer = SentimentAnalyzer()
|
241 |
self.context = ConversationContext()
|
242 |
|
243 |
self.headers = {
|
|
|
290 |
else:
|
291 |
return self.get_general_response(message, billing_context=True)
|
292 |
|
293 |
+
def get_empathetic_response_prefix(self, sentiment: SentimentType) -> str:
|
294 |
+
"""Generate empathetic response based on sentiment"""
|
295 |
+
prefixes = {
|
296 |
+
SentimentType.VERY_POSITIVE: "I'm so glad to hear your enthusiasm! 🌟 ",
|
297 |
+
SentimentType.POSITIVE: "That's wonderful! 😊 ",
|
298 |
+
SentimentType.EXCITED: "I can feel your excitement! 🎉 ",
|
299 |
+
SentimentType.ANXIOUS: "I understand this might be causing some concern. Let me help ease your worries. 🤗 ",
|
300 |
+
SentimentType.FRUSTRATED: "I can sense your frustration, and I'm here to help make this easier for you. 💙 ",
|
301 |
+
SentimentType.CONFUSED: "No worries, I'm here to clear things up for you! 🧠 ",
|
302 |
+
SentimentType.NEGATIVE: "I hear that you're having some difficulties. Let me help you with that. 💚 ",
|
303 |
+
SentimentType.VERY_NEGATIVE: "I'm really sorry you're going through this. I'm here to support you. ❤️ ",
|
304 |
+
SentimentType.NEUTRAL: ""
|
305 |
+
}
|
306 |
+
return prefixes.get(sentiment, "")
|
307 |
+
|
308 |
def get_general_response(self, message: str, billing_context: bool = False) -> str:
|
309 |
"""Get response from OpenRouter API for general queries"""
|
310 |
|
311 |
+
# Analyze sentiment
|
312 |
+
sentiment = self.sentiment_analyzer.analyze_sentiment(message)
|
313 |
+
self.context.current_sentiment = sentiment.value
|
314 |
+
self.context.sentiment_history.append(sentiment.value)
|
315 |
+
|
316 |
+
# Keep only last 10 sentiments
|
317 |
+
if len(self.context.sentiment_history) > 10:
|
318 |
+
self.context.sentiment_history = self.context.sentiment_history[-10:]
|
319 |
+
|
320 |
+
# Prepare system prompt with empathy
|
321 |
+
system_prompt = """You are a helpful, friendly, and empathetic AI assistant with expertise in healthcare billing codes.
|
322 |
You can assist with any topic - from casual conversation to complex questions.
|
323 |
When discussing medical billing codes, you provide accurate, detailed information.
|
324 |
+
Be conversational, helpful, and engaging. Show empathy and understanding.
|
325 |
+
Adapt your tone based on the user's emotional state - be more supportive if they seem frustrated or anxious."""
|
326 |
|
327 |
if billing_context:
|
328 |
system_prompt += "\nThe user is asking about medical billing. Provide helpful information even if you don't have specific code details."
|
|
|
355 |
result = response.json()
|
356 |
ai_response = result['choices'][0]['message']['content']
|
357 |
|
358 |
+
# Add empathetic prefix based on sentiment
|
359 |
+
empathy_prefix = self.get_empathetic_response_prefix(sentiment)
|
360 |
+
if empathy_prefix:
|
361 |
+
ai_response = empathy_prefix + ai_response
|
362 |
+
|
363 |
# Update context
|
364 |
self.context.messages.append({'role': 'user', 'content': message})
|
365 |
self.context.messages.append({'role': 'assistant', 'content': ai_response})
|
|
|
379 |
|
380 |
def get_fallback_response(self, message: str) -> str:
|
381 |
"""Fallback responses when API fails"""
|
382 |
+
sentiment = self.sentiment_analyzer.analyze_sentiment(message)
|
383 |
+
empathy_prefix = self.get_empathetic_response_prefix(sentiment)
|
384 |
+
|
385 |
fallbacks = [
|
386 |
"I'm having trouble connecting right now, but I'm still here to help! Could you rephrase your question?",
|
387 |
"Let me think about that differently. What specific aspect would you like to know more about?",
|
388 |
"That's an interesting question! While I process that, is there anything specific you'd like to explore?",
|
389 |
"I'm here to help! Could you provide a bit more detail about what you're looking for?"
|
390 |
]
|
391 |
+
return empathy_prefix + random.choice(fallbacks)
|
392 |
|
393 |
+
def process_message(self, message: str) -> Tuple[str, str]:
|
394 |
+
"""Main method to process any message and return response with sentiment"""
|
395 |
if not message.strip():
|
396 |
+
return "Feel free to ask me anything! I can help with general questions or healthcare billing codes. 😊", "neutral"
|
397 |
|
398 |
# Detect intent
|
399 |
intent = self.detect_intent(message)
|
400 |
|
401 |
# Route to appropriate handler
|
402 |
if intent['is_billing'] and intent['codes_found']:
|
403 |
+
response = self.handle_billing_query(message, intent['codes_found'])
|
404 |
else:
|
405 |
+
response = self.get_general_response(message, billing_context=intent['is_billing'])
|
406 |
+
|
407 |
+
return response, self.context.current_sentiment
|
408 |
|
409 |
def reset_context(self):
|
410 |
"""Reset conversation context"""
|
|
|
420 |
if not message.strip():
|
421 |
return "Feel free to ask me anything! I can help with general questions or healthcare billing codes. 😊"
|
422 |
|
423 |
+
# Process message and get sentiment
|
424 |
+
response, sentiment = assistant.process_message(message)
|
425 |
+
|
426 |
+
# Update UI based on sentiment (this will be handled by JavaScript)
|
427 |
return response
|
428 |
|
429 |
+
def process_voice_input(audio):
|
430 |
+
"""Process voice input and return text"""
|
431 |
+
if audio is None:
|
432 |
+
return "No audio received. Please try again."
|
433 |
+
|
434 |
+
# For now, return a placeholder message
|
435 |
+
# In a real implementation, you'd use speech recognition here
|
436 |
+
return "Voice input processed! (Speech recognition would be implemented here)"
|
437 |
+
|
438 |
def reset_chat():
|
439 |
"""Reset the conversation context"""
|
440 |
assistant.reset_context()
|
|
|
444 |
|
445 |
examples = [
|
446 |
"What is healthcare billing code A0429?",
|
447 |
+
"Can you explain CPT code 99213 in detail?",
|
448 |
"Tell me about DRG 470",
|
449 |
+
"I'm feeling frustrated with this billing issue",
|
450 |
+
"This is confusing, can you help me understand?",
|
451 |
+
"Thank you so much! This is exactly what I needed!",
|
452 |
"How does artificial intelligence work?",
|
453 |
"Give me a simple pasta recipe",
|
454 |
+
"Write a short poem about nature"
|
|
|
|
|
|
|
455 |
]
|
456 |
|
457 |
# ============= Create Interface =============
|
458 |
|
459 |
def create_interface():
|
460 |
+
"""Create the Gradio ChatInterface with Emotional UI and Voice Input"""
|
461 |
|
462 |
+
# Enhanced CSS with emotional UI and voice features
|
463 |
custom_css = """
|
464 |
/* Global Styles */
|
465 |
.gradio-container {
|
|
|
469 |
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
|
470 |
min-height: 100vh !important;
|
471 |
padding: 1rem !important;
|
472 |
+
transition: all 0.5s ease !important;
|
473 |
}
|
474 |
|
475 |
+
/* Emotional UI Color Schemes */
|
476 |
+
.sentiment-positive { background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%) !important; }
|
477 |
+
.sentiment-very-positive { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%) !important; }
|
478 |
+
.sentiment-negative { background: linear-gradient(135deg, #d299c2 0%, #fef9d7 100%) !important; }
|
479 |
+
.sentiment-very-negative { background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%) !important; }
|
480 |
+
.sentiment-anxious { background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%) !important; }
|
481 |
+
.sentiment-frustrated { background: linear-gradient(135deg, #ff8a80 0%, #ffad80 100%) !important; }
|
482 |
+
.sentiment-excited { background: linear-gradient(135deg, #ffd89b 0%, #19547b 100%) !important; }
|
483 |
+
.sentiment-confused { background: linear-gradient(135deg, #a8caba 0%, #5d4e75 100%) !important; }
|
484 |
+
|
485 |
+
/* Enhanced Header with Mood Indicator */
|
486 |
.header-text {
|
487 |
text-align: center;
|
488 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
493 |
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
|
494 |
position: relative;
|
495 |
overflow: hidden;
|
496 |
+
transition: all 0.5s ease;
|
497 |
}
|
498 |
|
499 |
.header-text::before {
|
|
|
512 |
50% { opacity: 0.6; }
|
513 |
}
|
514 |
|
515 |
+
/* Mood Indicator */
|
516 |
+
.mood-indicator {
|
517 |
+
position: absolute;
|
518 |
+
top: 1rem;
|
519 |
+
right: 1rem;
|
520 |
+
width: 60px;
|
521 |
+
height: 60px;
|
522 |
+
border-radius: 50%;
|
523 |
+
background: rgba(255,255,255,0.2);
|
524 |
+
backdrop-filter: blur(10px);
|
525 |
+
display: flex;
|
526 |
+
align-items: center;
|
527 |
+
justify-content: center;
|
528 |
+
font-size: 24px;
|
529 |
+
transition: all 0.5s ease;
|
530 |
+
animation: breathe 3s ease-in-out infinite;
|
531 |
+
}
|
532 |
+
|
533 |
+
@keyframes breathe {
|
534 |
+
0%, 100% { transform: scale(1); }
|
535 |
+
50% { transform: scale(1.05); }
|
536 |
+
}
|
537 |
+
|
538 |
+
.mood-positive { background: rgba(132, 250, 176, 0.3) !important; }
|
539 |
+
.mood-negative { background: rgba(255, 154, 158, 0.3) !important; }
|
540 |
+
.mood-anxious { background: rgba(255, 236, 210, 0.3) !important; }
|
541 |
+
.mood-excited { background: rgba(255, 216, 155, 0.3) !important; }
|
542 |
+
|
543 |
.header-text h1 {
|
544 |
margin: 0;
|
545 |
font-size: 3rem;
|
|
|
574 |
to { box-shadow: 0 0 20px rgba(255,255,255,0.6); }
|
575 |
}
|
576 |
|
577 |
+
/* Voice Input Button */
|
578 |
+
.voice-btn {
|
579 |
+
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%) !important;
|
580 |
+
color: white !important;
|
581 |
+
border: none !important;
|
582 |
+
border-radius: 50% !important;
|
583 |
+
width: 60px !important;
|
584 |
+
height: 60px !important;
|
585 |
+
font-size: 24px !important;
|
586 |
+
margin: 0.5rem !important;
|
587 |
+
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important;
|
588 |
+
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3) !important;
|
589 |
+
position: relative;
|
590 |
+
overflow: hidden;
|
591 |
+
}
|
592 |
+
|
593 |
+
.voice-btn:hover {
|
594 |
+
transform: scale(1.1) !important;
|
595 |
+
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.5) !important;
|
596 |
+
}
|
597 |
+
|
598 |
+
.voice-btn.recording {
|
599 |
+
animation: recordPulse 1s ease-in-out infinite !important;
|
600 |
+
background: linear-gradient(135deg, #ff3030 0%, #ff1010 100%) !important;
|
601 |
+
}
|
602 |
+
|
603 |
+
@keyframes recordPulse {
|
604 |
+
0% { box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.7); }
|
605 |
+
70% { box-shadow: 0 0 0 20px rgba(255, 107, 107, 0); }
|
606 |
+
100% { box-shadow: 0 0 0 0 rgba(255, 107, 107, 0); }
|
607 |
+
}
|
608 |
+
|
609 |
+
/* Chat Interface Styling with Emotional Feedback */
|
610 |
.gradio-chatinterface {
|
611 |
background: white !important;
|
612 |
border-radius: 20px !important;
|
|
|
614 |
padding: 2rem !important;
|
615 |
margin: 1rem 0 !important;
|
616 |
backdrop-filter: blur(10px) !important;
|
617 |
+
transition: all 0.5s ease !important;
|
618 |
+
}
|
619 |
+
|
620 |
+
.gradio-chatinterface.emotional-positive {
|
621 |
+
border: 2px solid rgba(132, 250, 176, 0.5) !important;
|
622 |
+
box-shadow: 0 25px 50px rgba(132, 250, 176, 0.2) !important;
|
623 |
+
}
|
624 |
+
|
625 |
+
.gradio-chatinterface.emotional-negative {
|
626 |
+
border: 2px solid rgba(255, 154, 158, 0.5) !important;
|
627 |
+
box-shadow: 0 25px 50px rgba(255, 154, 158, 0.2) !important;
|
628 |
}
|
629 |
|
630 |
/* Enhanced Buttons */
|
|
|
718 |
letter-spacing: 1px;
|
719 |
}
|
720 |
|
721 |
+
/* Empathy Animations */
|
722 |
+
@keyframes empathyPulse {
|
723 |
+
0%, 100% { transform: scale(1); }
|
724 |
+
50% { transform: scale(1.02); }
|
725 |
+
}
|
726 |
+
|
727 |
+
@keyframes supportGlow {
|
728 |
+
0%, 100% { box-shadow: 0 0 10px rgba(102, 126, 234, 0.3); }
|
729 |
+
50% { box-shadow: 0 0 20px rgba(102, 126, 234, 0.6); }
|
730 |
+
}
|
731 |
+
|
732 |
+
.empathy-support {
|
733 |
+
animation: empathyPulse 2s ease-in-out infinite, supportGlow 3s ease-in-out infinite;
|
734 |
+
}
|
735 |
+
|
736 |
+
/* Voice Recognition Feedback */
|
737 |
+
.voice-feedback {
|
738 |
+
position: fixed;
|
739 |
+
bottom: 2rem;
|
740 |
+
right: 2rem;
|
741 |
+
background: rgba(102, 126, 234, 0.9);
|
742 |
+
color: white;
|
743 |
+
padding: 1rem 1.5rem;
|
744 |
+
border-radius: 50px;
|
745 |
+
backdrop-filter: blur(10px);
|
746 |
+
z-index: 1000;
|
747 |
+
animation: slideInRight 0.3s ease-out;
|
748 |
+
}
|
749 |
+
|
750 |
+
@keyframes slideInRight {
|
751 |
+
from { transform: translateX(100%); opacity: 0; }
|
752 |
+
to { transform: translateX(0); opacity: 1; }
|
753 |
+
}
|
754 |
+
|
755 |
/* Enhanced Accordion */
|
756 |
.gradio-accordion {
|
757 |
background: rgba(255,255,255,0.9) !important;
|
|
|
777 |
padding: 1.5rem;
|
778 |
border: 1px solid rgba(255,255,255,0.3);
|
779 |
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
780 |
+
transition: all 0.3s ease;
|
781 |
+
}
|
782 |
+
|
783 |
+
.feature-card:hover {
|
784 |
+
transform: translateY(-5px);
|
785 |
+
box-shadow: 0 15px 35px rgba(0,0,0,0.15);
|
786 |
}
|
787 |
|
788 |
.feature-icon {
|
|
|
831 |
grid-template-columns: 1fr;
|
832 |
gap: 1rem;
|
833 |
}
|
834 |
+
|
835 |
+
.mood-indicator {
|
836 |
+
width: 50px;
|
837 |
+
height: 50px;
|
838 |
+
font-size: 20px;
|
839 |
+
}
|
840 |
+
|
841 |
+
.voice-btn {
|
842 |
+
width: 50px !important;
|
843 |
+
height: 50px !important;
|
844 |
+
font-size: 20px !important;
|
845 |
+
}
|
846 |
}
|
847 |
|
848 |
/* Loading Animation */
|
|
|
856 |
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
857 |
background-size: 400% 100%;
|
858 |
}
|
859 |
+
|
860 |
+
/* Sentiment-based message styling */
|
861 |
+
.message-positive {
|
862 |
+
border-left: 4px solid #84fab0 !important;
|
863 |
+
background: linear-gradient(135deg, rgba(132, 250, 176, 0.1) 0%, rgba(143, 211, 244, 0.1) 100%) !important;
|
864 |
+
}
|
865 |
+
|
866 |
+
.message-negative {
|
867 |
+
border-left: 4px solid #ff9a9e !important;
|
868 |
+
background: linear-gradient(135deg, rgba(255, 154, 158, 0.1) 0%, rgba(254, 207, 239, 0.1) 100%) !important;
|
869 |
+
}
|
870 |
+
|
871 |
+
.message-anxious {
|
872 |
+
border-left: 4px solid #ffecd2 !important;
|
873 |
+
background: linear-gradient(135deg, rgba(255, 236, 210, 0.1) 0%, rgba(252, 182, 159, 0.1) 100%) !important;
|
874 |
+
}
|
875 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|