joey1101 commited on
Commit
c39c802
·
verified ·
1 Parent(s): 828c881

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -161
app.py CHANGED
@@ -2,177 +2,109 @@
2
  # Step 0: Import required libraries
3
  ##########################################
4
  import streamlit as st # For building the web application interface
5
- from transformers import ( # For text classification, text-to-speech, and text generation
6
  pipeline,
7
  SpeechT5Processor,
8
  SpeechT5ForTextToSpeech,
9
  SpeechT5HifiGan,
10
  AutoModelForCausalLM,
11
  AutoTokenizer
12
- )
13
- from datasets import load_dataset # For loading speaker embeddings dataset
14
  import torch # For tensor operations
15
  import soundfile as sf # For saving audio as .wav files
16
  import sentencepiece # Required by SpeechT5Processor for tokenization
17
 
 
18
  ##########################################
19
  # Streamlit application title and input
20
  ##########################################
21
- # Display a deep blue title using HTML and CSS
22
  st.markdown(
23
- "<h1 style='text-align: center; color: #00008B; font-size: 50px;'>Just Comment</h1>",
24
  unsafe_allow_html=True
25
- ) # Set the title in deep blue
26
 
27
  # Display a gentle, warm subtitle below the title
28
  st.markdown(
29
- "<h3 style='text-align: center; color: #5D6D7E; font-style: italic;'>I'm listening to you, my friend</h3>",
30
  unsafe_allow_html=True
31
- ) # Set the subtitle with warm styling
32
 
33
- # Provide a text area for user input with a placeholder and tooltip
34
  text = st.text_area(
35
  "Enter your comment",
36
  placeholder="Type something here...",
37
  height=100,
38
- help="Write a comment you would like us to respond to!" # Tooltip for guidance
39
- ) # Create the text input field
 
 
40
 
41
  ##########################################
42
  # Step 1: Sentiment Analysis Function
43
  ##########################################
44
  def analyze_dominant_emotion(user_review):
45
  """
46
- Analyze the dominant emotion in the user's comment using a fine-tuned text classification model.
47
  """
48
- # Load the fine-tuned sentiment classification model from Hugging Face
49
  emotion_classifier = pipeline(
50
- "text-classification",
51
- model="Thea231/jhartmann_emotion_finetuning",
52
  return_all_scores=True
53
- )
54
- # Get sentiment scores for the input text
55
- emotion_results = emotion_classifier(user_review)[0]
56
- # Identify the emotion with the highest confidence score
57
- dominant_emotion = max(emotion_results, key=lambda x: x['score'])
58
- return dominant_emotion # Return the dominant emotion as a dictionary
59
 
 
 
 
 
 
60
  ##########################################
61
- # Step 2: Response Generation Functions
62
  ##########################################
