Zasha1 commited on
Commit
2e87ddf
·
verified ·
1 Parent(s): 6495c45

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -202
app.py CHANGED
@@ -15,75 +15,78 @@ from io import BytesIO
15
  import wave
16
 
17
  # Initialize components
18
- objection_handler = ObjectionHandler('objections.csv')
19
- product_recommender = ProductRecommender('recommendations.csv')
20
  model = SentenceTransformer('all-MiniLM-L6-v2')
21
 
22
- def real_time_analysis():
23
- st.info("Listening... Say 'stop' to end the process.")
24
-
25
- def audio_frame_callback(audio_frame):
26
- # Convert audio frame to bytes
27
- audio_bytes = audio_frame.to_ndarray().tobytes()
28
-
29
- # Save audio bytes to a temporary WAV file
30
- with BytesIO() as wav_buffer:
31
- with wave.open(wav_buffer, 'wb') as wav_file:
32
- wav_file.setnchannels(1) # Mono audio
33
- wav_file.setsampwidth(2) # 2 bytes for int16
34
- wav_file.setframerate(16000) # Sample rate
35
- wav_file.writeframes(audio_bytes)
36
-
37
- # Transcribe the audio
38
- text = transcribe_audio(wav_buffer.getvalue())
39
- if text:
40
- st.write(f"*Recognized Text:* {text}")
41
-
42
- # Analyze sentiment
43
- sentiment, score = analyze_sentiment(text)
44
- st.write(f"*Sentiment:* {sentiment} (Score: {score})")
45
-
46
- # Handle objection
47
- objection_response = handle_objection(text)
48
- st.write(f"*Objection Response:* {objection_response}")
49
-
50
- # Get product recommendation
51
- recommendations = []
52
- if is_valid_input(text) and is_relevant_sentiment(score):
53
- query_embedding = model.encode([text])
54
- distances, indices = product_recommender.index.search(query_embedding, 1)
55
-
56
- if distances[0][0] < 1.5: # Similarity threshold
57
- recommendations = product_recommender.get_recommendations(text)
58
-
59
- if recommendations:
60
- st.write("*Product Recommendations:*")
61
- for rec in recommendations:
62
- st.write(rec)
63
-
64
- return audio_frame
65
-
66
- # Start WebRTC audio stream
67
- webrtc_ctx = webrtc_streamer(
68
- key="real-time-audio",
69
- mode=WebRtcMode.SENDONLY,
70
- audio_frame_callback=audio_frame_callback,
71
- media_stream_constraints={"audio": True, "video": False},
72
- )
73
-
74
- def transcribe_audio(audio_bytes):
75
- """Transcribe audio using a speech-to-text model or API."""
76
- # Replace this with your actual speech-to-text implementation
77
- # For now, we'll just return a dummy text
78
- return "This is a placeholder transcription."
79
-
80
- def handle_objection(text):
81
- query_embedding = model.encode([text])
82
- distances, indices = objection_handler.index.search(query_embedding, 1)
83
- if distances[0][0] < 1.5: # Adjust similarity threshold as needed
84
- responses = objection_handler.handle_objection(text)
85
- return "\n".join(responses) if responses else "No objection response found."
86
- return "No objection response found."
 
 
 
87
 
88
  def is_valid_input(text):
89
  text = text.strip().lower()
@@ -94,109 +97,25 @@ def is_valid_input(text):
94
  def is_relevant_sentiment(sentiment_score):
95
  return sentiment_score > 0.4
96
 
