turanhasan commited on
Commit
1f244c9
·
verified ·
1 Parent(s): e82dfa6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +473 -98
app.py CHANGED
@@ -1,108 +1,483 @@
1
  import streamlit as st
2
- import requests
3
- from transformers import pipeline
 
 
 
 
 
4
  from reportlab.pdfgen import canvas
 
 
5
  import smtplib
6
- from email.message import EmailMessage
7
- import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- # Google Speech-to-Text API Anahtarı
10
- os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "your-google-cloud-key.json"
 
11
 
12
- # Gemini API Anahtarı
13
- GEMINI_API_KEY = "your-gemini-api-key"
 
 
 
 
 
 
 
 
14
 
15
- # Hugging Face Özetleme Modeli
16
- summary_pipeline = pipeline("summarization", model="facebook/bart-large-cnn")
17
 
18
- # Streamlit Sayfası Başlığı
19
- st.set_page_config(page_title="AI-Powered Meeting Notes & Reporting", layout="wide")
20
- st.title("📢 AI Meeting Notes & Reporting System")
 
 
 
21
 
22
- ### 📌 1️⃣ Speech-to-Text (Konuşmayı Metne Çevirme) ###
23
- def speech_to_text(audio_path):
24
- recognizer = sr.Recognizer()
25
- with sr.AudioFile(audio_path) as source:
26
- audio_data = recognizer.record(source)
27
  try:
28
- text = recognizer.recognize_google(audio_data, language="tr-TR")
29
- return text
30
- except sr.UnknownValueError:
31
- return "❌ Ses anlaşılamadı."
32
- except sr.RequestError:
33
- return "❌ Google API'ye erişilemiyor."
34
-
35
- uploaded_audio = st.file_uploader("🔊 Ses dosyanızı yükleyin (WAV formatında)", type=["wav"])
36
- if uploaded_audio:
37
- with open("temp.wav", "wb") as f:
38
- f.write(uploaded_audio.getbuffer())
39
- st.audio(uploaded_audio, format="audio/wav")
40
- transcript = speech_to_text("temp.wav")
41
- st.subheader("🎤 Metne Dönüştürülen Konuşma:")
42
- st.write(transcript)
43
-
44
- ### 📌 2️⃣ Metni Özetleme (Gemini API) ###
45
- def summarize_text(text):
46
- url = "https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateText"
47
- headers = {"Authorization": f"Bearer {GEMINI_API_KEY}"}
48
- payload = {"prompt": f"Summarize the following meeting transcript:\n{text}", "max_tokens": 500}
49
- response = requests.post(url, json=payload, headers=headers)
50
- return response.json()["choices"][0]["text"]
51
-
52
- if st.button("🔍 Toplantıyı Özetle"):
53
- if transcript:
54
- summary = summarize_text(transcript)
55
- st.subheader("📌 Toplant�� Özeti:")
56
- st.write(summary)
57
-
58
- ### 📌 3️⃣ Kararları Çıkarma (Hugging Face) ###
59
- def extract_decisions(text):
60
- summary = summary_pipeline(text, max_length=100, min_length=30, do_sample=False)
61
- return summary[0]['summary_text']
62
-
63
- if st.button("✅ Alınan Kararları Çıkar"):
64
- if transcript:
65
- decisions = extract_decisions(transcript)
66
- st.subheader("📋 Alınan Kararlar:")
67
- st.write(decisions)
68
-
69
- ### 📌 4️⃣ PDF Raporu Oluşturma ###
70
- def create_pdf(text, filename="meeting_report.pdf"):
71
- c = canvas.Canvas(filename)
72
- c.drawString(100, 750, "Meeting Report")
73
- c.drawString(100, 730, text)
74
- c.save()
75
- return filename
76
-
77
- if st.button("📄 PDF Raporu Oluştur"):
78
- if transcript:
79
- pdf_file = create_pdf(transcript)
80
- st.success("📄 Rapor oluşturuldu! Aşağıdan indirebilirsiniz.")
81
- with open(pdf_file, "rb") as f:
82
- st.download_button("📥 Raporu İndir", f, file_name="meeting_report.pdf", mime="application/pdf")
83
-
84
- ### 📌 5️⃣ E-Posta Gönderme ###
85
- def send_email(pdf_file, recipient_email):
86
- email = "[email protected]"
87
- password = "your-email-password"
88
-
89
- msg = EmailMessage()
90
- msg['Subject'] = 'Meeting Report'
91
- msg['From'] = email
92
- msg['To'] = recipient_email
93
- msg.set_content('The meeting report is attached.')
94
-
95
- with open(pdf_file, 'rb') as f:
96
- file_data = f.read()
97
- msg.add_attachment(file_data, maintype='application', subtype='pdf', filename=pdf_file)
98
-
99
- with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
100
- server.login(email, password)
101
- server.send_message(msg)
102
-
103
- email_address = st.text_input("📧 E-Posta Adresi:")
104
- if st.button("📤 Raporu Gönder"):
105
- if email_address:
106
- send_email("meeting_report.pdf", email_address)
107
- st.success("📤 E-posta başarıyla gönderildi!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import os
3
+ import google.generativeai as genai
4
+ from google.ai.generativelanguage_v1beta.types import content
5
+ import json
6
+ from tempfile import NamedTemporaryFile
7
+ from datetime import datetime
8
+ import io
9
  from reportlab.pdfgen import canvas
10
+ from reportlab.lib.pagesizes import letter
11
+ from reportlab.lib.units import inch
12
  import smtplib
13
+ from email.mime.text import MIMEText
14
+ from streamlit_mic_recorder import mic_recorder
15
+ import wave
16
+
17
+ # Install streamlit-mic-recorder if not already installed:
18
+ # pip install streamlit-mic-recorder
19
+
20
+ # Initialize session state for chat history if it doesn't exist
21
+ if 'chat_history' not in st.session_state:
22
+ st.session_state.chat_history = []
23
+
24
+ if 'diarization_output' not in st.session_state:
25
+ st.session_state.diarization_output = None
26
+
27
+ if 'uploaded_file' not in st.session_state:
28
+ st.session_state.uploaded_file = None
29
+
30
+ if 'language' not in st.session_state:
31
+ st.session_state.language = "English"
32
+
33
+ if 'num_speakers' not in st.session_state:
34
+ st.session_state.num_speakers = 2
35
+
36
+ if 'summary_output' not in st.session_state:
37
+ st.session_state.summary_output = None
38
+
39
+ if 'key_decisions_output' not in st.session_state:
40
+ st.session_state.key_decisions_output = None
41
+
42
+ if 'email_sent_message' not in st.session_state:
43
+ st.session_state.email_sent_message = ""
44
+
45
+ if 'recorded_audio' not in st.session_state:
46
+ st.session_state.recorded_audio = None
47
+
48
+
49
+ # Configuration for the page
50
+ st.set_page_config(
51
+ page_title="AI Meeting Notes & Reporting",
52
+ layout="wide"
53
+ )
54
+
55
+ # Function to generate PDF report
56
+ def generate_pdf_report(meeting_date, summary, key_decisions, transcription):
57
+ buffer = io.BytesIO()
58
+ p = canvas.Canvas(buffer, pagesize=letter)
59
+ p.setFont("Helvetica-Bold", 16)
60
+ p.drawString(inch, 10.5*inch, "Meeting Report")
61
+ p.setFont("Helvetica", 12)
62
+ p.drawString(inch, 10*inch, f"Date: {meeting_date.strftime('%Y-%m-%d')}")
63
+
64
+ y_position = 9.5*inch
65
+
66
+ p.setFont("Helvetica-Bold", 12)
67
+ p.drawString(inch, y_position, "Summary:")
68
+ y_position -= 0.3*inch
69
+ p.setFont("Helvetica", 10)
70
+ summary_lines = summary.split('\n')
71
+ for line in summary_lines:
72
+ p.drawString(inch, y_position, line)
73
+ y_position -= 0.2*inch
74
+ if y_position < 1*inch: # Simple page break to avoid content overflow - improve if needed
75
+ p.showPage()
76
+ y_position = 10.5*inch
77
+ p.setFont("Helvetica", 10)
78
+
79
+
80
+ if key_decisions:
81
+ p.setFont("Helvetica-Bold", 12)
82
+ p.drawString(inch, y_position, "Key Decisions:")
83
+ y_position -= 0.3*inch
84
+ p.setFont("Helvetica", 10)
85
+ key_decisions_list = key_decisions.strip().split('\n')
86
+ for decision in key_decisions_list:
87
+ if decision.strip():
88
+ p.drawString(inch, y_position, f"- {decision.strip()}")
89
+ y_position -= 0.2*inch
90
+ if y_position < 1*inch: # Simple page break
91
+ p.showPage()
92
+ y_position = 10.5*inch
93
+ p.setFont("Helvetica", 10)
94
+
95
+ if transcription:
96
+ p.setFont("Helvetica-Bold", 12)
97
+ p.drawString(inch, y_position, "Transcription:")
98
+ y_position -= 0.3*inch
99
+ p.setFont("Helvetica", 8) # Smaller font for transcription
100
+ transcription_lines = transcription.split('\n')
101
+ for line in transcription_lines:
102
+ p.drawString(inch, y_position, line)
103
+ y_position -= 0.15*inch # Reduced line spacing for transcription
104
+ if y_position < 1*inch: # Simple page break
105
+ p.showPage()
106
+ y_position = 10.5*inch
107
+ p.setFont("Helvetica", 8)
108
+
109
+
110
+ p.save()
111
+ pdf_out = buffer.getvalue()
112
+ buffer.close()
113
+ return pdf_out
114
+
115
+ def send_email_report(email_address, meeting_date, summary, key_decisions, transcription):
116
+ smtp_server = os.environ.get("SMTP_SERVER")
117
+ smtp_port = os.environ.get("SMTP_PORT")
118
+ smtp_username = os.environ.get("SMTP_USERNAME")
119
+ smtp_password = os.environ.get("SMTP_PASSWORD")
120
+ sender_email = smtp_username # For simplicity, assuming sender is the same as username
121
+
122
+ if not all([smtp_server, smtp_port, smtp_username, smtp_password, sender_email]):
123
+ return False, "SMTP configuration is missing. Please set environment variables: SMTP_SERVER, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD."
124
+
125
+ subject = f"Meeting Report - {meeting_date.strftime('%Y-%m-%d')}"
126
+ body = f"Meeting Date: {meeting_date.strftime('%Y-%m-%d')}\n\nSummary:\n{summary}\n\nKey Decisions:\n{key_decisions}\n\nTranscription:\n{transcription}"
127
+
128
+ msg = MIMEText(body)
129
+ msg['Subject'] = subject
130
+ msg['From'] = sender_email
131
+ msg['To'] = email_address
132
+
133
+ try:
134
+ with smtplib.SMTP(smtp_server, smtp_port) as server:
135
+ server.starttls()
136
+ server.login(smtp_username, smtp_password)
137
+ server.sendmail(sender_email, email_address, msg.as_string())
138
+ return True, "Email sent successfully!"
139
+ except Exception as e:
140
+ return False, f"Email sending failed: {e}"
141
+
142
+
143
+ # Main UI
144
+ st.title("AI Meeting Notes & Reporting")
145
+
146
+ # Meeting Date & Time
147
+ meeting_date_time = st.date_input("Meeting Date & Time", datetime.today())
148
+
149
+ # Number of speakers
150
+ num_speakers = st.number_input("Number of speakers", min_value=1, max_value=10, value=st.session_state.num_speakers)
151
+ st.session_state.num_speakers = num_speakers # Update session state
152
+
153
+ # Language selection
154
+ language = st.selectbox(
155
+ "Language of report",
156
+ ["English", "Turkish", "Spanish", "French", "German"],
157
+ index=["English", "Turkish", "Spanish", "French", "German"].index(st.session_state.language) if st.session_state.language in ["English", "Turkish", "Spanish", "French", "German"] else 0
158
+ )
159
+ st.session_state.language = language # Update session state
160
+
161
+ # File upload
162
+ uploaded_file = st.file_uploader("Upload audio file", type=['mp3', 'wav'])
163
+
164
+ # Voice recording
165
+ audio_bytes = mic_recorder(start_prompt="Record", stop_prompt="Stop recording", key='recorder')
166
+
167
+ if audio_bytes:
168
+ if isinstance(audio_bytes, dict) and "bytes" in audio_bytes: # Check if audio_bytes is a dict and has 'bytes' key
169
+ st.audio(audio_bytes["bytes"], format="audio/wav")
170
+ st.session_state.recorded_audio = audio_bytes["bytes"]
171
+ else: # If not a dict or doesn't have 'bytes' key, assume it's raw bytes (fallback, might need adjustment)
172
+ st.audio(audio_bytes, format="audio/wav")
173
+ st.session_state.recorded_audio = audio_bytes
174
+
175
 
176
+ # Diarization, Summarization and Key Decisions logic - Automatically after upload or record
177
+ process_audio = False
178
+ audio_source_indicator = ""
179
 
180
+ if uploaded_file and uploaded_file != st.session_state.uploaded_file: # Check if a new file is uploaded
181
+ st.session_state.uploaded_file = uploaded_file # Update session state
182
+ st.session_state.recorded_audio = None # Reset recorded audio
183
+ process_audio = True
184
+ audio_source_indicator = f"Processing uploaded file: {uploaded_file.name}"
185
+ elif st.session_state.recorded_audio and st.session_state.recorded_audio != getattr(st.session_state.get('last_recorded_audio_hash'), 'value', None): # Check if new recording
186
+ st.session_state.last_recorded_audio_hash = st.session_state.recorded_audio # Store hash to detect new recordings
187
+ st.session_state.uploaded_file = None # Reset uploaded file
188
+ process_audio = True
189
+ audio_source_indicator = "Processing recorded audio"
190
 
 
 
191
 
192
+ if process_audio:
193
+ st.session_state.diarization_output = None # Reset previous diarization output
194
+ st.session_state.summary_output = None # Reset previous summary output
195
+ st.session_state.key_decisions_output = None # Reset previous key decisions output
196
+ st.session_state.chat_history = [] # Clear chat history for new file
197
+ st.session_state.email_sent_message = "" # Clear email sent message
198
 
199
+ with st.spinner(f"Processing audio and generating summary and key decisions... {audio_source_indicator}"):
200
+ temp_path = None
 
 
 
201
  try:
202
+ # Configure Gemini
203
+ genai.configure(api_key=os.environ["GEMINI_API_KEY"])
204
+
205
+ if st.session_state.uploaded_file: # Process uploaded file
206
+ # Save uploaded file temporarily
207
+ with NamedTemporaryFile(delete=False, suffix='.mp3') as tmp_file: # Assuming mp3 for wider compatibility, could adjust based on uploaded file type
208
+ tmp_file.write(st.session_state.uploaded_file.getvalue())
209
+ temp_path = tmp_file.name
210
+ mime_type = "audio/mpeg" # Assuming mp3, adjust if needed based on file type
211
+ gemini_file = genai.upload_file(temp_path, mime_type=mime_type)
212
+
213
+ elif st.session_state.recorded_audio: # Process recorded audio
214
+ # Save recorded audio temporarily (WAV from mic_recorder) and convert to MP3 if needed for Gemini
215
+ with NamedTemporaryFile(delete=False, suffix='.wav') as tmp_file_wav:
216
+ tmp_file_wav.write(st.session_state.recorded_audio)
217
+ temp_path = tmp_file_wav.name
218
+ gemini_file = genai.upload_file(temp_path, mime_type="audio/wav") # Assuming WAV is directly compatible
219
+
220
+
221
+ # --- Diarization ---
222
+ diarization_config = {
223
+ "temperature": 0.5,
224
+ "top_p": 0.95, #0.95
225
+ "top_k": 40,
226
+ "max_output_tokens": 8192,
227
+ "response_mime_type": "application/json",
228
+ }
229
+
230
+ diarization_model = genai.GenerativeModel(
231
+ model_name="gemini-2.0-flash-exp",
232
+ generation_config=diarization_config,
233
+ safety_settings={
234
+ 'HATE': 'BLOCK_NONE',
235
+ 'HARASSMENT': 'BLOCK_NONE',
236
+ 'SEXUAL': 'BLOCK_NONE',
237
+ 'DANGEROUS': 'BLOCK_NONE'
238
+ }
239
+ )
240
+
241
+ chat_session_diarization = diarization_model.start_chat(
242
+ history=[{"role": "user", "parts": [gemini_file]}]
243
+ )
244
+
245
+ response_diarization = chat_session_diarization.send_message(
246
+ f"Generate meeting diarization of the meeting audio record provided in the file. "
247
+ f"The meeting may be in a foreign language, expect a mixture of words in local language "
248
+ f"and words in english. Provided audio has {num_speakers} speakers. "
249
+ f"Accurately name the speakers or use labels like SPEAKER_01, SPEAKER_02, SPEAKER_03 and so on. "
250
+ f"Provide a structured JSON output. timestamp (hh:mm:ss), speaker (name only), "
251
+ f"speech (transcription). Do not transcribe filler words."
252
+ )
253
+
254
+ json_data_diarization = json.loads(response_diarization.text)
255
+ formatted_output = ""
256
+ for item in json_data_diarization:
257
+ formatted_output += f"{item['timestamp']} - {item['speaker']}: {item['speech']}\n\n"
258
+
259
+ st.session_state.diarization_output = formatted_output
260
+
261
+ # --- Summarization ---
262
+ summarization_config = {
263
+ "temperature": 0.25,
264
+ "top_p": 0.95,
265
+ "top_k": 40,
266
+ "max_output_tokens": 8192,
267
+ "response_schema": content.Schema(
268
+ type=content.Type.OBJECT,
269
+ enum=[],
270
+ required=["summary"],
271
+ properties={
272
+ "summary": content.Schema(
273
+ type=content.Type.STRING,
274
+ ),
275
+ },
276
+ ),
277
+ "response_mime_type": "application/json",
278
+ }
279
+
280
+ summarization_model = genai.GenerativeModel(
281
+ model_name="gemini-2.0-flash-exp",
282
+ generation_config=summarization_config,
283
+ safety_settings={
284
+ 'HATE': 'BLOCK_NONE',
285
+ 'HARASSMENT': 'BLOCK_NONE',
286
+ 'SEXUAL': 'BLOCK_NONE',
287
+ 'DANGEROUS': 'BLOCK_NONE'
288
+ }
289
+ )
290
+
291
+ chat_session_summarization = summarization_model.start_chat(
292
+ history=[{"role": "user", "parts": [st.session_state.diarization_output]}]
293
+ )
294
+
295
+ response_summarization = chat_session_summarization.send_message(
296
+ f"Generate a detailed summarization of the meeting, provide information on "
297
+ f"the topic of the meeting, agenda, things discussed and future plans if any mentioned. "
298
+ f"Provide structured output with only one tag 'summary'. Generate response in {language}."
299
+ )
300
+
301
+ json_data_summarization = json.loads(response_summarization.text)
302
+ summary = json_data_summarization.get('summary', "No summary found.")
303
+ st.session_state.summary_output = summary
304
+ st.session_state.chat_history.append(("Summary", summary))
305
+
306
+ # --- Key Decisions ---
307
+ key_decisions_config = {
308
+ "temperature": 0.25,
309
+ "top_p": 0.95,
310
+ "top_k": 40,
311
+ "max_output_tokens": 8192,
312
+ "response_schema": content.Schema(
313
+ type=content.Type.OBJECT,
314
+ enum=[],
315
+ required=["key_decisions"],
316
+ properties={
317
+ "key_decisions": content.Schema(
318
+ type=content.Type.STRING,
319
+ ),
320
+ },
321
+ ),
322
+ "response_mime_type": "application/json",
323
+ }
324
+
325
+ key_decisions_model = genai.GenerativeModel(
326
+ model_name="gemini-2.0-flash-exp",
327
+ generation_config=key_decisions_config,
328
+ safety_settings={
329
+ 'HATE': 'BLOCK_NONE',
330
+ 'HARASSMENT': 'BLOCK_NONE',
331
+ 'SEXUAL': 'BLOCK_NONE',
332
+ 'DANGEROUS': 'BLOCK_NONE'
333
+ }
334
+ )
335
+
336
+ chat_session_key_decisions = key_decisions_model.start_chat(
337
+ history=[{"role": "user", "parts": [st.session_state.diarization_output]}]
338
+ )
339
+
340
+ response_key_decisions = chat_session_key_decisions.send_message(
341
+ f"Identify and list the key decisions made during the meeting. "
342
+ f"Generate response in {language}."
343
+ )
344
+
345
+ json_data_key_decisions = json.loads(response_key_decisions.text)
346
+ key_decisions = json_data_key_decisions.get('key_decisions', "No key decisions found.")
347
+ st.session_state.key_decisions_output = key_decisions
348
+
349
+
350
+ except Exception as e:
351
+ st.error(f"Error processing audio: {str(e)}")
352
+ finally:
353
+ # Clean up temp file if created
354
+ if temp_path:
355
+ os.unlink(temp_path)
356
+
357
+ # Diarization output display
358
+ if st.session_state.diarization_output:
359
+ st.subheader("Diarization Output")
360
+ st.text_area("Transcript", st.session_state.diarization_output, height=300)
361
+
362
+ # Summary output
363
+ if st.session_state.summary_output:
364
+ st.subheader("Summary")
365
+ st.write(st.session_state.summary_output)
366
+
367
+ # Key decisions output
368
+ if st.session_state.key_decisions_output:
369
+ st.subheader("Key decisions")
370
+ key_decisions_list = st.session_state.key_decisions_output.strip().split('\n') # Split by newline
371
+ for decision in key_decisions_list:
372
+ if decision.strip(): # make sure decision is not empty
373
+ st.markdown(f"- {decision.strip()}")
374
+
375
+
376
+ # Generate PDF Report button
377
+ if st.button("Generate PDF report"):
378
+ if st.session_state.summary_output and st.session_state.key_decisions_output and st.session_state.diarization_output:
379
+ pdf_bytes = generate_pdf_report(
380
+ meeting_date_time,
381
+ st.session_state.summary_output,
382
+ st.session_state.key_decisions_output,
383
+ st.session_state.diarization_output
384
+ )
385
+ st.download_button(
386
+ label="Download PDF Report",
387
+ data=pdf_bytes,
388
+ file_name="meeting_report.pdf",
389
+ mime="application/pdf"
390
+ )
391
+ else:
392
+ st.warning("Please upload or record audio to generate report.")
393
+
394
+ # Q&A section
395
+ if st.session_state.diarization_output:
396
+ st.subheader("Question Answering")
397
+ question = st.text_input("Type in your question")
398
+ if st.button("Send"):
399
+ if question:
400
+ # Add user question to chat history
401
+ st.session_state.chat_history.append(("User", question))
402
+
403
+ with st.spinner("Generating response..."):
404
+ try:
405
+ # Configure QnA model
406
+ qna_config = {
407
+ "temperature": 0.25,
408
+ "top_p": 0.95,
409
+ "top_k": 40,
410
+ "max_output_tokens": 8192,
411
+ "response_schema": content.Schema(
412
+ type=content.Type.OBJECT,
413
+ enum=[],
414
+ required=["answer"],
415
+ properties={
416
+ "answer": content.Schema(
417
+ type=content.Type.STRING,
418
+ ),
419
+ },
420
+ ),
421
+ "response_mime_type": "application/json",
422
+ }
423
+
424
+ qna_model = genai.GenerativeModel(
425
+ model_name="gemini-2.0-flash-exp",
426
+ generation_config=qna_config,
427
+ safety_settings={
428
+ 'HATE': 'BLOCK_NONE',
429
+ 'HARASSMENT': 'BLOCK_NONE',
430
+ 'SEXUAL': 'BLOCK_NONE',
431
+ 'DANGEROUS': 'BLOCK_NONE'
432
+ }
433
+ )
434
+
435
+ # Generate answer
436
+ chat_session_qna = qna_model.start_chat(
437
+ history=[{"role": "user", "parts": [st.session_state.diarization_output]}]
438
+ )
439
+
440
+ response_qna = chat_session_qna.send_message(
441
+ f"Answer the following question based on the meeting: {question}. Generate response in {language}."
442
+ f"Provide structured output with only one tag 'answer'."
443
+ )
444
+
445
+ json_data_qna = json.loads(response_qna.text)
446
+ answer = json_data_qna.get('answer', "No answer found.")
447
+
448
+ # Add bot response to chat history
449
+ st.session_state.chat_history.append(("Bot", answer))
450
+ st.rerun() # Rerun to update the chat display
451
+
452
+ except Exception as e:
453
+ st.error(f"Error generating answer: {str(e)}")
454
+
455
+ # Chat history display for Q&A
456
+ for role, message in st.session_state.chat_history:
457
+ if role == "User":
458
+ st.write(f"**Question**: {message}")
459
+ elif role == "Bot":
460
+ st.write(f"**Answer**: {message}")
461
+
462
+ # Email input and Send Report button
463
+ st.subheader("Share Report")
464
+ email_address = st.text_input("Email address:")
465
 
466
+ send_button = st.button("Send Report")
467
+ if send_button:
468
+ if not email_address:
469
+ st.warning("Please enter an email address.")
470
+ elif not (st.session_state.summary_output and st.session_state.key_decisions_output and st.session_state.diarization_output):
471
+ st.warning("Please upload or record audio and generate report first.")
472
+ else:
473
+ success, message = send_email_report(
474
+ email_address,
475
+ meeting_date_time,
476
+ st.session_state.summary_output,
477
+ st.session_state.key_decisions_output,
478
+ st.session_state.diarization_output
479
+ )
480
+ if success:
481
+ st.success(message)
482
+ else:
483
+ st.error(message)