awacke1 commited on
Commit
ec1ca1c
·
verified ·
1 Parent(s): 3cbe462

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -105
app.py CHANGED
@@ -190,7 +190,7 @@ def generate_filename(prompt, response, file_type="md"):
190
  return filename
191
 
192
  def create_file(prompt, response, file_type="md"):
193
- """Create file with intelligent naming"""
194
  filename = generate_filename(prompt.strip(), response.strip(), file_type)
195
  with open(filename, 'w', encoding='utf-8') as f:
196
  f.write(prompt + "\n\n" + response)
@@ -226,7 +226,7 @@ def speech_synthesis_html(result):
226
  components.html(html_code, height=0)
227
 
228
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
229
- """Generate audio using Edge TTS"""
230
  text = clean_for_speech(text)
231
  if not text.strip():
232
  return None
@@ -238,17 +238,16 @@ async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=
238
  return out_fn
239
 
240
  def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0):
241
- """Wrapper for edge TTS generation"""
242
  return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch))
243
 
244
  def play_and_download_audio(file_path):
245
- """Play and provide download link for audio"""
246
  if file_path and os.path.exists(file_path):
247
  st.audio(file_path)
248
  dl_link = f'<a href="data:audio/mpeg;base64,{base64.b64encode(open(file_path,"rb").read()).decode()}" download="{os.path.basename(file_path)}">Download {os.path.basename(file_path)}</a>'
249
  st.markdown(dl_link, unsafe_allow_html=True)
250
 
251
- # NEW: Helper to embed auto-play audio
252
  def auto_play_audio(file_path):
253
  """
254
  Reads MP3 file as base64, displays an <audio> tag with autoplay + controls + download link.
@@ -270,17 +269,19 @@ def auto_play_audio(file_path):
270
  </a>
271
  """, unsafe_allow_html=True)
272
 
273
- # NEW: Generate specialized MP3 filename using query + paper metadata
274
  def generate_audio_filename(query, title, summary):
 
 
 
275
  combined = (query + " " + title + " " + summary).strip().lower()
276
  combined = re.sub(r'[^\w\s-]', '', combined) # remove special chars
277
- combined = "_".join(combined.split())[:80] # max ~80 chars to keep it short
278
  prefix = datetime.now().strftime("%y%m_%H%M")
279
  return f"{prefix}_{combined}.mp3"
280
 
281
  # 🎬 8. Media Processing
282
  def process_image(image_path, user_prompt):
283
- """Process image with GPT-4V"""
284
  with open(image_path, "rb") as imgf:
285
  image_data = imgf.read()
286
  b64img = base64.b64encode(image_data).decode("utf-8")
@@ -301,14 +302,14 @@ def process_image(image_path, user_prompt):
301
  return resp.choices[0].message.content
302
 
303
  def process_audio(audio_path):
304
- """Process audio with Whisper"""
305
  with open(audio_path, "rb") as f:
306
  transcription = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
307
  st.session_state.messages.append({"role": "user", "content": transcription.text})
308
  return transcription.text
309
 
310
  def process_video(video_path, seconds_per_frame=1):
311
- """Extract frames from video"""
312
  vid = cv2.VideoCapture(video_path)
313
  total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
314
  fps = vid.get(cv2.CAP_PROP_FPS)
@@ -325,7 +326,7 @@ def process_video(video_path, seconds_per_frame=1):
325
  return frames_b64
326
 
327
  def process_video_with_gpt(video_path, prompt):
328
- """Analyze video frames with GPT-4V"""
329
  frames = process_video(video_path)
330
  resp = openai_client.chat.completions.create(
331
  model=st.session_state["openai_model"],
@@ -334,7 +335,7 @@ def process_video_with_gpt(video_path, prompt):
334
  {
335
  "role": "user",
336
  "content": [
337
- {"type": "text","text": prompt},
338
  *[{"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{fr}"}} for fr in frames]
339
  ]
340
  }
