AnasAlokla's picture
Update app.py
6dfb995 verified
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()