63
- def prompt_gen(user_review):
64
- """
65
- Generate the text generation prompt based on the user's comment and detected emotion.
66
- """
67
- # Determine the dominant emotion from the user's comment
68
- dominant_emotion = analyze_dominant_emotion(user_review)
69
- # Define prompt templates for seven emotions
70
- emotion_strategies = {
71
- "anger": {
72
- "prompt": (
73
- "Customer complaint: '{review}'\n\n"
74
- "As a customer service representative, craft a professional response that:\n"
75
- "- Begins with a sincere apology and acknowledgment.\n"
76
- "- Clearly explains a solution process with concrete steps.\n"
77
- "- Offers appropriate compensation or redemption.\n"
78
- "- Keeps a humble, solution-focused tone (1-3 sentences).\n\n"
79
- "Response:"
80
- )
81
- },
82
- "disgust": {
83
- "prompt": (
84
- "Customer quality concern: '{review}'\n\n"
85
- "As a customer service representative, craft a response that:\n"
86
- "- Immediately acknowledges the product issue.\n"
87
- "- Explains quality control measures being taken.\n"
88
- "- Provides clear return/replacement instructions.\n"
89
- "- Offers a goodwill gesture (1-3 sentences).\n\n"
90
- "Response:"
91
- )
92
- },
93
- "fear": {
94
- "prompt": (
95
- "Customer safety concern: '{review}'\n\n"
96
- "As a customer service representative, craft a reassuring response that:\n"
97
- "- Directly addresses safety worries.\n"
98
- "- References relevant certifications or standards.\n"
99
- "- Offers dedicated support contact.\n"
100
- "- Provides a satisfaction guarantee (1-3 sentences).\n\n"
101
- "Response:"
102
- )
103
- },
104
- "joy": {
105
- "prompt": (
106
- "Customer review: '{review}'\n\n"
107
- "As a customer service representative, craft a concise response that:\n"
108
- "- Thanks the customer for their feedback.\n"
109
- "- Acknowledges both positive and constructive points.\n"
110
- "- Invites exploration of loyalty or referral programs (1-3 sentences).\n\n"
111
- "Response:"
112
- )
113
- },
114
- "neutral": {
115
- "prompt": (
116
- "Customer feedback: '{review}'\n\n"
117
- "As a customer service representative, craft a balanced response that:\n"
118
- "- Provides relevant product information.\n"
119
- "- Highlights key service features.\n"
120
- "- Politely requests detailed feedback.\n"
121
- "- Maintains a professional tone (1-3 sentences).\n\n"
122
- "Response:"
123
- )
124
- },
125
- "sadness": {
126
- "prompt": (
127
- "Customer disappointment: '{review}'\n\n"
128
- "As a customer service representative, craft an empathetic response that:\n"
129
- "- Shows genuine understanding of the issue.\n"
130
- "- Proposes a personalized recovery solution.\n"
131
- "- Offers extended support options.\n"
132
- "- Maintains a positive outlook (1-3 sentences).\n\n"
133
- "Response:"
134
- )
135
- },
136
- "surprise": {
137
- "prompt": (
138
- "Customer enthusiastic feedback: '{review}'\n\n"
139
- "As a customer service representative, craft a response that:\n"
140
- "- Matches the customer's positive energy.\n"
141
- "- Highlights unexpected product benefits.\n"
142
- "- Invites the customer to join community events.\n"
143
- "- Maintains the brand's voice (1-3 sentences).\n\n"
144
- "Response:"
145
- )
146
- }
147
- } # End dictionary of prompt templates
148
- # Select the template based on detected emotion; default to neutral if not found
149
- template = emotion_strategies.get(dominant_emotion["label"].lower(), emotion_strategies["neutral"])["prompt"]
150
- prompt = template.format(review=user_review) # Format the prompt with the user's comment
151
- print(f"Generated prompt: {prompt}") # Debug: print the generated prompt using an f-string
152
- return prompt # Return the text generation prompt
153
-
154
  def response_gen(user_review):
155
  """
156
- Generate a response using text generation based on the user's comment and detected emotion.
157
  """
158
- # Get the text generation prompt based on the user's comment and its dominant emotion
159
- prompt = prompt_gen(user_review)
160
- # Load the tokenizer and language model for text generation
161
- tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B") # Load tokenizer
162
- model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen1.5-0.5B") # Load causal language model
163
- inputs = tokenizer(prompt, return_tensors="pt") # Tokenize the prompt
164
- # Generate a response with constraints to ensure a concise and complete answer
165
- outputs = model.generate(
166
- **inputs,
167
- max_new_tokens=100, # Allow up to 100 new tokens for the generated answer
168
- min_length=30, # Ensure at least 30 tokens in the generated response
169
- no_repeat_ngram_size=2, # Avoid repeated phrases
170
- temperature=0.7 # Moderate randomness for creative responses
171
- )
172
- input_length = inputs.input_ids.shape[1] # Get the length of the input prompt
173
- # Decode only the generated text (after the prompt)
174
- response = tokenizer.decode(outputs[0][input_length:], skip_special_tokens=True)
175
- print(f"Generated response: {response}") # Debug: print the generated response using an f-string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  return response # Return the generated response
177
 
178
  ##########################################
@@ -180,42 +112,37 @@ def response_gen(user_review):
180
  ##########################################
181
  def sound_gen(response):
182
  """
183
- Convert the generated response to speech and embed an auto-playing audio player.
184
  """