@@ -348,108 +349,104 @@ def save_full_transcript(query, text):
348
  """Save full transcript of Arxiv results as a file."""
349
  create_file(query, text, "md")
350
 
351
- # NEW: parse references with bracketed titles, up to 20
 
 
 
 
 
352
  def parse_arxiv_refs(ref_text: str):
353
  lines = ref_text.split('\n')
354
- results = []
355
- for line in lines:
356
- line = line.strip()
357
- if not line:
358
- continue
359
- # bracketed title
360
- title_match = re.search(r"\[([^\]]+)\]", line)
361
- if title_match:
362
- raw_title = title_match.group(1).strip()
363
- else:
364
- raw_title = "No Title"
365
- remainder = line.replace(title_match.group(0), "").strip() if title_match else line
366
- summary = remainder
367
-
368
- # guess year from either title or summary
369
- year_match = re.search(r'(20\d{2})', raw_title)
370
- if not year_match:
371
- year_match = re.search(r'(20\d{2})', summary)
372
  year = int(year_match.group(1)) if year_match else None
373
-
374
- results.append({
375
- 'title': raw_title,
376
- 'summary': summary,
377
- 'year': year
378
  })
379
- return results
380
-
381
 
382
  def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
383
  titles_summary=True, full_audio=False):
384
- """Perform Arxiv search and generate audio summaries."""
 
 
 
 
 
 
385
  start = time.time()
386
 
387
- # 1) Query the HF RAG pipeline
388
  client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
389
- refs = client.predict(q, 20, "Semantic Search","mistralai/Mixtral-8x7B-Instruct-v0.1", api_name="/update_with_rag_md")[0]
 
 
 
390
  r2 = client.predict(q, "mistralai/Mixtral-8x7B-Instruct-v0.1", True, api_name="/ask_llm")
391
 
392
- # 2) Combine for final text output
393
  result = f"### 🔎 {q}\n\n{r2}\n\n{refs}"
394
  st.markdown(result)
395
 
396
- # Existing logic for "all-at-once" audio
397
  if full_audio:
398
  complete_text = f"Complete response for query: {q}. {clean_for_speech(r2)} {clean_for_speech(refs)}"
399
  audio_file_full = speak_with_edge_tts(complete_text)
400
  st.write("### 📚 Full Audio")
401
- play_and_download_audio(audio_file_full) # or auto_play_audio(audio_file_full)
402
 
403
  if vocal_summary:
404
  main_text = clean_for_speech(r2)
405
  audio_file_main = speak_with_edge_tts(main_text)
406
  st.write("### 🎙 Short Audio")
407
- play_and_download_audio(audio_file_main) # or auto_play_audio(audio_file_main)
408
 
409
  if extended_refs:
410
  summaries_text = "Extended references: " + refs.replace('"','')
411
  summaries_text = clean_for_speech(summaries_text)
412
  audio_file_refs = speak_with_edge_tts(summaries_text)
413
  st.write("### 📜 Long Refs")
414
- play_and_download_audio(audio_file_refs) # or auto_play_audio(audio_file_refs)
415
-
416
- # --------------------------------------
417
- # Parse references, limit to 20, generate TTS if year==2023 or 2024
418
- # --------------------------------------
419
- parsed_refs = parse_arxiv_refs(refs)[:20]
420
- # Sort by year descending (None -> bottom)
421
- parsed_refs.sort(key=lambda x: x["year"] if x["year"] else 0, reverse=True)
422
-
423
- st.write("## Individual Papers (Most Recent First)")
424
- for idx, paper in enumerate(parsed_refs):
425
- year_str = paper["year"] if paper["year"] else "Unknown Year"
426
- st.markdown(f"**{idx+1}. {paper['title']}** \n*Year:* {year_str}")
427
- st.markdown(f"*Summary:* {paper['summary']}")
 
 
 
 
 
428
  st.write("---")
429
 
