awacke1 commited on
Commit
8aa93ff
·
verified ·
1 Parent(s): ec1ca1c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -322
app.py CHANGED
@@ -2,36 +2,12 @@ import streamlit as st
2
  import anthropic
3
  import openai
4
  import base64
5
- import cv2
6
- import glob
7
- import json
8
- import math
9
  import os
10
- import pytz
11
- import random
12
  import re
13
- import requests
14
- import textract
15
- import time
16
- import zipfile
17
- import plotly.graph_objects as go
18
- import streamlit.components.v1 as components
19
  from datetime import datetime
20
- from audio_recorder_streamlit import audio_recorder
21
- from bs4 import BeautifulSoup
22
- from collections import defaultdict, deque
23
- from dotenv import load_dotenv
24
  from gradio_client import Client
25
- from huggingface_hub import InferenceClient
26
- from io import BytesIO
27
- from PIL import Image
28
- from PyPDF2 import PdfReader
29
- from urllib.parse import quote
30
- from xml.etree import ElementTree as ET
31
- from openai import OpenAI
32
- import extra_streamlit_components as stx
33
- from streamlit.runtime.scriptrunner import get_script_run_ctx
34
- import asyncio
35
  import edge_tts
36
 
37
  # 🎯 1. Core Configuration & Setup
@@ -46,12 +22,22 @@ st.set_page_config(
46
  'About': "🚲BikeAI🏆 Claude/GPT Research AI"
47
  }
48
  )
49
- load_dotenv()
 
 
 
 
 
 
 
 
50
 
51
  # 🔑 2. API Setup & Clients
 
 
 
52
  openai_api_key = os.getenv('OPENAI_API_KEY', "")
53
  anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', "")
54
- xai_key = os.getenv('xai',"")
55
  if 'OPENAI_API_KEY' in st.secrets:
56
  openai_api_key = st.secrets['OPENAI_API_KEY']
57
  if 'ANTHROPIC_API_KEY' in st.secrets:
@@ -59,9 +45,7 @@ if 'ANTHROPIC_API_KEY' in st.secrets:
59
 
60
  openai.api_key = openai_api_key
61
  claude_client = anthropic.Anthropic(api_key=anthropic_key)