185
- # Load the SpeechT5 processor, TTS model, and vocoder for audio synthesis
186
- processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts") # Load TTS processor
187
- model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts") # Load TTS model
188
- vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan") # Load vocoder
189
- # Process the entire generated response text for TTS
190
- inputs = processor(text=response, return_tensors="pt") # Tokenize and process the response
191
- # Create a dummy speaker embedding with the expected dimension (1 x 1280)
192
- speaker_embeddings = torch.zeros(1, 1280, dtype=torch.float32) # Dummy embedding to avoid shape mismatches
193
- # Generate the speech spectrogram using the input tokens and dummy speaker embeddings
194
- spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings)
195
- with torch.no_grad():
 
196
  speech = vocoder(spectrogram) # Convert the spectrogram into an audio waveform
197
- # Save the audio waveform as a .wav file with a 16kHz sampling rate
198
- sf.write("customer_service_response.wav", speech.numpy(), samplerate=16000)
199
- # Embed an auto-playing audio player in the app to play the full response
200
- st.audio("customer_service_response.wav", start_time=0)
201
 
202
  ##########################################
203
  # Main Function
204
  ##########################################
205
  def main():
206
  """
207
- Main function to orchestrate text generation and text-to-speech conversion.
208
- It displays only the generated response and plays its audio without extra information.
209
  """
210
- if text: # Only proceed if the user has entered a comment
211
- response = response_gen(text) # Generate a response based on text generation and emotion detection
212
- st.markdown(
213
- f"<p style='color:#3498DB; font-size:20px;'>{response}</p>",
214
- unsafe_allow_html=True
215
- ) # Display the response in styled formatting (only the final answer)
216
- sound_gen(response) # Convert the full generated response to speech and embed the audio player
217
- print(f"Final generated response: {response}") # Debug: print the final response using an f-string
218
-
219
- # Execute the main function when the script is run
220
  if __name__ == "__main__":
221
- main() # Call the main function
 
2
  # Step 0: Import required libraries
3
  ##########################################
4
  import streamlit as st # For building the web application interface
5
+ from transformers import (
6
  pipeline,
7
  SpeechT5Processor,
8
  SpeechT5ForTextToSpeech,
9
  SpeechT5HifiGan,
10
  AutoModelForCausalLM,
11
  AutoTokenizer
12
+ ) # For sentiment analysis, text-to-speech, and response generation
13
+ from datasets import load_dataset # For loading datasets (e.g., speaker embeddings)
14
  import torch # For tensor operations
15
  import soundfile as sf # For saving audio as .wav files
16
  import sentencepiece # Required by SpeechT5Processor for tokenization
17
 
18
+
19
  ##########################################
20
  # Streamlit application title and input
21
  ##########################################
22
+ # Display a deep blue title in a large, visually appealing font
23
  st.markdown(
24
+ "<h1 style='text-align: center; color: #00008B; font-size: 50px;'>🚀 Just Comment</h1>",
25
  unsafe_allow_html=True
26
+ ) # Set deep blue title
27
 
28
  # Display a gentle, warm subtitle below the title
29
  st.markdown(
30
+ "<h3 style='text-align: center; color: #5D6D7E; font-style: italic;'>I'm listening to you, my friend~</h3>",
31
  unsafe_allow_html=True
32
+ ) # Set a friendly subtitle
33
 
34
+ # Add a text area for user input with placeholder and tooltip
35
  text = st.text_area(
36
  "Enter your comment",
37
  placeholder="Type something here...",
38
  height=100,
39
+ help="Write a comment you would like us to respond to!" # Provide tooltip
40
+ ) # Create text input field
41
+
42
+
43
 
44
  ##########################################
45
  # Step 1: Sentiment Analysis Function
46
  ##########################################
47
  def analyze_dominant_emotion(user_review):
48
  """
49
+ Analyze the dominant emotion in the user's review using our fine-tuned sentiment analysis model.
50
  """
 
51
  emotion_classifier = pipeline(
52
+ "text-classification",
53
+ model="Thea231/jhartmann_emotion_finetuning",
54
  return_all_scores=True
55
+ ) # Load our fine-tuned sentiment analysis model from Hugging Face
 
 
 
 
 
56
 