430
- # Only auto-generate TTS if year is 2023 or 2024
431
- if paper["year"] in [2023, 2024]:
432
- # Combine Title, Year, Summary in one text
433
- tts_text = (
434
- f"Title: {paper['title']}. "
435
- f"Year: {paper['year']}. "
436
- f"Summary: {paper['summary']}"
437
- )
438
- # Generate specialized MP3 filename
439
- mp3_filename = generate_audio_filename(q, paper['title'], paper['summary'])
440
-
441
- # Create TTS as usual
442
- temp_mp3 = speak_with_edge_tts(tts_text)
443
- if temp_mp3 and os.path.exists(temp_mp3):
444
- # rename to a meaningful name
445
- os.rename(temp_mp3, mp3_filename)
446
- # Now embed the auto-play audio
447
- auto_play_audio(mp3_filename)
448
-
449
- # Titles Only (all-in-one)
450
  if titles_summary:
 
 
 
451
  titles = []
452
- for line in refs.split('\n'):
453
  m = re.search(r"\[([^\]]+)\]", line)
454
  if m:
455
  titles.append(m.group(1))
@@ -459,17 +456,15 @@ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
459
  audio_file_titles = speak_with_edge_tts(titles_text)
460
  st.write("### 🔖 Titles (All-In-One)")
461
  play_and_download_audio(audio_file_titles)
462
- # or auto_play_audio(audio_file_titles)
463
 
464
  elapsed = time.time() - start
465
  st.write(f"**Total Elapsed:** {elapsed:.2f} s")
466
 
467
- # Save entire text to file as usual
468
  create_file(q, result, "md")
469
 
470
  return result
471
 
472
-
473
  def process_with_gpt(text):
474
  """Process text with GPT-4"""
475
  if not text:
@@ -509,7 +504,7 @@ def process_with_claude(text):
509
 
510
  # 📂 10. File Management
511
  def create_zip_of_files(md_files, mp3_files):
512
- """Create zip with intelligent naming"""
513
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
514
  all_files = md_files + mp3_files
515
  if not all_files:
@@ -541,22 +536,21 @@ def load_files_for_sidebar():
541
  """Load and group files for sidebar display"""
542
  md_files = glob.glob("*.md")
543
  mp3_files = glob.glob("*.mp3")
544
-
545
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
546
- all_files = md_files + mp3_files
547
 
 
548
  groups = defaultdict(list)
549
  for f in all_files:
550
  fname = os.path.basename(f)
551
- prefix = fname[:10]
552
  groups[prefix].append(f)
553
 
554
  for prefix in groups:
555
  groups[prefix].sort(key=lambda x: os.path.getmtime(x), reverse=True)
556
 
557
  sorted_prefixes = sorted(groups.keys(),
558
- key=lambda pre: max(os.path.getmtime(x) for x in groups[pre]),
559
- reverse=True)
560
  return groups, sorted_prefixes
561
 
562
  def extract_keywords_from_md(files):
@@ -596,14 +590,14 @@ def display_file_manager_sidebar(groups, sorted_prefixes):
596
  if st.button("⬇️ ZipAll"):
597
  z = create_zip_of_files(all_md, all_mp3)
598
  if z:
599
- st.sidebar.markdown(get_download_link(z),unsafe_allow_html=True)
600
 
601
  for prefix in sorted_prefixes:
602
  files = groups[prefix]
603
  kw = extract_keywords_from_md(files)
604
  keywords_str = " ".join(kw) if kw else "No Keywords"
605
  with st.sidebar.expander(f"{prefix} Files ({len(files)}) - KW: {keywords_str}", expanded=True):
606
- c1, c2 = st.columns(2)
607
  with c1:
608
  if st.button("👀ViewGrp", key="view_group_"+prefix):
609
  st.session_state.viewing_prefix = prefix
@@ -624,7 +618,7 @@ def main():
624
  st.sidebar.markdown("### 🚲BikeAI🏆 Multi-Agent Research")
