File size: 7,203 Bytes
152d61c
 
 
 
c39c802
152d61c
 
 
 
 
 
c39c802
 
152d61c
 
 
 
c39c802
152d61c
 
 
c39c802
152d61c
c39c802
152d61c
c39c802
152d61c
b70e6a4
152d61c
c39c802
152d61c
c39c802
152d61c
c39c802
152d61c
 
 
d090d86
c39c802
 
 
 
152d61c
 
f26186a
152d61c
 
 
c39c802
152d61c
 
c39c802
 
152d61c
c39c802
2253128
c39c802
 
 
 
 
152d61c
c39c802
152d61c
 
b70e6a4
c39c802
b70e6a4
c39c802
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f26186a
152d61c
 
 
 
 
 
c39c802
152d61c
c39c802
 
 
 
 
 
 
 
 
 
 
 
828c881
c39c802
 
 
152d61c
 
 
 
 
 
c39c802
152d61c
c39c802
 
 
 
 
 
152d61c
c39c802
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
##########################################
# Step 0: Import required libraries
##########################################
import streamlit as st  # For building the web application interface
from transformers import (
    pipeline,
    SpeechT5Processor,
    SpeechT5ForTextToSpeech,
    SpeechT5HifiGan,
    AutoModelForCausalLM,
    AutoTokenizer
)  # For sentiment analysis, text-to-speech, and response generation
from datasets import load_dataset  # For loading datasets (e.g., speaker embeddings)
import torch  # For tensor operations
import soundfile as sf  # For saving audio as .wav files
import sentencepiece  # Required by SpeechT5Processor for tokenization


##########################################
# Streamlit application title and input
##########################################
# Display a deep blue title in a large, visually appealing font
st.markdown(
    "<h1 style='text-align: center; color: #00008B; font-size: 50px;'>🚀 Just Comment</h1>",
    unsafe_allow_html=True
)  # Set deep blue title

# Display a gentle, warm subtitle below the title
st.markdown(
    "<h3 style='text-align: center; color: #5D6D7E; font-style: italic;'>I'm listening to you, my friend~</h3>",
    unsafe_allow_html=True
)  # Set a friendly subtitle

# Add a text area for user input with placeholder and tooltip
text = st.text_area(
    "Enter your comment",
    placeholder="Type something here...",
    height=100,
    help="Write a comment you would like us to respond to!"  # Provide tooltip
)  # Create text input field



##########################################
# Step 1: Sentiment Analysis Function
##########################################
def analyze_dominant_emotion(user_review):
    """
    Analyze the dominant emotion in the user's review using our fine-tuned sentiment analysis model.
    """
    emotion_classifier = pipeline(
        "text-classification", 
        model="Thea231/jhartmann_emotion_finetuning", 
        return_all_scores=True
    )  # Load our fine-tuned sentiment analysis model from Hugging Face

    emotion_results = emotion_classifier(user_review)[0]  # Perform sentiment analysis on the user input
    dominant_emotion = max(emotion_results, key=lambda x: x['score'])  # Extract the emotion with the highest confidence score
    return dominant_emotion  # Return the dominant emotion with its label and score

    
##########################################
# Step 2: Response Generation Function
##########################################
def response_gen(user_review):
    """
    Generate a logical and complete response based on the sentiment of the user's review.
    """
    dominant_emotion = analyze_dominant_emotion(user_review)  # Identify the dominant emotion from the user's review
    emotion_label = dominant_emotion['label'].lower()  # Extract the emotion label and convert it to lowercase

    # Define response templates tailored to each emotion
    emotion_prompts = {
        "anger": (
            f"Customer complaint: '{user_review}'\n\n"
            "As a customer service representative, write a response that:\n"
            "- Sincerely apologizes for the issue\n"
            "- Explains how the issue will be resolved\n"
            "- Offers compensation where appropriate\n\n"
            "Response:"
        ),
        "joy": (
            f"Customer review: '{user_review}'\n\n"
            "As a customer service representative, write a positive response that:\n"
            "- Thanks the customer for their feedback\n"
            "- Acknowledges both positive and constructive comments\n"
            "- Invites them to explore loyalty programs\n\n"
            "Response:"
        ),
        # Add other emotions (e.g., sadness, fear) as needed
    }

    # Select the appropriate prompt template based on the detected emotion
    prompt = emotion_prompts.get(emotion_label, f"Neutral feedback: '{user_review}'\n\nProvide a professional response.")

    # Load a small text generation model for generating concise, logical responses
    tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B")  # Load a tokenizer for processing the prompt
    model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen1.5-0.5B")  # Load the language model for generating text

    inputs = tokenizer(prompt, return_tensors="pt")  # Tokenize the input prompt
    outputs = model.generate(**inputs, max_new_tokens=100)  # Generate a response with a limit on the number of tokens
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)  # Decode the generated response to text

    # Ensure the response length falls within the desired range (50-200 words)
    if len(response.split()) < 50 or len(response.split()) > 200:
        response = f"Dear customer, thank you for your feedback regarding '{user_review}'. We appreciate your patience and will ensure improvements based on your valuable input."  # Fallback response

    return response  # Return the generated response

##########################################
# Step 3: Text-to-Speech Conversion Function
##########################################
def sound_gen(response):
    """
    Convert the generated text response to a speech file and save it locally.
    """
    processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts")  # Load the processor for the TTS model
    model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts")  # Load the text-to-speech model
    vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")  # Load the vocoder model for audio synthesis

    # Load speaker embeddings for generating the audio (neutral female voice)
    embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")  # Load speaker embeddings dataset
    speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0)  # Select a sample embedding

    inputs = processor(text=response, return_tensors="pt")  # Convert the text response into processor-compatible format
    spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings)  # Generate speech as a spectrogram

    with torch.no_grad():  # Disable gradient computation for audio generation
        speech = vocoder(spectrogram)  # Convert the spectrogram into an audio waveform

    sf.write("customer_service_response.wav", speech.numpy(), samplerate=16000)  # Save the audio as a .wav file
    st.audio("customer_service_response.wav")  # Allow users to play the generated audio in the app

##########################################
# Main Function
##########################################
def main():
    """
    Main function to combine sentiment analysis, response generation, and text-to-speech functionality.
    """
    if text:  # Check if the user has entered a comment in the text area
        response = response_gen(text)  # Generate an automated response based on the input comment
        st.write(f"Generated response: {response}")  # Display the generated response in the app
        sound_gen(response)  # Convert the text response to speech and make it available for playback

# Run the main function when the script is executed
if __name__ == "__main__":
    main()