57
+ emotion_results = emotion_classifier(user_review)[0] # Perform sentiment analysis on the user input
58
+ dominant_emotion = max(emotion_results, key=lambda x: x['score']) # Extract the emotion with the highest confidence score
59
+ return dominant_emotion # Return the dominant emotion with its label and score
60
+
61
+
62
  ##########################################
63
+ # Step 2: Response Generation Function
64
  ##########################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  def response_gen(user_review):
66
  """
67
+ Generate a logical and complete response based on the sentiment of the user's review.
68
  """
69
+ dominant_emotion = analyze_dominant_emotion(user_review) # Identify the dominant emotion from the user's review
70
+ emotion_label = dominant_emotion['label'].lower() # Extract the emotion label and convert it to lowercase
71
+
72
+ # Define response templates tailored to each emotion
73
+ emotion_prompts = {
74
+ "anger": (
75
+ f"Customer complaint: '{user_review}'\n\n"
76
+ "As a customer service representative, write a response that:\n"
77
+ "- Sincerely apologizes for the issue\n"
78
+ "- Explains how the issue will be resolved\n"
79
+ "- Offers compensation where appropriate\n\n"
80
+ "Response:"
81
+ ),
82
+ "joy": (
83
+ f"Customer review: '{user_review}'\n\n"
84
+ "As a customer service representative, write a positive response that:\n"
85
+ "- Thanks the customer for their feedback\n"
86
+ "- Acknowledges both positive and constructive comments\n"
87
+ "- Invites them to explore loyalty programs\n\n"
88
+ "Response:"
89
+ ),
90
+ # Add other emotions (e.g., sadness, fear) as needed
91
+ }
92
+
93
+ # Select the appropriate prompt template based on the detected emotion
94
+ prompt = emotion_prompts.get(emotion_label, f"Neutral feedback: '{user_review}'\n\nProvide a professional response.")
95
+
96
+ # Load a small text generation model for generating concise, logical responses
97
+ tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B") # Load a tokenizer for processing the prompt
98
+ model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen1.5-0.5B") # Load the language model for generating text
99
+
100
+ inputs = tokenizer(prompt, return_tensors="pt") # Tokenize the input prompt
101
+ outputs = model.generate(**inputs, max_new_tokens=100) # Generate a response with a limit on the number of tokens
102
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True) # Decode the generated response to text
103
+
104
+ # Ensure the response length falls within the desired range (50-200 words)
105
+ if len(response.split()) < 50 or len(response.split()) > 200:
106
+ 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
107
+
108
  return response # Return the generated response
109
 
110
  ##########################################
 
112
  ##########################################
113
  def sound_gen(response):
114
  """
115
+ Convert the generated text response to a speech file and save it locally.
116
  """
117
+ processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts") # Load the processor for the TTS model
118
+ model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts") # Load the text-to-speech model
119
+ vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan") # Load the vocoder model for audio synthesis
120
+
121
+ # Load speaker embeddings for generating the audio (neutral female voice)
122
+ embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation") # Load speaker embeddings dataset
123
+ speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0) # Select a sample embedding
124
+
125
+ inputs = processor(text=response, return_tensors="pt") # Convert the text response into processor-compatible format
126
+ spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings) # Generate speech as a spectrogram
127
+
128
+ with torch.no_grad(): # Disable gradient computation for audio generation
129
  speech = vocoder(spectrogram) # Convert the spectrogram into an audio waveform
130
+
131
+ sf.write("customer_service_response.wav", speech.numpy(), samplerate=16000) # Save the audio as a .wav file
132
+ st.audio("customer_service_response.wav") # Allow users to play the generated audio in the app
 
133
 
134
  ##########################################
135
  # Main Function
136
  ##########################################
137
  def main():
138
  """
139
+ Main function to combine sentiment analysis, response generation, and text-to-speech functionality.
 
140
  """
141
+ if text: # Check if the user has entered a comment in the text area
142
+ response = response_gen(text) # Generate an automated response based on the input comment
143
+ st.write(f"Generated response: {response}") # Display the generated response in the app
144
+ sound_gen(response) # Convert the text response to speech and make it available for playback
145
+
146
+ # Run the main function when the script is executed
 
 
 
 
147
  if __name__ == "__main__":
148
+ main()