625
  tab_main = st.radio("Action:", ["🎤 Voice","📸 Media","🔍 ArXiv","📝 Editor"], horizontal=True)
626
 
627
- # This is your custom React component reference, if used.
628
  mycomponent = components.declare_component("mycomponent", path="mycomponent")
629
  val = mycomponent(my_input_value="Hello")
630
 
@@ -650,11 +644,10 @@ def main():
650
  extended_refs=False,
651
  titles_summary=True,
652
  full_audio=full_audio)
653
- else:
654
- if run_option == "GPT-4o":
655
- process_with_gpt(edited_input)
656
- elif run_option == "Claude-3.5":
657
- process_with_claude(edited_input)
658
  else:
659
  if st.button("▶ Run"):
660
  st.session_state.old_val = val
@@ -664,11 +657,10 @@ def main():
664
  extended_refs=False,
665
  titles_summary=True,
666
  full_audio=full_audio)
667
- else:
668
- if run_option == "GPT-4o":
669
- process_with_gpt(edited_input)
670
- elif run_option == "Claude-3.5":
671
- process_with_claude(edited_input)
672
 
673
  if tab_main == "🔍 ArXiv":
674
  st.subheader("🔍 Query ArXiv")
@@ -726,7 +718,7 @@ def main():
726
  with tabs[0]:
727
  imgs = glob.glob("*.png")+glob.glob("*.jpg")
728
  if imgs:
729
- c = st.slider("Cols",1,5,3)
730
  cols = st.columns(c)
731
  for i, f in enumerate(imgs):
732
  with cols[i % c]:
@@ -749,7 +741,7 @@ def main():
749
  st.write("No videos found.")
750
 
751
  elif tab_main == "📝 Editor":
752
- if getattr(st.session_state,'current_file', None):
753
  st.subheader(f"Editing: {st.session_state.current_file}")
754
  new_text = st.text_area("✏️ Content:", st.session_state.file_content, height=300)
755
  if st.button("💾 Save"):
@@ -760,9 +752,11 @@ def main():
760
  else:
761
  st.write("Select a file from the sidebar to edit.")
762
 
 
763
  groups, sorted_prefixes = load_files_for_sidebar()
764
  display_file_manager_sidebar(groups, sorted_prefixes)
765
 
 
766
  if st.session_state.viewing_prefix and st.session_state.viewing_prefix in groups:
767
  st.write("---")
768
  st.write(f"**Viewing Group:** {st.session_state.viewing_prefix}")
@@ -784,6 +778,5 @@ def main():
784
  st.session_state.should_rerun = False
785
  st.rerun()
786
 
787
-
788
  if __name__ == "__main__":
789
  main()
 
190
  return filename
191
 
192
  def create_file(prompt, response, file_type="md"):
193
+ """Create file with an intelligent naming scheme."""
194
  filename = generate_filename(prompt.strip(), response.strip(), file_type)
195
  with open(filename, 'w', encoding='utf-8') as f:
196
  f.write(prompt + "\n\n" + response)
 
226
  components.html(html_code, height=0)
227
 
228
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
229
+ """Generate audio using Edge TTS (async)"""
230
  text = clean_for_speech(text)
231
  if not text.strip():
232
  return None
 
238
  return out_fn
239
 
240
  def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0):
241
+ """Wrapper for edge TTS generation (sync)"""
242
  return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch))
243
 
244
  def play_and_download_audio(file_path):
245
+ """Play and provide a download link for audio"""
246
  if file_path and os.path.exists(file_path):
247
  st.audio(file_path)
248
  dl_link = f'<a href="data:audio/mpeg;base64,{base64.b64encode(open(file_path,"rb").read()).decode()}" download="{os.path.basename(file_path)}">Download {os.path.basename(file_path)}</a>'
249
  st.markdown(dl_link, unsafe_allow_html=True)
250
 
 
251
  def auto_play_audio(file_path):
