awacke1 commited on
Commit
bb0696e
β€’
1 Parent(s): 5a4281c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -46
app.py CHANGED
@@ -33,8 +33,8 @@ st.set_page_config(
33
  }
34
  )
35
  load_dotenv()
36
- openai.api_key = os.getenv('OPENAI_API_KEY') or st.secrets['OPENAI_API_KEY']
37
- anthropic_key = os.getenv("ANTHROPIC_API_KEY_3") or st.secrets["ANTHROPIC_API_KEY"]
38
  claude_client = anthropic.Anthropic(api_key=anthropic_key)
39
  openai_client = OpenAI(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
40
  HF_KEY = os.getenv('HF_KEY')
@@ -80,21 +80,36 @@ FILE_EMOJIS = {
80
  "mp3": "🎡",
81
  }
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  def generate_filename(prompt, file_type="md"):
 
84
  ctz = pytz.timezone('US/Central')
85
- date_str = datetime.now(ctz).strftime("%m%d_%H%M")
86
  safe = re.sub(r'[<>:"/\\\\|?*\n]', ' ', prompt)
87
  safe = re.sub(r'\s+', ' ', safe).strip()[:90]
88
  return f"{date_str}_{safe}.{file_type}"
89
 
90
  def create_file(filename, prompt, response):
91
- # Creating file does not trigger immediate rerun
92
  with open(filename, 'w', encoding='utf-8') as f:
93
  f.write(prompt + "\n\n" + response)
 
94
 
95
  def get_download_link(file):
96
  with open(file, "rb") as f:
97
  b64 = base64.b64encode(f.read()).decode()
 
98
  return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">πŸ“‚ Download {os.path.basename(file)}</a>'
99
 
100
  @st.cache_resource
@@ -110,7 +125,7 @@ def speech_synthesis_html(result):
110
  components.html(html_code, height=0)
111
 
112
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
113
- # Just create mp3 file, no immediate rerun
114
  if not text.strip():
115
  return None
116
  rate_str = f"{rate:+d}%"
@@ -150,7 +165,7 @@ def process_audio(audio_path):
150
  with open(audio_path, "rb") as f:
151
  transcription = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
152
  st.session_state.messages.append({"role": "user", "content": transcription.text})
153
- # No immediate rerun
154
  return transcription.text
155
 
156
  def process_video(video_path, seconds_per_frame=1):
@@ -204,13 +219,16 @@ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False, titles_summary
204
  st.markdown(result)
205
 
206
  if vocal_summary:
207
- audio_file_main = speak_with_edge_tts(r2, voice="en-US-AriaNeural", rate=0, pitch=0)
 
 
208
  st.write("### πŸŽ™οΈ Vocal Summary (Short Answer)")
209
  play_and_download_audio(audio_file_main)
210
 
211
  if extended_refs:
212
  summaries_text = "Here are the summaries from the references: " + refs.replace('"','')
213
- audio_file_refs = speak_with_edge_tts(summaries_text, voice="en-US-AriaNeural", rate=0, pitch=0)
 
214
  st.write("### πŸ“œ Extended References & Summaries")
215
  play_and_download_audio(audio_file_refs)
216
 
@@ -222,7 +240,8 @@ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False, titles_summary
222
  titles.append(m.group(1))
223
  if titles:
224
  titles_text = "Here are the titles of the papers: " + ", ".join(titles)
225
- audio_file_titles = speak_with_edge_tts(titles_text, voice="en-US-AriaNeural", rate=0, pitch=0)
 
226
  st.write("### πŸ”– Paper Titles")
227
  play_and_download_audio(audio_file_titles)
228
 
@@ -274,9 +293,7 @@ def create_zip_of_files(md_files, mp3_files):
274
  return None
275
  # Build a descriptive name from file stems
276
  stems = [os.path.splitext(os.path.basename(f))[0] for f in all_files]
277
- # Join them
278
  joined = "_".join(stems)
279
- # Truncate if too long
280
  if len(joined) > 50:
281
  joined = joined[:50] + "_etc"
282
  zip_name = f"{joined}.zip"
@@ -300,59 +317,74 @@ def load_files_for_sidebar():
300
  # Exclude README.md from listings
301
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
302
 
303
- files_by_ext = defaultdict(list)
304
- if md_files: files_by_ext['md'].extend(md_files)
305
- if mp3_files: files_by_ext['mp3'].extend(mp3_files)
 
 
 
 
 
306
 
307
- # Sort each extension group by modification time descending
308
- for ext in files_by_ext:
309
- files_by_ext[ext].sort(key=lambda x: os.path.getmtime(x), reverse=True)
310
- return files_by_ext
311
 
312
- def display_file_manager_sidebar(files_by_ext):
313
- st.sidebar.title("🎡 Audio & Document Manager")
314
 
315
- md_files = files_by_ext.get('md', [])
316
- mp3_files = files_by_ext.get('mp3', [])
 
 
317
 
318
- # Buttons to delete all except README.md (already excluded)
319
- col_del = st.sidebar.columns(3)
320
- with col_del[0]:
 
 
 
 
 
 
 
 
 
321
  if st.button("πŸ—‘ Del All MD"):
322
  for f in md_files:
323
  os.remove(f)
324
  st.session_state.should_rerun = True
325
- with col_del[1]:
326
  if st.button("πŸ—‘ Del All MP3"):
327
  for f in mp3_files:
328
  os.remove(f)
329
  st.session_state.should_rerun = True
330
- with col_del[2]:
331
  if st.button("⬇️ Zip All"):
332
- # create a zip of all md and mp3 except README.md
333
  z = create_zip_of_files(md_files, mp3_files)
334
  if z:
335
  st.sidebar.markdown(get_download_link(z),unsafe_allow_html=True)
336
 
337
- ext_counts = {ext: len(files) for ext, files in files_by_ext.items()}
338
- sorted_ext = sorted(files_by_ext.keys(), key=lambda x: ext_counts[x], reverse=True)
339
-
340
- # Display files with actions
341
- for ext in sorted_ext:
342
- emoji = FILE_EMOJIS.get(ext, "πŸ“¦")
343
- count = len(files_by_ext[ext])
344
- with st.sidebar.expander(f"{emoji} {ext.upper()} Files ({count})", expanded=True):
345
- for f in files_by_ext[ext]:
346
  fname = os.path.basename(f)
347
  ctime = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%Y-%m-%d %H:%M:%S")
348
- # Show filename and buttons in a row
349
  st.write(f"**{fname}** - {ctime}")
350
  file_buttons_col = st.columns([1,1,1])
351
  with file_buttons_col[0]:
352
  if st.button("πŸ‘€View", key="view_"+f):
353
  st.session_state.viewing_file = f
354
  st.session_state.viewing_file_type = ext
355
- # No rerun needed, just set state
356
  with file_buttons_col[1]:
357
  if ext == "md":
358
  if st.button("✏️Edit", key="edit_"+f):
@@ -409,12 +441,16 @@ def main():
409
  col1,col2,col3=st.columns(3)
410
  with col1:
411
  st.subheader("GPT-4o Omni:")
412
- try: process_with_gpt(user_input)
413
- except: st.write('GPT 4o error')
 
 
414
  with col2:
415
  st.subheader("Claude-3 Sonnet:")
416
- try: process_with_claude(user_input)
417
- except: st.write('Claude error')
 
 
418
  with col3:
419
  st.subheader("Arxiv + Mistral:")
420
  try:
@@ -514,10 +550,10 @@ def main():
514
  st.write("Select a file from the sidebar to edit.")
515
 
516
  # After main content, load files and display in sidebar
517
- files_by_ext = load_files_for_sidebar()
518
- display_file_manager_sidebar(files_by_ext)
519
 
520
- # If viewing a file, show its content below (in the main area)
521
  if st.session_state.viewing_file and os.path.exists(st.session_state.viewing_file):
522
  st.write("---")
523
  st.write(f"**Viewing File:** {os.path.basename(st.session_state.viewing_file)}")
 
33
  }