62
- openai_client = OpenAI(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
63
- HF_KEY = os.getenv('HF_KEY')
64
- API_URL = os.getenv('API_URL')
65
 
66
  # 📝 3. Session State Management
67
  if 'transcript_history' not in st.session_state:
@@ -69,17 +53,9 @@ if 'transcript_history' not in st.session_state:
69
  if 'chat_history' not in st.session_state:
70
  st.session_state['chat_history'] = []
71
  if 'openai_model' not in st.session_state:
72
- st.session_state['openai_model'] = "gpt-4o-2024-05-13"
73
  if 'messages' not in st.session_state:
74
  st.session_state['messages'] = []
75
- if 'last_voice_input' not in st.session_state:
76
- st.session_state['last_voice_input'] = ""
77
- if 'editing_file' not in st.session_state:
78
- st.session_state['editing_file'] = None
79
- if 'edit_new_name' not in st.session_state:
80
- st.session_state['edit_new_name'] = ""
81
- if 'edit_new_content' not in st.session_state:
82
- st.session_state['edit_new_content'] = ""
83
  if 'viewing_prefix' not in st.session_state:
84
  st.session_state['viewing_prefix'] = None
85
  if 'should_rerun' not in st.session_state:
@@ -87,23 +63,7 @@ if 'should_rerun' not in st.session_state:
87
  if 'old_val' not in st.session_state:
88
  st.session_state['old_val'] = None
89
 
90
- # 🎨 4. Custom CSS
91
- st.markdown("""
92
- <style>
93
- .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
94
- .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
95
- .stButton>button {
96
- margin-right: 0.5rem;
97
- }
98
- </style>
99
- """, unsafe_allow_html=True)
100
-
101
- FILE_EMOJIS = {
102
- "md": "📝",
103
- "mp3": "🎵",
104
- }
105
-
106
- # 🧠 5. High-Information Content Extraction
107
  def get_high_info_terms(text: str) -> list:
108
  """Extract high-information terms from text, including key phrases."""
109
  stop_words = set([
@@ -164,7 +124,7 @@ def clean_text_for_filename(text: str) -> str:
164
  filtered = [w for w in words if len(w)>3 and w not in stop_short]
165
  return '_'.join(filtered)[:200]
166
 
167
- # 📁 6. File Operations
168
  def generate_filename(prompt, response, file_type="md"):
169
  """
170
  Generate filename with meaningful terms and short dense clips from prompt & response.
@@ -202,7 +162,7 @@ def get_download_link(file):
202
  b64 = base64.b64encode(f.read()).decode()
203
  return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">📂 Download {os.path.basename(file)}</a>'
204
 
205
- # 🔊 7. Audio Processing
206
  def clean_for_speech(text: str) -> str:
207
  """Clean text for speech synthesis"""
208
  text = text.replace("\n", " ")
@@ -212,20 +172,7 @@ def clean_for_speech(text: str) -> str:
212
  text = re.sub(r"\s+", " ", text).strip()
213
  return text
214
 
215
- @st.cache_resource
216
- def speech_synthesis_html(result):
217
- """Create HTML for speech synthesis"""
218
- html_code = f"""
219
- <html><body>
220
- <script>
221
- var msg = new SpeechSynthesisUtterance("{result.replace('"', '')}");
222
- window.speechSynthesis.speak(msg);
223
- </script>
224
- </body></html>
225
- """
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():
@@ -233,13 +180,12 @@ async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=
233
  rate_str = f"{rate:+d}%"
234
  pitch_str = f"{pitch:+d}Hz"
235
  communicate = edge_tts.Communicate(text, voice, rate=rate_str, pitch=pitch_str)
236
- out_fn = generate_filename(text, text, "mp3")
237
  await communicate.save(out_fn)
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"""
@@ -249,10 +195,7 @@ def play_and_download_audio(file_path):
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.
254
- Note: Some browsers block audio autoplay if there's no user interaction.
255
- """
256
  if not file_path or not os.path.exists(file_path):
257
  return
258
  with open(file_path, "rb") as f:
@@ -271,45 +214,44 @@ def auto_play_audio(file_path):
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")
288
- resp = openai_client.chat.completions.create(
289
  model=st.session_state["openai_model"],
290
  messages=[
291
  {"role": "system", "content": "You are a helpful assistant."},
292
- {
293
- "role": "user",
294
- "content": [
295
- {"type": "text", "text": user_prompt},
296
- {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64img}"}}
297
- ]
298
- }
299
  ],
300
  temperature=0.0,
301
  )
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,145 +268,25 @@ def process_video(video_path, seconds_per_frame=1):
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"],
333
  messages=[
334
  {"role": "system", "content": "Analyze video frames."},
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
- }
342
  ]
343
  )
344
  return resp.choices[0].message.content
345
 
346
- # 🤖 9. AI Model Integration
347
-
348
  def save_full_transcript(query, text):
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))
453
- if titles:
454
- titles_text = "Titles: " + ", ".join(titles)
455
- titles_text = clean_for_speech(titles_text)
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:
@@ -473,13 +295,13 @@ def process_with_gpt(text):
473
  with st.chat_message("user"):
474
  st.markdown(text)
475
  with st.chat_message("assistant"):
476
- c = openai_client.chat.completions.create(
477
  model=st.session_state["openai_model"],
478
  messages=st.session_state.messages,
479
  stream=False
480
  )
481
  ans = c.choices[0].message.content
482
- st.write("GPT-4o: " + ans)
483
  create_file(text, ans, "md")
484
  st.session_state.messages.append({"role":"assistant","content":ans})
485
  return ans
@@ -491,20 +313,20 @@ def process_with_claude(text):
491
  with st.chat_message("user"):
492
  st.markdown(text)
493
  with st.chat_message("assistant"):
494
- r = claude_client.messages.create(
495
- model="claude-3-sonnet-20240229",
496
- max_tokens=1000,
497
- messages=[{"role":"user","content":text}]
498
  )
499
- ans = r.content[0].text
500
  st.write("Claude-3.5: " + ans)
501
  create_file(text, ans, "md")
502
  st.session_state.chat_history.append({"user":text,"claude":ans})
503
  return ans
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:
@@ -542,7 +364,7 @@ def load_files_for_sidebar():
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:
@@ -613,87 +435,32 @@ def display_file_manager_sidebar(groups, sorted_prefixes):
613
  ctime = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%Y-%m-%d %H:%M:%S")
614
  st.write(f"**{fname}** - {ctime}")
615
 
616
- # 🎯 11. Main Application
617
  def main():
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
-
625
- # Show input in a text box for editing if detected
626
- if val:
627
- val_stripped = val.replace('\n', ' ')
628
- edited_input = st.text_area("✏️ Edit Input:", value=val_stripped, height=100)
629
- run_option = st.selectbox("Model:", ["Arxiv", "GPT-4o", "Claude-3.5"])
630
- col1, col2 = st.columns(2)
631
- with col1:
632
- autorun = st.checkbox("⚙ AutoRun", value=True)
633
- with col2:
634
- full_audio = st.checkbox("📚FullAudio", value=False,
635
- help="Generate full audio response")
636
-
637
- input_changed = (val != st.session_state.old_val)
638
-
639
- if autorun and input_changed:
640
- st.session_state.old_val = val
641
- if run_option == "Arxiv":
642
- perform_ai_lookup(edited_input,
643
- vocal_summary=True,
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
654
- if run_option == "Arxiv":
655
- perform_ai_lookup(edited_input,
656
- vocal_summary=True,
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")
667
  q = st.text_input("🔍 Query:")
668
 
669
  st.markdown("### 🎛 Options")
670
- vocal_summary = st.checkbox("🎙ShortAudio", value=True)
671
- extended_refs = st.checkbox("📜LongRefs", value=False)
672
- titles_summary = st.checkbox("🔖TitlesOnly", value=True)
673
- full_audio = st.checkbox("📚FullAudio", value=False,
674
- help="Full audio of results")
675
- full_transcript = st.checkbox("🧾FullTranscript", value=False,
676
- help="Generate a full transcript file")
677
-
678
- if q and st.button("🔍Run"):
679
- result = perform_ai_lookup(q,
680
- vocal_summary=vocal_summary,
681
- extended_refs=extended_refs,
682
- titles_summary=titles_summary,
683
- full_audio=full_audio)
684
- if full_transcript:
685
- save_full_transcript(q, result)
686
-
687
- st.markdown("### Change Prompt & Re-Run")
688
- q_new = st.text_input("🔄 Modify Query:")
689
- if q_new and st.button("🔄 Re-Run with Modified Query"):
690
- result = perform_ai_lookup(q_new,
691
- vocal_summary=vocal_summary,
692
- extended_refs=extended_refs,
693
- titles_summary=titles_summary,
694
- full_audio=full_audio)
695
  if full_transcript:
696
- save_full_transcript(q_new, result)
697
 
698
  elif tab_main == "🎤 Voice":
699
  st.subheader("🎤 Voice Input")
@@ -702,7 +469,7 @@ def main():
702
  if st.button("📨 Send"):
703
  process_with_gpt(user_text)
704
  st.subheader("📜 Chat History")
705
- t1, t2 = st.tabs(["Claude History","GPT-4o History"])
706
  with t1:
707
  for c in st.session_state.chat_history:
708
  st.write("**You:**", c["user"])
@@ -716,12 +483,11 @@ def main():
716
  st.header("📸 Images & 🎥 Videos")
717
  tabs = st.tabs(["🖼 Images", "🎥 Video"])
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]:
725
  st.image(Image.open(f), use_container_width=True)
726
  if st.button(f"👀 Analyze {os.path.basename(f)}", key=f"analyze_{f}"):
727
  a = process_image(f, "Describe this image.")
@@ -729,28 +495,33 @@ def main():
729
  else:
730
  st.write("No images found.")
731
  with tabs[1]:
732
- vids = glob.glob("*.mp4")
733
  if vids:
734
  for v in vids:
735
  with st.expander(f"🎥 {os.path.basename(v)}"):
736
  st.video(v)
737
  if st.button(f"Analyze {os.path.basename(v)}", key=f"analyze_{v}"):
738
- a = process_video_with_gpt(v, "Describe video.")
739
  st.markdown(a)
740
  else:
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"):
748
- with open(st.session_state.current_file, 'w', encoding='utf-8') as f:
749
- f.write(new_text)
750
- st.success("Updated!")
751
- st.session_state.should_rerun = True
 
 
 
 
 
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()
@@ -776,7 +547,120 @@ def main():
776
 
777
  if st.session_state.should_rerun:
778
  st.session_state.should_rerun = False
779
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
780
 
 
781
  if __name__ == "__main__":
782
  main()
 
2
  import anthropic
3
  import openai
4
  import base64
 
 
 
 
5
  import os
 
 
6
  import re
7
+ import asyncio
 
 
 
 
 
8
  from datetime import datetime
 
 
 
 
9
  from gradio_client import Client
10
+ from collections import defaultdict
 
 
 
 
 
 
 
 
 
11
  import edge_tts
12
 
13
  # 🎯 1. Core Configuration & Setup
 
22
  'About': "🚲BikeAI🏆 Claude/GPT Research AI"
23
  }
24
  )
25
+ st.markdown("""
26
+ <style>
27
+ .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
28
+ .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
29
+ .stButton>button {
30
+ margin-right: 0.5rem;
31
+ }
32
+ </style>
33
+ """, unsafe_allow_html=True)
34
 
35
  # 🔑 2. API Setup & Clients
36
+ from dotenv import load_dotenv
37
+ load_dotenv()
38
+
39
  openai_api_key = os.getenv('OPENAI_API_KEY', "")
40
  anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', "")
 
41
  if 'OPENAI_API_KEY' in st.secrets:
42
  openai_api_key = st.secrets['OPENAI_API_KEY']
43
  if 'ANTHROPIC_API_KEY' in st.secrets:
 
45
 
46
  openai.api_key = openai_api_key
47
  claude_client = anthropic.Anthropic(api_key=anthropic_key)
48
+ openai_client = openai # Using OpenAI directly
 
 
49
 
50
  # 📝 3. Session State Management
51
  if 'transcript_history' not in st.session_state:
 
53
  if 'chat_history' not in st.session_state:
54
  st.session_state['chat_history'] = []
55
  if 'openai_model' not in st.session_state:
56
+ st.session_state['openai_model'] = "gpt-4" # Update as needed
57
  if 'messages' not in st.session_state:
58
  st.session_state['messages'] = []
 
 
 
 
 
 
 
 
59
  if 'viewing_prefix' not in st.session_state:
60
  st.session_state['viewing_prefix'] = None
61
  if 'should_rerun' not in st.session_state:
 
63
  if 'old_val' not in st.session_state:
64
  st.session_state['old_val'] = None
65
 
66
+ # 🧠 4. High-Information Content Extraction
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  def get_high_info_terms(text: str) -> list:
68
  """Extract high-information terms from text, including key phrases."""
69
  stop_words = set([
 
124
  filtered = [w for w in words if len(w)>3 and w not in stop_short]
125
  return '_'.join(filtered)[:200]
126
 
127
+ # 📁 5. File Operations
128
  def generate_filename(prompt, response, file_type="md"):
129
  """
130
  Generate filename with meaningful terms and short dense clips from prompt & response.
 
162
  b64 = base64.b64encode(f.read()).decode()
163
  return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">📂 Download {os.path.basename(file)}</a>'
164
 
165
+ # 🔊 6. Audio Processing
166
  def clean_for_speech(text: str) -> str:
167
  """Clean text for speech synthesis"""
168
  text = text.replace("\n", " ")
 
172
  text = re.sub(r"\s+", " ", text).strip()
173
  return text
174
 
175
+ async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0, out_fn="temp.mp3"):
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  """Generate audio using Edge TTS (async)"""
177
  text = clean_for_speech(text)
178
  if not text.strip():
 
180
  rate_str = f"{rate:+d}%"
181
  pitch_str = f"{pitch:+d}Hz"
182
  communicate = edge_tts.Communicate(text, voice, rate=rate_str, pitch=pitch_str)
 
183
  await communicate.save(out_fn)
184
  return out_fn
185
 
186
+ def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0, out_fn="temp.mp3"):
187
+ """Wrapper for Edge TTS generation (sync)"""
188
+ return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch, out_fn))
189
 