252
  """
253
  Reads MP3 file as base64, displays an <audio> tag with autoplay + controls + download link.
 
269
  </a>
270
  """, unsafe_allow_html=True)
271
 
 
272
  def generate_audio_filename(query, title, summary):
273
+ """
274
+ Example specialized MP3 filename: prefix + query + short snippet of title/summary
275
+ """
276
  combined = (query + " " + title + " " + summary).strip().lower()
277
  combined = re.sub(r'[^\w\s-]', '', combined) # remove special chars
278
+ combined = "_".join(combined.split())[:80] # limit length
279
  prefix = datetime.now().strftime("%y%m_%H%M")
280
  return f"{prefix}_{combined}.mp3"
281
 
282
  # 🎬 8. Media Processing
283
  def process_image(image_path, user_prompt):
284
+ """Process image with GPT-4V (placeholder logic)"""
285
  with open(image_path, "rb") as imgf:
286
  image_data = imgf.read()
287
  b64img = base64.b64encode(image_data).decode("utf-8")
 
302
  return resp.choices[0].message.content
303
 
304
  def process_audio(audio_path):
305
+ """Process audio with Whisper (placeholder logic)"""
306
  with open(audio_path, "rb") as f:
307
  transcription = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
308
  st.session_state.messages.append({"role": "user", "content": transcription.text})
309
  return transcription.text
310
 
311
  def process_video(video_path, seconds_per_frame=1):
312
+ """Extract frames from video (placeholder logic)"""
313
  vid = cv2.VideoCapture(video_path)
314
  total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
315
  fps = vid.get(cv2.CAP_PROP_FPS)
 
326
  return frames_b64
327
 
328
  def process_video_with_gpt(video_path, prompt):
329
+ """Analyze video frames with GPT-4V (placeholder logic)"""
330
  frames = process_video(video_path)
331
  resp = openai_client.chat.completions.create(
332
  model=st.session_state["openai_model"],
 
335
  {
336
  "role": "user",
337
  "content": [
338
+ {"type":"text","text":prompt},
339
  *[{"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{fr}"}} for fr in frames]
340
  ]
341
  }
 
349
  """Save full transcript of Arxiv results as a file."""
350
  create_file(query, text, "md")
351
 
352
+ # ---------------------------------------------------
353
+ # NEW: Extremely simple "parse_arxiv_refs" logic
354
+ # that reads each non-empty line, up to 20 lines.
355
+ # Extract bracketed title if present, year if present.
356
+ # The entire line is the "summary" for display + TTS.
357
+ # ---------------------------------------------------
358
  def parse_arxiv_refs(ref_text: str):
359
  lines = ref_text.split('\n')
360
+ # remove empty lines
361
+ lines = [ln.strip() for ln in lines if ln.strip()]
362
+ # limit to 20
363
+ lines = lines[:20]
364
+
365
+ refs = []
366
+ for ln in lines:
367
+ # bracketed title if found
368
+ bracket_match = re.search(r"\[([^\]]+)\]", ln)
369
+ title = bracket_match.group(1) if bracket_match else "No Title"
370
+ # find a year 20xx if present
371
+ year_match = re.search(r"(20\d{2})", ln)
 
 
 
 
 
 
372
  year = int(year_match.group(1)) if year_match else None
373
+
374
+ refs.append({
375
+ "line": ln, # the entire raw line for display
376
+ "title": title, # bracketed content or "No Title"
377
+ "year": year # e.g. 2023, 2024, or None
378
  })
379
+ return refs
 
380
 
381
  def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
382
  titles_summary=True, full_audio=False):
383
+ """
384
+ 1) Query the RAG pipeline
385
+ 2) Display results
386
+ 3) Also parse references into lines, up to 20
387
+ 4) Show each reference with full content
388
+ 5) If year in [2023, 2024], auto-generate TTS
389
+ """
390
  start = time.time()
391
 
392
+ # 1) Query HF RAG pipeline
393
  client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