34
  )
35
  load_dotenv()
36
+ openai.api_key = os.getenv('OPENAI_API_KEY') or st.secrets.get('OPENAI_API_KEY', "")
37
+ anthropic_key = os.getenv("ANTHROPIC_API_KEY_3") or st.secrets.get("ANTHROPIC_API_KEY", "")
38
  claude_client = anthropic.Anthropic(api_key=anthropic_key)
39
  openai_client = OpenAI(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
40
  HF_KEY = os.getenv('HF_KEY')
 
80
  "mp3": "🎡",
81
  }
82
 
83
+ def clean_for_speech(text: str) -> str:
84
+ # Remove \n
85
+ text = text.replace("\n", " ")
86
+ # Remove </s>
87
+ text = text.replace("</s>", " ")
88
+ # Remove markdown headings (#)
89
+ text = text.replace("#", "")
90
+ # Remove links of the form (https://...)
91
+ text = re.sub(r"\(https?:\/\/[^\)]+\)", "", text)
92
+ # Collapse multiple spaces
93
+ text = re.sub(r"\s+", " ", text).strip()
94
+ return text
95
+
96
  def generate_filename(prompt, file_type="md"):
97
+ # Include seconds to get a 10-character prefix: %m%d%H%M%S results in exactly 10 chars
98
  ctz = pytz.timezone('US/Central')
99
+ date_str = datetime.now(ctz).strftime("%m%d%H%M%S")
100
  safe = re.sub(r'[<>:"/\\\\|?*\n]', ' ', prompt)
101
  safe = re.sub(r'\s+', ' ', safe).strip()[:90]
102
  return f"{date_str}_{safe}.{file_type}"
103
 
104
  def create_file(filename, prompt, response):
 
105
  with open(filename, 'w', encoding='utf-8') as f:
106
  f.write(prompt + "\n\n" + response)
107
+ # No immediate rerun. Changes will appear next load.
108
 
109
  def get_download_link(file):
110
  with open(file, "rb") as f:
111
  b64 = base64.b64encode(f.read()).decode()
112
+ # It's a zip file download
113
  return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">πŸ“‚ Download {os.path.basename(file)}</a>'
114
 
115
  @st.cache_resource
 
125
  components.html(html_code, height=0)
126
 
127
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
128
+ text = clean_for_speech(text)
129
  if not text.strip():
130
  return None
131
  rate_str = f"{rate:+d}%"
 
165
  with open(audio_path, "rb") as f:
166
  transcription = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
167
  st.session_state.messages.append({"role": "user", "content": transcription.text})
168
+ # No immediate rerun.
169
  return transcription.text
170
 
171
  def process_video(video_path, seconds_per_frame=1):
 
219
  st.markdown(result)
220
 
221
  if vocal_summary:
222
+ # Clean before speech
223
+ main_text = clean_for_speech(r2)
224
+ audio_file_main = speak_with_edge_tts(main_text)
225
  st.write("### πŸŽ™οΈ Vocal Summary (Short Answer)")
226
  play_and_download_audio(audio_file_main)
227
 
228
  if extended_refs:
229
  summaries_text = "Here are the summaries from the references: " + refs.replace('"','')
230
+ summaries_text = clean_for_speech(summaries_text)
231
+ audio_file_refs = speak_with_edge_tts(summaries_text)
232
  st.write("### πŸ“œ Extended References & Summaries")
233
  play_and_download_audio(audio_file_refs)
234
 
 
240
  titles.append(m.group(1))
241
  if titles:
242
  titles_text = "Here are the titles of the papers: " + ", ".join(titles)
243
+ titles_text = clean_for_speech(titles_text)
244
+ audio_file_titles = speak_with_edge_tts(titles_text)
245
  st.write("### πŸ”– Paper Titles")
246
  play_and_download_audio(audio_file_titles)
247
 
 
293
  return None
294
  # Build a descriptive name from file stems
295
  stems = [os.path.splitext(os.path.basename(f))[0] for f in all_files]
 
296
  joined = "_".join(stems)
 
297
  if len(joined) > 50:
298
  joined = joined[:50] + "_etc"
299
  zip_name = f"{joined}.zip"
 
317
  # Exclude README.md from listings
318
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
319
 
320
+ all_files = md_files + mp3_files
321
+ # Group by first 10 chars of filename
322
+ # Note: We assume all files have at least 10 chars before underscore from generate_filename
323
+ groups = defaultdict(list)
324
+ for f in all_files:
325
+ fname = os.path.basename(f)
326
+ prefix = fname[:10] # first 10 chars
327
+ groups[prefix].append(f)
328
 
329
+ # Sort groups by mod time of the newest file in that group
330
+ # Also sort files within each group by mod time descending
331
+ for prefix in groups:
332
+ groups[prefix].sort(key=lambda x: os.path.getmtime(x), reverse=True)
333
 
334
+ # Sort prefix keys by latest mod time of their newest file
335
+ sorted_prefixes = sorted(groups.keys(), key=lambda pre: max(os.path.getmtime(x) for x in groups[pre]), reverse=True)
336
 
337
+ return groups, sorted_prefixes
338
+
339
+ def display_file_manager_sidebar(groups, sorted_prefixes):
340
+ st.sidebar.title("🎡 Audio & Document Manager")
341
 
342
+ # Collect all files except README.md
343
+ md_files = []
344
+ mp3_files = []
345
+ for prefix in groups:
346
+ for f in groups[prefix]:
347
+ if f.endswith(".md"):
348
+ md_files.append(f)
349
+ elif f.endswith(".mp3"):
350
+ mp3_files.append(f)
351
+
352
+ top_bar = st.sidebar.columns(3)
353
+ with top_bar[0]:
354
  if st.button("πŸ—‘ Del All MD"):
355
  for f in md_files:
356
  os.remove(f)
357
  st.session_state.should_rerun = True
358
+ with top_bar[1]:
359
  if st.button("πŸ—‘ Del All MP3"):
360
  for f in mp3_files:
361
  os.remove(f)
362
  st.session_state.should_rerun = True
363
+ with top_bar[2]:
364
  if st.button("⬇️ Zip All"):
 
365
  z = create_zip_of_files(md_files, mp3_files)
366
  if z:
367
  st.sidebar.markdown(get_download_link(z),unsafe_allow_html=True)
368
 
369
+ # Display groups in expanders
370
+ for prefix in sorted_prefixes:
371
+ files = groups[prefix]
372
+ # Determine file types inside the group
373
+ # Just show the prefix + number of files
374
+ exp = st.sidebar.expander(f"{prefix} Files ({len(files)})", expanded=True)
375
+ with exp:
376
+ # Files sorted by mod time descending
377
+ for f in files:
378
  fname = os.path.basename(f)
379
  ctime = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%Y-%m-%d %H:%M:%S")
380
+ ext = os.path.splitext(fname)[1].lower().strip('.')
381
  st.write(f"**{fname}** - {ctime}")
382
  file_buttons_col = st.columns([1,1,1])
383
  with file_buttons_col[0]:
384
  if st.button("πŸ‘€View", key="view_"+f):
385
  st.session_state.viewing_file = f
386
  st.session_state.viewing_file_type = ext
387
+ # Just update state, no rerun
388
  with file_buttons_col[1]:
389
  if ext == "md":
390
  if st.button("✏️Edit", key="edit_"+f):
 
441
  col1,col2,col3=st.columns(3)
442
  with col1:
443
  st.subheader("GPT-4o Omni:")
444
+ try:
445
+ process_with_gpt(user_input)
446
+ except:
447
+ st.write('GPT 4o error')
448
  with col2:
449
  st.subheader("Claude-3 Sonnet:")
450
+ try:
451
+ process_with_claude(user_input)
452
+ except:
453
+ st.write('Claude error')
454
  with col3:
455
  st.subheader("Arxiv + Mistral:")
456
  try:
 
550
  st.write("Select a file from the sidebar to edit.")
551
 
552
  # After main content, load files and display in sidebar
553
+ groups, sorted_prefixes = load_files_for_sidebar()
554
+ display_file_manager_sidebar(groups, sorted_prefixes)
555
 
556
+ # If viewing a file, show its content or audio below
557
  if st.session_state.viewing_file and os.path.exists(st.session_state.viewing_file):
558
  st.write("---")
559
  st.write(f"**Viewing File:** {os.path.basename(st.session_state.viewing_file)}")