190
  def play_and_download_audio(file_path):
191
  """Play and provide a download link for audio"""
 
195
  st.markdown(dl_link, unsafe_allow_html=True)
196
 
197
  def auto_play_audio(file_path):
198
+ """Embeds an <audio> tag with autoplay + controls + a download link."""
 
 
 
199
  if not file_path or not os.path.exists(file_path):
200
  return
201
  with open(file_path, "rb") as f:
 
214
 
215
  def generate_audio_filename(query, title, summary):
216
  """
217
+ Generate a specialized MP3 filename using query + title + summary.
218
+ Example: "2310_1205_query_title_summary.mp3"
219
  """
220
  combined = (query + " " + title + " " + summary).strip().lower()
221
+ combined = re.sub(r'[^\w\s-]', '', combined) # Remove special characters
222
+ combined = "_".join(combined.split())[:80] # Limit length
223
  prefix = datetime.now().strftime("%y%m_%H%M")
224
  return f"{prefix}_{combined}.mp3"
225
 
226
+ # 🎬 7. Media Processing
227
  def process_image(image_path, user_prompt):
228
+ """Process image with GPT-4V"""
229
  with open(image_path, "rb") as imgf:
230
  image_data = imgf.read()
231
  b64img = base64.b64encode(image_data).decode("utf-8")