394
+ # 20 references
395
+ refs = client.predict(q, 20, "Semantic Search", "mistralai/Mixtral-8x7B-Instruct-v0.1",
396
+ api_name="/update_with_rag_md")[0]
397
+ # Main summary
398
  r2 = client.predict(q, "mistralai/Mixtral-8x7B-Instruct-v0.1", True, api_name="/ask_llm")
399
 
400
+ # 2) Combine for final text
401
  result = f"### 🔎 {q}\n\n{r2}\n\n{refs}"
402
  st.markdown(result)
403
 
404
+ # Optionally produce "all at once" TTS
405
  if full_audio:
406
  complete_text = f"Complete response for query: {q}. {clean_for_speech(r2)} {clean_for_speech(refs)}"
407
  audio_file_full = speak_with_edge_tts(complete_text)
408
  st.write("### 📚 Full Audio")
409
+ play_and_download_audio(audio_file_full)
410
 
411
  if vocal_summary:
412
  main_text = clean_for_speech(r2)
413
  audio_file_main = speak_with_edge_tts(main_text)
414
  st.write("### 🎙 Short Audio")
415
+ play_and_download_audio(audio_file_main)
416
 
417
  if extended_refs:
418
  summaries_text = "Extended references: " + refs.replace('"','')
419
  summaries_text = clean_for_speech(summaries_text)
420
  audio_file_refs = speak_with_edge_tts(summaries_text)
421
  st.write("### 📜 Long Refs")
422
+ play_and_download_audio(audio_file_refs)
423
+
424
+ # 3) Parse references
425
+ parsed = parse_arxiv_refs(refs)
426
+
427
+ # 4) Show references
428
+ st.write("## Individual Paper Lines (Up to 20)")
429
+ for i, ref in enumerate(parsed):
430
+ st.markdown(f"**Ref #{i+1}**: {ref['line']}")
431
+ if ref['year'] in [2023, 2024]:
432
+ # TTS content: "Title + entire line"
433
+ tts_text = f"Title: {ref['title']}. Full content: {ref['line']}"
434
+ out_fn = generate_audio_filename(q, ref['title'], ref['line'])
435
+ tmp_mp3 = speak_with_edge_tts(tts_text)
436
+ if tmp_mp3 and os.path.exists(tmp_mp3):
437
+ # rename to out_fn
438
+ os.rename(tmp_mp3, out_fn)
439
+ # auto-play
440
+ auto_play_audio(out_fn)
441
  st.write("---")
442
 
443
+ # Titles only block
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  if titles_summary:
445
+ # This was your older code - parse bracketed titles from each line
446
+ # to produce an all-in-one TTS if desired
447
+ lines = refs.split('\n')
448
  titles = []
449
+ for line in lines:
450
  m = re.search(r"\[([^\]]+)\]", line)
451
  if m:
452
  titles.append(m.group(1))
 
456
  audio_file_titles = speak_with_edge_tts(titles_text)
457
  st.write("### 🔖 Titles (All-In-One)")
458
  play_and_download_audio(audio_file_titles)
 
459
 
460
  elapsed = time.time() - start
461
  st.write(f"**Total Elapsed:** {elapsed:.2f} s")
462
 
463
+ # 5) Save entire text as MD file
464
  create_file(q, result, "md")
465
 
466
  return result
467
 
 
468
  def process_with_gpt(text):
469
  """Process text with GPT-4"""
470
  if not text:
 
504
 
505
  # 📂 10. File Management
506
  def create_zip_of_files(md_files, mp3_files):
507
+ """Create zip with a short naming approach"""
508
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
509
  all_files = md_files + mp3_files
510
  if not all_files:
 
536
  """Load and group files for sidebar display"""
537
  md_files = glob.glob("*.md")
538
  mp3_files = glob.glob("*.mp3")
 
539
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
 
540
 
541
+ all_files = md_files + mp3_files
542
  groups = defaultdict(list)