97
- def run_app():
98
- st.set_page_config(page_title="Sales Call Assistant", layout="wide")
99
- st.title("AI Sales Call Assistant")
100
-
101
- st.sidebar.title("Navigation")
102
- app_mode = st.sidebar.radio("Choose a mode:", ["Real-Time Call Analysis", "Dashboard"])
103
-
104
- if app_mode == "Real-Time Call Analysis":
105
- st.header("Real-Time Sales Call Analysis")
106
- real_time_analysis()
107
-
108
- elif app_mode == "Dashboard":
109
- st.header("Call Summaries and Sentiment Analysis")
110
- try:
111
- data = fetch_call_data(config["google_sheet_id"])
112
- if data.empty:
113
- st.warning("No data available in the Google Sheet.")
114
- else:
115
- sentiment_counts = data['Sentiment'].value_counts()
116
-
117
- col1, col2 = st.columns(2)
118
- with col1:
119
- st.subheader("Sentiment Distribution")
120
- fig_pie = px.pie(
121
- values=sentiment_counts.values,
122
- names=sentiment_counts.index,
123
- title='Call Sentiment Breakdown',
124
- color_discrete_map={
125
- 'POSITIVE': 'green',
126
- 'NEGATIVE': 'red',
127
- 'NEUTRAL': 'blue'
128
- }
129
- )
130
- st.plotly_chart(fig_pie)
131
-
132
- with col2:
133
- st.subheader("Sentiment Counts")
134
- fig_bar = px.bar(
135
- x=sentiment_counts.index,
136
- y=sentiment_counts.values,
137
- title='Number of Calls by Sentiment',
138
- labels={'x': 'Sentiment', 'y': 'Number of Calls'},
139
- color=sentiment_counts.index,
140
- color_discrete_map={
141
- 'POSITIVE': 'green',
142
- 'NEGATIVE': 'red',
143
- 'NEUTRAL': 'blue'
144
- }
145
- )
146
- st.plotly_chart(fig_bar)
147
-
148
- st.subheader("All Calls")
149
- display_data = data.copy()
150
- display_data['Summary Preview'] = display_data['Summary'].str[:100] + '...'
151
- st.dataframe(display_data[['Call ID', 'Chunk', 'Sentiment', 'Summary Preview', 'Overall Sentiment']])
152
 
153
- unique_call_ids = data[data['Call ID'] != '']['Call ID'].unique()
154
- call_id = st.selectbox("Select a Call ID to view details:", unique_call_ids)
155
-
156
- call_details = data[data['Call ID'] == call_id]
157
- if not call_details.empty:
158
- st.subheader("Detailed Call Information")
159
- st.write(f"**Call ID:** {call_id}")
160
- st.write(f"**Overall Sentiment:** {call_details.iloc[0]['Overall Sentiment']}")
161
-
162
- st.subheader("Full Call Summary")
163
- st.text_area("Summary:",
164
- value=call_details.iloc[0]['Summary'],
165
- height=200,
166
- disabled=True)
167
-
168
- st.subheader("Conversation Chunks")
169
- for _, row in call_details.iterrows():
170
- if pd.notna(row['Chunk']):
171
- st.write(f"**Chunk:** {row['Chunk']}")
172
- st.write(f"**Sentiment:** {row['Sentiment']}")
173
- st.write("---")
174
- else:
175
- st.error("No details available for the selected Call ID.")
176
- except Exception as e:
177
- st.error(f"Error loading dashboard: {e}")
178
-
179
- if __name__ == "__main__":
180
- run_app()from streamlit_webrtc import webrtc_streamer, WebRtcMode
181
- from sentiment_analysis import analyze_sentiment
182
- from product_recommender import ProductRecommender
183
- from objection_handler import ObjectionHandler
184
- from google_sheets import fetch_call_data, store_data_in_sheet
185
- from sentence_transformers import SentenceTransformer
186
- from env_setup import config
187
- import re
188
- import uuid
189
- import pandas as pd
190
- import plotly.express as px
191
- import streamlit as st
192
- import numpy as np
193
- from io import BytesIO
194
- import wave
195
-
196
- # Initialize components
197
- objection_handler = ObjectionHandler('objections.csv')
198
- product_recommender = ProductRecommender('recommendations.csv')
199
- model = SentenceTransformer('all-MiniLM-L6-v2')
200
 
201
  def real_time_analysis():