232
+ resp = openai_client.ChatCompletion.create(
233
  model=st.session_state["openai_model"],
234
  messages=[
235
  {"role": "system", "content": "You are a helpful assistant."},
236
+ {"role": "user", "content": [
237
+ {"type": "text", "text": user_prompt},
238
+ {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64img}"}}
239
+ ]}
 
 
 
240
  ],
241
  temperature=0.0,
242
  )
243
  return resp.choices[0].message.content
244
 
245
+ def process_audio_with_whisper(audio_path):
246
+ """Process audio with Whisper"""
247
  with open(audio_path, "rb") as f:
248
+ transcription = openai_client.Audio.transcriptions.create(model="whisper-1", file=f)
249
  st.session_state.messages.append({"role": "user", "content": transcription.text})
250
  return transcription.text
251
 
252
  def process_video(video_path, seconds_per_frame=1):
253
+ """Extract frames from video"""
254
+ import cv2
255
  vid = cv2.VideoCapture(video_path)
256
  total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
257
  fps = vid.get(cv2.CAP_PROP_FPS)
 
268
  return frames_b64
269
 
270
  def process_video_with_gpt(video_path, prompt):
271
+ """Analyze video frames with GPT-4V"""
272
  frames = process_video(video_path)
273
+ resp = openai_client.ChatCompletion.create(
274
  model=st.session_state["openai_model"],
275
  messages=[
276
  {"role": "system", "content": "Analyze video frames."},
277
+ {"role": "user", "content": [
278
+ {"type": "text", "text": prompt},
279
+ *[{"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{fr}"}} for fr in frames]
280
+ ]}
 
 
 
281
  ]
