| import gradio as gr |
| import json |
| import pandas as pd |
| from openai import OpenAI |
| import os |
| from dotenv import load_dotenv |
| from prompts import emotion_detection_prompt, single_emotion_detection_prompt, evaluation_prompt |
|
|
| load_dotenv() |
|
|
| |
| client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) |
|
|
| def detect_emotions(conversation_text): |
| """Parse conversation text and detect emotions for each utterance""" |
| |
| |
| utterances = [] |
| lines = conversation_text.strip().split('\n') |
| |
| for line in lines: |
| line = line.strip() |
| if line and ':' in line: |
| |
| parts = line.split(':', 1) |
| if len(parts) == 2: |
| speaker = parts[0].strip() |
| utterance = parts[1].strip() |
| if utterance: |
| utterances.append({ |
| "speaker": speaker, |
| "utterance": utterance |
| }) |
| |
| if not utterances: |
| return "No valid conversation found. Please format as 'Speaker: utterance'" |
| |
| system_prompt = emotion_detection_prompt |
|
|
| user_prompt = ( |
| "Conversation context:\n" |
| f"{json.dumps(utterances, indent=2)}\n\n" |
| "Process the conversation turn by turn." |
| ) |
|
|
| messages = [ |
| {"role": "system", "content": system_prompt}, |
| {"role": "user", "content": user_prompt} |
| ] |
|
|
| try: |
| response = client.chat.completions.create( |
| model="gpt-4o-mini", |
| messages=messages, |
| temperature=0.0, |
| ) |
| |
| output = response.choices[0].message.content |
| parsed_output = json.loads(output) |
| |
| |
| formatted_output = [] |
| for item in parsed_output: |
| formatted_output.append(f"**{item['speaker']}**: \"{item['utterance']}\"") |
| formatted_output.append(f" - **Emotion**: {item['emotion']}") |
| formatted_output.append(f" - **Confidence**: {item['confidence']}") |
| formatted_output.append(f" - **Reason**: {item['reason']}") |
| formatted_output.append("") |
| |
| return "\n".join(formatted_output) |
| |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
| def predict_single_emotion(utterance): |
| """Predict Plutchik emotion for a single utterance""" |
| system_prompt = single_emotion_detection_prompt |
|
|
| user_prompt = f"Utterance: {utterance}\n\nWhat is the Plutchik emotion? Output only the emotion word:" |
|
|
| messages = [ |
| {"role": "system", "content": system_prompt}, |
| {"role": "user", "content": user_prompt} |
| ] |
|
|
| try: |
| response = client.chat.completions.create( |
| model="gpt-4o-mini", |
| messages=messages, |
| temperature=0.0, |
| ) |
| output = response.choices[0].message.content.strip().lower() |
| |
| valid_emotions = ["joy", "trust", "fear", "surprise", "sadness", "disgust", "anger", "anticipation", "neutral"] |
| for emotion in valid_emotions: |
| if emotion in output: |
| return emotion |
| return output.split()[0] if output else "neutral" |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
| def map_meld_to_plutchik(meld_emotion): |
| """Use LLM to map MELD emotion to Plutchik emotion""" |
| system_prompt = evaluation_prompt |
|
|
| user_prompt = f"MELD emotion: {meld_emotion}\n\nMap to Plutchik emotion (output only the word):" |
|
|
| messages = [ |
| {"role": "system", "content": system_prompt}, |
| {"role": "user", "content": user_prompt} |
| ] |
|
|
| try: |
| response = client.chat.completions.create( |
| model="gpt-4o-mini", |
| messages=messages, |
| temperature=0.0, |
| ) |
| output = response.choices[0].message.content.strip().lower() |
| valid_emotions = ["joy", "trust", "fear", "surprise", "sadness", "disgust", "anger", "anticipation", "neutral"] |
| for emotion in valid_emotions: |
| if emotion in output: |
| return emotion |
| return output.split()[0] if output else "neutral" |
| except Exception as e: |
| return meld_emotion |
|
|
| def evaluate_meld_dataset(max_samples=100): |
| """Evaluate on MELD dataset using LLM for both prediction and mapping""" |
| try: |
| df = pd.read_csv("dev_sent_emo.csv") |
| |
| rows = [] |
| count = 0 |
| |
| for _, row in df.iterrows(): |
| if count >= max_samples: |
| break |
| |
| utterance = str(row.get("Utterance", "")) |
| meld_emotion = str(row.get("Emotion", "")).lower() |
| speaker = str(row.get("Speaker", "")) |
| |
| if not utterance or pd.isna(row.get("Emotion")): |
| continue |
| |
| |
| pred_emotion = predict_single_emotion(utterance) |
| |
| |
| gold_plutchik = map_meld_to_plutchik(meld_emotion) |
| |
| |
| match = "Yes" if pred_emotion == gold_plutchik else "No" |
| |
| rows.append({ |
| "Utterance": utterance[:100] + "..." if len(utterance) > 100 else utterance, |
| "Speaker": speaker, |
| "MELD_Emotion": meld_emotion.capitalize(), |
| "Gold_Plutchik": gold_plutchik.capitalize(), |
| "Predicted_Plutchik": pred_emotion.capitalize(), |
| "Match": match |
| }) |
| |
| count += 1 |
| |
| return pd.DataFrame(rows) |
| |
| except Exception as e: |
| import traceback |
| return pd.DataFrame({ |
| "Error": [f"Failed to evaluate: {str(e)}\n{traceback.format_exc()}"] |
| }) |
|
|
| |
| with gr.Blocks(title="Emotion Detection System", theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# π Emotion Detection System") |
| gr.Markdown("Analyze emotions in conversations using Plutchik's emotion model") |
| |
| with gr.Tabs(): |
| with gr.Tab("π¬ Emotion Detection"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| gr.Markdown("## Input Conversation") |
| conversation_input = gr.Textbox( |
| label="Enter conversation (format: Speaker: utterance)", |
| placeholder="Customer: I received a notice saying my premium is increasing\nSalesperson: I'm sorry for the confusion, let me check what caused the change\nCustomer: It feels unfair because I haven't filed any claims", |
| lines=8, |
| value="Customer: I received a notice saying my premium is increasing, and no one explained why\nSalesperson: I'm sorry for the confusion, let me check what caused the change\nCustomer: It feels unfair because I haven't filed any claims\nSalesperson: I understand why that would be upsetting, and I want to clarify it for you\nCustomer: I'm concerned this might keep happening every year\nSalesperson: We can review options that offer more predictable pricing\nCustomer: If there's a way to avoid surprises, I'd be willing to consider it\nSalesperson: That makes sense, and I'll help you find a plan that gives you peace of mind" |
| ) |
| |
| analyze_btn = gr.Button("π Analyze Emotions", variant="primary", size="lg") |
| |
| gr.Markdown("### π Format Instructions") |
| gr.Markdown(""" |
| - Each line should be: `Speaker: utterance` |
| - Multiple speakers supported |
| - One utterance per line |
| - Example: `John: I'm really excited about this!` |
| """) |
| |
| with gr.Column(scale=1): |
| gr.Markdown("## Emotion Analysis Results") |
| output_display = gr.Textbox( |
| label="Emotion Analysis Results", |
| lines=20, |
| interactive=False |
| ) |
| |
| |
| gr.Markdown("## π‘ Example Conversations") |
| |
| with gr.Row(): |
| example1 = gr.Button("Customer Service Example", size="sm") |
| example2 = gr.Button("Team Discussion Example", size="sm") |
| example3 = gr.Button("Personal Conversation Example", size="sm") |
| |
| |
| example_conversations = { |
| "Customer Service Example": "Customer: I received a notice saying my premium is increasing, and no one explained why\nSalesperson: I'm sorry for the confusion, let me check what caused the change\nCustomer: It feels unfair because I haven't filed any claims\nSalesperson: I understand why that would be upsetting, and I want to clarify it for you", |
| |
| "Team Discussion Example": "Manager: We need to finish this project by Friday\nTeam Member: I'm not sure we can meet that deadline\nManager: What challenges are you seeing?\nTeam Member: We're still waiting for the design approval\nManager: I'll follow up with the design team right away", |
| |
| "Personal Conversation Example": "Friend: I got the job offer yesterday!\nYou: That's amazing news! Congratulations!\nFriend: I'm so excited but also a bit nervous\nYou: You'll do great, you were the best candidate" |
| } |
| |
| |
| analyze_btn.click( |
| fn=detect_emotions, |
| inputs=conversation_input, |
| outputs=output_display |
| ) |
| |
| def load_example(example_name): |
| return example_conversations[example_name] |
| |
| example1.click(fn=lambda: load_example("Customer Service Example"), outputs=conversation_input) |
| example2.click(fn=lambda: load_example("Team Discussion Example"), outputs=conversation_input) |
| example3.click(fn=lambda: load_example("Personal Conversation Example"), outputs=conversation_input) |
| |
| with gr.Tab("π Evaluation (MELD)"): |
| gr.Markdown("### Evaluate model on MELD dataset") |
| gr.Markdown("Uses LLM to map MELD emotions to Plutchik emotions and compares with predictions") |
| gr.Markdown("**MELD Emotions:** anger, disgust, fear, joy, neutral, sadness, surprise") |
| |
| sample_slider = gr.Slider( |
| minimum=10, |
| maximum=200, |
| value=50, |
| step=10, |
| label="Number of Samples" |
| ) |
| |
| eval_btn = gr.Button("π Run Evaluation", variant="primary", size="lg") |
| |
| eval_output = gr.Dataframe( |
| label="Evaluation Results", |
| wrap=True |
| ) |
|
|
| eval_btn.click( |
| fn=evaluate_meld_dataset, |
| inputs=sample_slider, |
| outputs=eval_output |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch(debug=True) |