543
  for f in all_files:
544
  fname = os.path.basename(f)
545
+ prefix = fname[:10] # e.g. "2310_1205_"
546
  groups[prefix].append(f)
547
 
548
  for prefix in groups:
549
  groups[prefix].sort(key=lambda x: os.path.getmtime(x), reverse=True)
550
 
551
  sorted_prefixes = sorted(groups.keys(),
552
+ key=lambda pre: max(os.path.getmtime(x) for x in groups[pre]),
553
+ reverse=True)
554
  return groups, sorted_prefixes
555
 
556
  def extract_keywords_from_md(files):
 
590
  if st.button("⬇️ ZipAll"):
591
  z = create_zip_of_files(all_md, all_mp3)
592
  if z:
593
+ st.sidebar.markdown(get_download_link(z), unsafe_allow_html=True)
594
 
595
  for prefix in sorted_prefixes:
596
  files = groups[prefix]
597
  kw = extract_keywords_from_md(files)
598
  keywords_str = " ".join(kw) if kw else "No Keywords"
599
  with st.sidebar.expander(f"{prefix} Files ({len(files)}) - KW: {keywords_str}", expanded=True):
600
+ c1,c2 = st.columns(2)
601
  with c1:
602
  if st.button("👀ViewGrp", key="view_group_"+prefix):
603
  st.session_state.viewing_prefix = prefix
 
618
  st.sidebar.markdown("### 🚲BikeAI🏆 Multi-Agent Research")
619
  tab_main = st.radio("Action:", ["🎤 Voice","📸 Media","🔍 ArXiv","📝 Editor"], horizontal=True)
620
 
621
+ # If you have a custom React component
622
  mycomponent = components.declare_component("mycomponent", path="mycomponent")
623
  val = mycomponent(my_input_value="Hello")
624
 
 
644
  extended_refs=False,
645
  titles_summary=True,
646
  full_audio=full_audio)
647
+ elif run_option == "GPT-4o":
648
+ process_with_gpt(edited_input)
649
+ elif run_option == "Claude-3.5":
650
+ process_with_claude(edited_input)
 
651
  else:
652
  if st.button("▶ Run"):
653
  st.session_state.old_val = val
 
657
  extended_refs=False,
658
  titles_summary=True,
659
  full_audio=full_audio)
660
+ elif run_option == "GPT-4o":
661
+ process_with_gpt(edited_input)
662
+ elif run_option == "Claude-3.5":
663
+ process_with_claude(edited_input)
 
664
 
665
  if tab_main == "🔍 ArXiv":
666
  st.subheader("🔍 Query ArXiv")
 
718
  with tabs[0]:
719
  imgs = glob.glob("*.png")+glob.glob("*.jpg")
720
  if imgs:
721
+ c = st.slider("Cols", 1, 5, 3)
722
  cols = st.columns(c)
723
  for i, f in enumerate(imgs):
724
  with cols[i % c]:
 
741
  st.write("No videos found.")
742
 
743
  elif tab_main == "📝 Editor":
744
+ if getattr(st.session_state, 'current_file', None):
745
  st.subheader(f"Editing: {st.session_state.current_file}")
746
  new_text = st.text_area("✏️ Content:", st.session_state.file_content, height=300)
747
  if st.button("💾 Save"):
 
752
  else:
753
  st.write("Select a file from the sidebar to edit.")
754
 
755
+ # File manager in sidebar
756
  groups, sorted_prefixes = load_files_for_sidebar()
757
  display_file_manager_sidebar(groups, sorted_prefixes)
758
 
759
+ # If user clicked "view group"
760
  if st.session_state.viewing_prefix and st.session_state.viewing_prefix in groups:
761
  st.write("---")
762
  st.write(f"**Viewing Group:** {st.session_state.viewing_prefix}")
 
778
  st.session_state.should_rerun = False
779
  st.rerun()
780
 
 
781
  if __name__ == "__main__":
782
  main()