282
  )
283
  return resp.choices[0].message.content
284
 
285
+ # 🤖 8. AI Model Integration
 
286
  def save_full_transcript(query, text):
287
  """Save full transcript of Arxiv results as a file."""
288
  create_file(query, text, "md")
289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  def process_with_gpt(text):
291
  """Process text with GPT-4"""
292
  if not text:
 
295
  with st.chat_message("user"):
296
  st.markdown(text)
297
  with st.chat_message("assistant"):
298
+ c = openai_client.ChatCompletion.create(
299
  model=st.session_state["openai_model"],
300
  messages=st.session_state.messages,
301
  stream=False
302
  )
303
  ans = c.choices[0].message.content
304
+ st.write("GPT-4: " + ans)
305
  create_file(text, ans, "md")
306
  st.session_state.messages.append({"role":"assistant","content":ans})
307
  return ans
 
313
  with st.chat_message("user"):
314
  st.markdown(text)
315
  with st.chat_message("assistant"):
316
+ r = claude_client.completions.create(
317
+ prompt=text,
318
+ model="claude-3",
319
+ max_tokens=1000
320
  )
321
+ ans = r['completion']
322
  st.write("Claude-3.5: " + ans)
323
  create_file(text, ans, "md")
324
  st.session_state.chat_history.append({"user":text,"claude":ans})
325
  return ans
326
 
327
+ # 📂 9. File Management
328
  def create_zip_of_files(md_files, mp3_files):
329
+ """Create zip with intelligent naming"""
330
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
331
  all_files = md_files + mp3_files
332
  if not all_files:
 
364
  groups = defaultdict(list)
365
  for f in all_files:
366
  fname = os.path.basename(f)
367
+ prefix = fname[:10] # e.g., "2310_1205_"
368
  groups[prefix].append(f)
369
 
370
  for prefix in groups:
 
435
  ctime = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%Y-%m-%d %H:%M:%S")
436
  st.write(f"**{fname}** - {ctime}")
437
 
438
+ # 🎯 10. Main Application
439
  def main():
440
  st.sidebar.markdown("### 🚲BikeAI🏆 Multi-Agent Research")
441
  tab_main = st.radio("Action:", ["🎤 Voice","📸 Media","🔍 ArXiv","📝 Editor"], horizontal=True)
442
 