202
  st.info("Listening... Say 'stop' to end the process.")
@@ -256,23 +175,6 @@ def transcribe_audio(audio_bytes):
256
  # For now, we'll just return a dummy text
257
  return "This is a placeholder transcription."
258
 
259
- def handle_objection(text):
260
- query_embedding = model.encode([text])
261
- distances, indices = objection_handler.index.search(query_embedding, 1)
262
- if distances[0][0] < 1.5: # Adjust similarity threshold as needed
263
- responses = objection_handler.handle_objection(text)
264
- return "\n".join(responses) if responses else "No objection response found."
265
- return "No objection response found."
266
-
267
- def is_valid_input(text):
268
- text = text.strip().lower()
269
- if len(text) < 3 or re.match(r'^[a-zA-Z\s]*$', text) is None:
270
- return False
271
- return True
272
-
273
- def is_relevant_sentiment(sentiment_score):
274
- return sentiment_score > 0.4
275
-
276
  def run_app():
277
  st.set_page_config(page_title="Sales Call Assistant", layout="wide")
278
  st.title("AI Sales Call Assistant")
@@ -291,65 +193,73 @@ def run_app():
291
  if data.empty:
292
  st.warning("No data available in the Google Sheet.")
293
  else:
 
294
  sentiment_counts = data['Sentiment'].value_counts()
295
-
 
296
  col1, col2 = st.columns(2)
297
  with col1:
298
  st.subheader("Sentiment Distribution")
299
  fig_pie = px.pie(
300
- values=sentiment_counts.values,
301
- names=sentiment_counts.index,
302
  title='Call Sentiment Breakdown',
303
  color_discrete_map={
304
- 'POSITIVE': 'green',
305
- 'NEGATIVE': 'red',
306
  'NEUTRAL': 'blue'
307
  }
308
  )
309
  st.plotly_chart(fig_pie)
310
 
 
311
  with col2:
312
  st.subheader("Sentiment Counts")
313
  fig_bar = px.bar(
314
- x=sentiment_counts.index,
315
- y=sentiment_counts.values,
316
  title='Number of Calls by Sentiment',
317
  labels={'x': 'Sentiment', 'y': 'Number of Calls'},
318
  color=sentiment_counts.index,
319
  color_discrete_map={
320
- 'POSITIVE': 'green',
321
- 'NEGATIVE': 'red',
322
  'NEUTRAL': 'blue'
323
  }
324
  )
325
  st.plotly_chart(fig_bar)
326
 
 
327
  st.subheader("All Calls")
328
  display_data = data.copy()
329
  display_data['Summary Preview'] = display_data['Summary'].str[:100] + '...'
330
  st.dataframe(display_data[['Call ID', 'Chunk', 'Sentiment', 'Summary Preview', 'Overall Sentiment']])
331
 
 
332
  unique_call_ids = data[data['Call ID'] != '']['Call ID'].unique()
333
  call_id = st.selectbox("Select a Call ID to view details:", unique_call_ids)
334
 
 
335
  call_details = data[data['Call ID'] == call_id]
336
  if not call_details.empty:
337
  st.subheader("Detailed Call Information")
338
  st.write(f"**Call ID:** {call_id}")
339
  st.write(f"**Overall Sentiment:** {call_details.iloc[0]['Overall Sentiment']}")
340
-
 
341
  st.subheader("Full Call Summary")
342
- st.text_area("Summary:",
343
- value=call_details.iloc[0]['Summary'],
344
- height=200,
345
  disabled=True)
346
-
 
347
  st.subheader("Conversation Chunks")
348
  for _, row in call_details.iterrows():
349
- if pd.notna(row['Chunk']):
350
  st.write(f"**Chunk:** {row['Chunk']}")
351
  st.write(f"**Sentiment:** {row['Sentiment']}")
352
- st.write("---")
353
  else:
354
  st.error("No details available for the selected Call ID.")
355
  except Exception as e:
 
