import streamlit as st from transformers import pipeline import google.generativeai as genai import json import random import os from dotenv import load_dotenv import langdetect from langdetect import detect, DetectorFactory from langdetect.lang_detect_exception import LangDetectException # Set seed for consistent language detection DetectorFactory.seed = 0 # Load environment variables load_dotenv() # Load language configurations from JSON with open('languages_config.json', 'r', encoding='utf-8') as f: LANGUAGES = json.load(f)['LANGUAGES'] # Load the JSON data for emotion templates with open('emotion_templates.json', 'r') as f: data = json.load(f) # Configure Gemini API gemini_api_key = os.getenv("GEMINI_API_KEY") if not gemini_api_key: st.error("GEMINI_API_KEY not found in environment variables. Please set it in your .env file.") st.stop() genai.configure(api_key=gemini_api_key) model = genai.GenerativeModel('gemini-2.0-flash') # Configure Hugging Face API (optional, for private models or rate limiting) hf_token = os.getenv("HUGGINGFACE_TOKEN") if hf_token: os.environ["HUGGINGFACE_HUB_TOKEN"] = hf_token # Available emotion detection models EMOTION_MODELS = { "AnasAlokla/multilingual_go_emotions": "Multilingual Go Emotions (Original)", "AnasAlokla/multilingual_go_emotions_V1.1": "Multilingual Go Emotions (V1.1)", "AnasAlokla/multilingual_go_emotions_V1.2": "Multilingual Go Emotions (V1.2)" } # Language mapping for detection SUPPORTED_LANGUAGES = { 'en': 'English', 'ar': 'Arabic', 'fr': 'French', 'es': 'Spanish', 'nl': 'Dutch', 'tr': 'Turkish' } def normalize_emotion_predictions(predictions): """ Normalize emotion predictions to ensure consistent format. Handles different return formats from Hugging Face pipelines. """ try: # If predictions is a list of lists (multiple inputs) if isinstance(predictions, list) and len(predictions) > 0: if isinstance(predictions[0], list): # Take the first prediction set predictions = predictions[0] # Ensure each prediction has the required keys normalized = [] for pred in predictions: if isinstance(pred, dict): # Handle different possible key names label = pred.get('label') or pred.get('LABEL') or pred.get('emotion', 'unknown') score = pred.get('score') or pred.get('SCORE') or pred.get('confidence', 0.0) normalized.append({ 'label': str(label).lower(), 'score': float(score) }) else: # Handle unexpected format st.warning(f"Unexpected prediction format: {pred}") continue return normalized if normalized else [{'label': 'neutral', 'score': 1.0}] else: # Handle case where predictions is not in expected format st.warning(f"Unexpected predictions format: {type(predictions)}") return [{'label': 'neutral', 'score': 1.0}] except Exception as e: st.error(f"Error normalizing predictions: {str(e)}") return [{'label': 'neutral', 'score': 1.0}] def detect_language(text): """Detect the language of the input text.""" try: detected_lang = detect(text) if detected_lang in SUPPORTED_LANGUAGES: return detected_lang else: return 'en' # Default to English if language not supported except LangDetectException: return 'en' # Default to English if detection fails def get_language_name(lang_code): """Get the full language name from language code.""" return SUPPORTED_LANGUAGES.get(lang_code, 'English') def categorize_emotion(emotion): """Categorize emotion as positive, negative, or neutral.""" positive_emotions = ['admiration', 'amusement', 'approval', 'caring', 'curiosity', 'desire', 'excitement', 'gratitude', 'joy', 'love', 'optimism', 'pride', 'relief'] negative_emotions = ['anger', 'annoyance', 'confusion', 'disappointment', 'disapproval', 'disgust', 'embarrassment', 'fear', 'grief', 'nervousness', 'remorse', 'sadness'] if emotion in positive_emotions: return 'positive' elif emotion in negative_emotions: return 'negative' else: return 'neutral' def generate_text(prompt, context=""): """ Generates text using the Gemini model. """ try: response = model.generate_content(prompt) return response.text except Exception as e: print(f"Error generating text: {e}") return "I am sorry, I encountered an error while generating the text." def create_enhanced_prompt(emotion, topic, detected_language, emotion_score): """ Creates an enhanced emotional prompt based on detected language and emotion intensity. """ # Get base template from emotion_templates.json templates = data["emotion_templates"][emotion] base_prompt = random.choice(templates) # Replace placeholders if topic: placeholders = ["[topic/person]", "[topic]", "[person]", "[object]", "[outcome]"] for placeholder in placeholders: base_prompt = base_prompt.replace(placeholder, topic) # Get language name language_name = get_language_name(detected_language) # Get emotion category emotion_category = categorize_emotion(emotion) # Get emotional enhancers from JSON file emotional_enhancers = data.get("emotional_enhancers", {}) language_enhancers = emotional_enhancers.get(detected_language, emotional_enhancers.get('en', {})) emotion_enhancer = "" if language_enhancers and emotion_category in language_enhancers: emotion_enhancer = random.choice(language_enhancers[emotion_category]) # Calculate emotion intensity intensity = "high" if emotion_score > 0.7 else "moderate" if emotion_score > 0.4 else "low" # Create enhanced prompt enhanced_prompt = f""" You are an emotionally intelligent AI assistant. Respond with genuine {emotion} emotion at {intensity} intensity. Language Instructions: - Respond ONLY in {language_name} - Use natural, native-speaker expressions - Match the emotional tone of a {language_name} speaker Emotional Guidelines: - The detected emotion is: {emotion} (confidence: {emotion_score:.2f}) - Emotion category: {emotion_category} - Use emotionally resonant words like: {emotion_enhancer} - Express {emotion} authentically and appropriately - Make your response feel genuinely {emotion_category} Context: {base_prompt} Topic to respond about: {topic} Requirements: - Keep response concise but emotionally expressive (2-4 sentences) - Use appropriate emotional language for {emotion} - Sound natural in {language_name} - Show empathy and understanding - Match the emotional intensity of the user's input """ return enhanced_prompt @st.cache_resource def load_emotion_classifier(model_name): """Load and cache the emotion classifier model.""" try: # Use the HF token if available for authentication if hf_token: return pipeline("text-classification", model=model_name, use_auth_token=hf_token, top_k=None) else: return pipeline("text-classification", model=model_name, top_k=None) except Exception as e: st.error(f"Error loading model {model_name}: {str(e)}") return None def get_ai_response(user_input, emotion_predictions, detected_language): """Generates AI response based on user input, detected emotions, and language.""" try: # Ensure predictions are normalized normalized_predictions = normalize_emotion_predictions(emotion_predictions) dominant_emotion = None max_score = 0 for prediction in normalized_predictions: if prediction['score'] > max_score: max_score = prediction['score'] dominant_emotion = prediction['label'] if dominant_emotion is None: return "Error: No emotion detected for response generation." # Create enhanced prompt with language and emotion context prompt_text = create_enhanced_prompt(dominant_emotion, user_input, detected_language, max_score) response = generate_text(prompt_text) return response except Exception as e: st.error(f"Error generating AI response: {str(e)}") return "I'm sorry, I encountered an error while generating a response." def display_top_predictions(emotion_predictions, selected_language, num_predictions=3): """Display top emotion predictions in sidebar.""" try: # Normalize predictions first normalized_predictions = normalize_emotion_predictions(emotion_predictions) # Sort predictions by score in descending order sorted_predictions = sorted(normalized_predictions, key=lambda x: x['score'], reverse=True) # Take top N predictions top_predictions = sorted_predictions[:num_predictions] # Display in sidebar st.sidebar.markdown("---") st.sidebar.subheader("🎯 Top Emotion Predictions") for i, prediction in enumerate(top_predictions, 1): emotion = prediction['label'] score = prediction['score'] percentage = score * 100 # Create a progress bar for visual representation st.sidebar.markdown(f"**{i}. {emotion.title()}**") st.sidebar.progress(score) st.sidebar.markdown(f"Score: {percentage:.1f}%") st.sidebar.markdown("---") except Exception as e: st.sidebar.error(f"Error displaying predictions: {str(e)}") def display_language_info(detected_language, confidence_scores=None): """Display detected language information.""" language_name = get_language_name(detected_language) st.sidebar.markdown("---") st.sidebar.subheader("🌐 Language Detection") st.sidebar.success(f"**Detected:** {language_name} ({detected_language.upper()})") if confidence_scores: st.sidebar.markdown("**Detection Confidence:**") for lang, score in confidence_scores.items(): if lang in SUPPORTED_LANGUAGES: lang_name = SUPPORTED_LANGUAGES[lang] st.sidebar.markdown(f"• {lang_name}: {score:.2f}") def main(): # Sidebar configurations st.sidebar.header("⚙️ Configuration") # Language Selection selected_language = st.sidebar.selectbox( "🌐 Select Interface Language", list(LANGUAGES.keys()), index=0 # Default to English ) # Model Selection selected_model_key = st.sidebar.selectbox( "🤖 Select Emotion Detection Model", list(EMOTION_MODELS.keys()), format_func=lambda x: EMOTION_MODELS[x], index=0 # Default to first model ) # Number of predictions to show in sidebar num_predictions = st.sidebar.slider( "📊 Number of predictions to show", min_value=1, max_value=6, value=3, step=1 ) # Language detection settings auto_detect = True # Load the selected emotion classifier emotion_classifier = load_emotion_classifier(selected_model_key) # Check if model loaded successfully if emotion_classifier is None: st.error("Failed to load the selected emotion detection model. Please try again or select a different model.") return # Display selected model info st.sidebar.success(f"✅ Current Model: {EMOTION_MODELS[selected_model_key]}") # Display Image if os.path.exists('chatBot_image.jpg'): st.image('chatBot_image.jpg', channels='RGB') # Set page title and header based on selected language st.title(LANGUAGES[selected_language]['title']) st.markdown(f"### 💬 {LANGUAGES[selected_language]['analyze_subtitle']}") # Add language support info st.info("🌍 **Supported Languages:** English, Arabic, French, Spanish, Dutch, Turkish") # Input Text Box user_input = st.text_area( LANGUAGES[selected_language]['input_placeholder'], "", height=100, help="Type your message here to analyze emotions and get an emotionally appropriate response" ) if user_input: try: # Language Detection if auto_detect: detected_language = detect_language(user_input) # Display language detection results display_language_info(detected_language) # Emotion Detection with st.spinner("Analyzing emotions..."): emotion_predictions = emotion_classifier(user_input) # Normalize predictions normalized_predictions = normalize_emotion_predictions(emotion_predictions) # Display top predictions in sidebar display_top_predictions(emotion_predictions, selected_language, num_predictions) # Display Emotions in main area (top 5) st.subheader(LANGUAGES[selected_language]['emotions_header']) top_5_emotions = sorted(normalized_predictions, key=lambda x: x['score'], reverse=True)[:5] # Create columns for better display col1, col2 = st.columns(2) for i, prediction in enumerate(top_5_emotions): emotion = prediction['label'] score = prediction['score'] percentage = score * 100 # Add emotion category indicator emotion_category = categorize_emotion(emotion) category_emoji = "😊" if emotion_category == "positive" else "😔" if emotion_category == "negative" else "😐" if i % 2 == 0: with col1: st.metric( label=f"{category_emoji} {emotion.title()}", value=f"{percentage:.1f}%", delta=None ) else: with col2: st.metric( label=f"{category_emoji} {emotion.title()}", value=f"{percentage:.1f}%", delta=None ) # Get AI Response with enhanced emotional intelligence with st.spinner("Generating emotionally intelligent response..."): ai_response = get_ai_response(user_input, emotion_predictions, detected_language) # Display AI Response st.subheader(f"🤖 {LANGUAGES[selected_language]['response_header']}") # Show dominant emotion and response language dominant_emotion = max(normalized_predictions, key=lambda x: x['score']) language_name = get_language_name(detected_language) # Display the response in a nice container with st.container(): st.write(ai_response) # Add emotion intensity indicator emotion_score = dominant_emotion['score'] intensity = "High" if emotion_score > 0.7 else "Moderate" if emotion_score > 0.4 else "Low" st.caption(f"Emotion Intensity: {intensity} ({emotion_score:.2f})") except Exception as e: st.error(f"An error occurred: {str(e)}") st.error("Please try again with different input or check your configuration.") # Run the main function if __name__ == "__main__": main()