443
+ # Placeholder for custom component if needed
444
+ # mycomponent = components.declare_component("mycomponent", path="mycomponent")
445
+ # val = mycomponent(my_input_value="Hello")
446
+
447
+ # Example input handling
448
+ # if val:
449
+ # # Handle custom component input
450
+ # pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
  if tab_main == "🔍 ArXiv":
453
  st.subheader("🔍 Query ArXiv")
454
  q = st.text_input("🔍 Query:")
455
 
456
  st.markdown("### 🎛 Options")
457
+ full_audio = st.checkbox("📚 Full Audio", value=False, help="Generate full audio response")
458
+ full_transcript = st.checkbox("🧾 Full Transcript", value=False, help="Generate a full transcript file")
459
+
460
+ if q and st.button("🔍 Run Query"):
461
+ perform_ai_lookup(q)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  if full_transcript:
463
+ create_file(q, "Full transcript generated.", "md") # Customize as needed
464
 
465
  elif tab_main == "🎤 Voice":
466
  st.subheader("🎤 Voice Input")
 
469
  if st.button("📨 Send"):
470
  process_with_gpt(user_text)
471
  st.subheader("📜 Chat History")
472
+ t1, t2 = st.tabs(["Claude History","GPT-4 History"])
473
  with t1:
474
  for c in st.session_state.chat_history:
475
  st.write("**You:**", c["user"])
 
483
  st.header("📸 Images & 🎥 Videos")
484
  tabs = st.tabs(["🖼 Images", "🎥 Video"])
485
  with tabs[0]:
486
+ imgs = glob.glob("*.png") + glob.glob("*.jpg") + glob.glob("*.jpeg")
487
  if imgs:
488
+ cols = st.columns(st.slider("Cols", 1, 5, 3))
 
489
  for i, f in enumerate(imgs):
490
+ with cols[i % len(cols)]:
491
  st.image(Image.open(f), use_container_width=True)
492
  if st.button(f"👀 Analyze {os.path.basename(f)}", key=f"analyze_{f}"):
493
  a = process_image(f, "Describe this image.")
 
495
  else:
496
  st.write("No images found.")
497
  with tabs[1]:
498
+ vids = glob.glob("*.mp4") + glob.glob("*.avi") + glob.glob("*.mov")
499
  if vids:
500
  for v in vids:
501
  with st.expander(f"🎥 {os.path.basename(v)}"):
502
  st.video(v)
503
  if st.button(f"Analyze {os.path.basename(v)}", key=f"analyze_{v}"):
504
+ a = process_video_with_gpt(v, "Describe this video.")
505
  st.markdown(a)
506
  else:
507
  st.write("No videos found.")
508
 
509
  elif tab_main == "📝 Editor":
510
+ st.subheader("📝 File Editor")
511
+ # Example editor logic: list markdown files and allow editing
512
+ md_files = glob.glob("*.md")
513
+ if md_files:
514
+ selected_file = st.selectbox("Select a file to edit:", md_files)
515
+ with st.form("edit_form"):
516
+ new_content = st.text_area("✏️ Content:", open(selected_file, 'r', encoding='utf-8').read(), height=300)
517
+ submitted = st.form_submit_button("💾 Save")
518
+ if submitted:
519
+ with open(selected_file, 'w', encoding='utf-8') as f:
520
+ f.write(new_content)
521
+ st.success(f"Updated {selected_file}!")
522
+ st.session_state.should_rerun = True
523
  else:
524
+ st.write("No markdown files available to edit.")
525
 
526
  # File manager in sidebar
527
  groups, sorted_prefixes = load_files_for_sidebar()
 
547
 
548
  if st.session_state.should_rerun:
549
  st.session_state.should_rerun = False