15
  import wave
16
 
17
  # Initialize components
18
+ product_recommender = ProductRecommender(r"C:\Users\shaik\Downloads\Sales Calls Transcriptions - Sheet2.csv")
19
+ objection_handler = ObjectionHandler(r"C:\Users\shaik\Downloads\Sales Calls Transcriptions - Sheet3.csv")
20
  model = SentenceTransformer('all-MiniLM-L6-v2')
21
 
22
+ def generate_comprehensive_summary(chunks):
23
+ """
24
+ Generate a comprehensive summary from conversation chunks
25
+ """
26
+ # Extract full text from chunks
27
+ full_text = " ".join([chunk[0] for chunk in chunks])
28
+
29
+ # Perform basic analysis
30
+ total_chunks = len(chunks)
31
+ sentiments = [chunk[1] for chunk in chunks]
32
+
33
+ # Determine overall conversation context
34
+ context_keywords = {
35
+ 'product_inquiry': ['dress', 'product', 'price', 'stock'],
36
+ 'pricing': ['cost', 'price', 'budget'],
37
+ 'negotiation': ['installment', 'payment', 'manage']
38
+ }
39
+
40
+ # Detect conversation themes
41
+ themes = []
42
+ for keyword_type, keywords in context_keywords.items():
43
+ if any(keyword.lower() in full_text.lower() for keyword in keywords):
44
+ themes.append(keyword_type)
45
+
46
+ # Basic sentiment analysis
47
+ positive_count = sentiments.count('POSITIVE')
48
+ negative_count = sentiments.count('NEGATIVE')
49
+ neutral_count = sentiments.count('NEUTRAL')
50
+
51
+ # Key interaction highlights
52
+ key_interactions = []
53
+ for chunk in chunks:
54
+ if any(keyword.lower() in chunk[0].lower() for keyword in ['price', 'dress', 'stock', 'installment']):
55
+ key_interactions.append(chunk[0])
56
+
57
+ # Construct summary
58
+ summary = f"Conversation Summary:\n"
59
+
60
+ # Context and themes
61
+ if 'product_inquiry' in themes:
62
+ summary += "• Customer initiated a product inquiry about items.\n"
63
+
64
+ if 'pricing' in themes:
65
+ summary += "• Price and budget considerations were discussed.\n"
66
+
67
+ if 'negotiation' in themes:
68
+ summary += "• Customer and seller explored flexible payment options.\n"
69
+
70
+ # Sentiment insights
71
+ summary += f"\nConversation Sentiment:\n"
72
+ summary += f"• Positive Interactions: {positive_count}\n"
73
+ summary += f"• Negative Interactions: {negative_count}\n"
74
+ summary += f"• Neutral Interactions: {neutral_count}\n"
75
+
76
+ # Key highlights
77
+ summary += "\nKey Conversation Points:\n"
78
+ for interaction in key_interactions[:3]: # Limit to top 3 key points
79
+ summary += f"• {interaction}\n"
80
+
81
+ # Conversation outcome
82
+ if positive_count > negative_count:
83
+ summary += "\nOutcome: Constructive and potentially successful interaction."
84
+ elif negative_count > positive_count:
85
+ summary += "\nOutcome: Interaction may require further follow-up."
86
+ else:
87
+ summary += "\nOutcome: Neutral interaction with potential for future engagement."
88
+
89
+ return summary
90
 
91
  def is_valid_input(text):
92
  text = text.strip().lower()
 
97
  def is_relevant_sentiment(sentiment_score):
98
  return sentiment_score > 0.4
99
 