550
+ st.experimental_rerun()
551
+
552
+ def parse_arxiv_papers(ref_text: str):
553
+ """
554
+ Splits the references into paper-level chunks.
555
+ Each paper starts with a number followed by a parenthesis, e.g., "1) [Title (Year)] Summary..."
556
+ Returns a list of dictionaries with 'title', 'summary', and 'year'.
557
+ Limits to 20 papers.
558
+ """
559
+ # Split based on patterns like "1) ", "2) ", etc.
560
+ chunks = re.split(r'\n?\d+\)\s+', ref_text)
561
+ # Remove any empty strings resulting from split
562
+ chunks = [chunk.strip() for chunk in chunks if chunk.strip()]
563
+ papers = []
564
+ for chunk in chunks[:20]:
565
+ # Extract title within brackets if present
566
+ title_match = re.search(r'\[([^\]]+)\]', chunk)
567
+ title = title_match.group(1).strip() if title_match else "No Title"
568
+
569
+ # Extract year (assuming it's a 4-digit number within the title or summary)
570
+ year_match = re.search(r'\b(20\d{2})\b', chunk)
571
+ year = int(year_match.group(1)) if year_match else None
572
+
573
+ # The entire chunk is considered the summary
574
+ summary = chunk
575
+
576
+ papers.append({
577
+ 'title': title,
578
+ 'summary': summary,
579
+ 'year': year
580
+ })
581
+ return papers
582
+
583
+ def perform_ai_lookup(q):
584
+ """
585
+ Performs the Arxiv search and handles the processing of results.
586
+ Generates audio files for each paper (if year is 2023 or 2024).
587
+ """
588
+ st.write(f"## Query: {q}")
589
+
590
+ # 1) Query the HF RAG pipeline
591
+ client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
592
+ refs = client.predict(q, 20, "Semantic Search", "mistralai/Mixtral-8x7B-Instruct-v0.1", api_name="/update_with_rag_md")[0]
593
+ r2 = client.predict(q, "mistralai/Mixtral-8x7B-Instruct-v0.1", True, api_name="/ask_llm")
594
+
595
+ # 2) Combine for final text output
596
+ result = f"### 🔎 {q}\n\n{r2}\n\n{refs}"
597
+ st.markdown(result)
598
+
599
+ # 3) Parse references into papers
600
+ papers = parse_arxiv_papers(refs)
601
+
602
+ # 4) Display each paper and generate audio if applicable
603
+ st.write("## Individual Papers (Up to 20)")
604
+ for idx, paper in enumerate(papers):
605
+ year_str = paper["year"] if paper["year"] else "Unknown Year"
606
+ st.markdown(f"**Paper #{idx+1}: {paper['title']}** \n*Year:* {year_str}")
607
+ st.markdown(f"*Summary:* {paper['summary']}")
608
+ st.write("---")
609
+
610
+ # Generate TTS if year is 2023 or 2024
611
+ if paper["year"] in [2023, 2024]:
612
+ # Combine title and summary for TTS
613
+ tts_text = f"Title: {paper['title']}. Summary: {paper['summary']}"
614
+ # Generate a specialized filename
615
+ mp3_filename = generate_audio_filename(q, paper['title'], paper['summary'])
616
+ # Generate audio using Edge TTS
617
+ temp_mp3 = speak_with_edge_tts(tts_text, out_fn=mp3_filename)
618
+ if temp_mp3 and os.path.exists(mp3_filename):
619
+ # Embed the audio player with auto-play and download link
620
+ auto_play_audio(mp3_filename)
621
+
622
+ # Optionally save the full transcript
623
+ st.write("### Transcript")
624
+ st.markdown(result)
625
+ create_file(q, result, "md")
626
+
627
+ def process_with_gpt(text):
628
+ """Process text with GPT-4"""
629
+ if not text:
630
+ return
631
+ st.session_state.messages.append({"role":"user","content":text})
632
+ with st.chat_message("user"):
633
+ st.markdown(text)
634
+ with st.chat_message("assistant"):
635
+ c = openai_client.ChatCompletion.create(
636
+ model=st.session_state["openai_model"],
637
+ messages=st.session_state.messages,
638
+ stream=False
639
+ )
640
+ ans = c.choices[0].message.content
641
+ st.write("GPT-4: " + ans)
642
+ create_file(text, ans, "md")
643
+ st.session_state.messages.append({"role":"assistant","content":ans})
644
+ return ans
645
+
646
+ def process_with_claude(text):
647
+ """Process text with Claude"""
648
+ if not text:
649
+ return
650
+ with st.chat_message("user"):
651
+ st.markdown(text)
652
+ with st.chat_message("assistant"):
653
+ r = claude_client.completions.create(
654
+ prompt=text,
655
+ model="claude-3",
656
+ max_tokens=1000
657
+ )
658
+ ans = r['completion']
659
+ st.write("Claude-3.5: " + ans)
660
+ create_file(text, ans, "md")
661
+ st.session_state.chat_history.append({"user":text,"claude":ans})
662
+ return ans
663
 
664
+ # Run the app
665
  if __name__ == "__main__":
666
  main()