100
+ def calculate_overall_sentiment(sentiment_scores):
101
+ if sentiment_scores:
102
+ average_sentiment = sum(sentiment_scores) / len(sentiment_scores)
103
+ overall_sentiment = (
104
+ "POSITIVE" if average_sentiment > 0 else
105
+ "NEGATIVE" if average_sentiment < 0 else
106
+ "NEUTRAL"
107
+ )
108
+ else:
109
+ overall_sentiment = "NEUTRAL"
110
+ return overall_sentiment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
+ def handle_objection(text):
113
+ query_embedding = model.encode([text])
114
+ distances, indices = objection_handler.index.search(query_embedding, 1)
115
+ if distances[0][0] < 1.5: # Adjust similarity threshold as needed
116
+ responses = objection_handler.handle_objection(text)
117
+ return "\n".join(responses) if responses else "No objection response found."
118
+ return "No objection response found."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  def real_time_analysis():
121
  st.info("Listening... Say 'stop' to end the process.")
 
175
  # For now, we'll just return a dummy text
176
  return "This is a placeholder transcription."
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  def run_app():
179
  st.set_page_config(page_title="Sales Call Assistant", layout="wide")
180
  st.title("AI Sales Call Assistant")
 
193
  if data.empty:
194
  st.warning("No data available in the Google Sheet.")
195
  else:
196
+ # Sentiment Visualizations
197
  sentiment_counts = data['Sentiment'].value_counts()
198
+
199
+ # Pie Chart
200
  col1, col2 = st.columns(2)
201
  with col1:
202
  st.subheader("Sentiment Distribution")
203
  fig_pie = px.pie(
204
+ values=sentiment_counts.values,
205
+ names=sentiment_counts.index,
206
  title='Call Sentiment Breakdown',
207
  color_discrete_map={
208
+ 'POSITIVE': 'green',
209
+ 'NEGATIVE': 'red',
210
  'NEUTRAL': 'blue'
211
  }
212
  )
213
  st.plotly_chart(fig_pie)
214
 
215
+ # Bar Chart
216
  with col2:
217
  st.subheader("Sentiment Counts")
218
  fig_bar = px.bar(
219
+ x=sentiment_counts.index,
220
+ y=sentiment_counts.values,
221
  title='Number of Calls by Sentiment',
222
  labels={'x': 'Sentiment', 'y': 'Number of Calls'},
223
  color=sentiment_counts.index,
224
  color_discrete_map={
225
+ 'POSITIVE': 'green',
226
+ 'NEGATIVE': 'red',
227
  'NEUTRAL': 'blue'
228
  }
229
  )
230
  st.plotly_chart(fig_bar)
231
 
232
+ # Existing Call Details Section
233
  st.subheader("All Calls")
234
  display_data = data.copy()
235
  display_data['Summary Preview'] = display_data['Summary'].str[:100] + '...'
236
  st.dataframe(display_data[['Call ID', 'Chunk', 'Sentiment', 'Summary Preview', 'Overall Sentiment']])
237
 
238
+ # Dropdown to select Call ID
239
  unique_call_ids = data[data['Call ID'] != '']['Call ID'].unique()
240
  call_id = st.selectbox("Select a Call ID to view details:", unique_call_ids)
241
 
242
+ # Display selected Call ID details
243
  call_details = data[data['Call ID'] == call_id]
244
  if not call_details.empty:
245
  st.subheader("Detailed Call Information")
246
  st.write(f"**Call ID:** {call_id}")
247
  st.write(f"**Overall Sentiment:** {call_details.iloc[0]['Overall Sentiment']}")
248
+
249
+ # Expand summary section
250
  st.subheader("Full Call Summary")
251
+ st.text_area("Summary:",
252
+ value=call_details.iloc[0]['Summary'],
253
+ height=200,
254
  disabled=True)
255
+
256
+ # Show all chunks for the selected call
257
  st.subheader("Conversation Chunks")
258
  for _, row in call_details.iterrows():
259
+ if pd.notna(row['Chunk']):
260
  st.write(f"**Chunk:** {row['Chunk']}")
261
  st.write(f"**Sentiment:** {row['Sentiment']}")
262
+ st.write("---") # Separator between chunks
263
  else:
264
  st.error("No details available for the selected Call ID.")
265
  except Exception as e: