awacke1 commited on
Commit
fe0cd5e
·
verified ·
1 Parent(s): adc0603

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -551
app.py CHANGED
@@ -19,11 +19,8 @@ import extra_streamlit_components as stx
19
  from streamlit.runtime.scriptrunner import get_script_run_ctx
20
  import asyncio
21
  import edge_tts
22
-
23
- # Imports section (add streamlit-marquee)
24
  from streamlit_marquee import streamlit_marquee
25
 
26
-
27
  # 🎯 1. Core Configuration & Setup
28
  st.set_page_config(
29
  page_title="🚲TalkingAIResearcher🏆",
@@ -38,40 +35,6 @@ st.set_page_config(
38
  )
39
  load_dotenv()
40
 
41
-
42
- def display_marquee_controls():
43
- st.sidebar.markdown("### 🎯 Marquee Settings")
44
- cols = st.sidebar.columns(2)
45
- with cols[0]:
46
- bg_color = st.color_picker("🎨 Background", "#1E1E1E", key="bg_color_picker")
47
- text_color = st.color_picker("✍️ Text", "#FFFFFF", key="text_color_picker")
48
- with cols[1]:
49
- font_size = st.slider("📏 Size", 10, 24, 14, key="font_size_slider")
50
- duration = st.slider("⏱️ Speed", 1, 20, 10, key="duration_slider")
51
-
52
- return {
53
- "background": bg_color,
54
- "color": text_color,
55
- "font-size": f"{font_size}px",
56
- "animationDuration": f"{duration}s",
57
- "width": "100%",
58
- "lineHeight": "35px"
59
- }
60
-
61
-
62
- def display_marquee_controls_papers():
63
- return {
64
- "background": "#1E1E1E",
65
- "color": "#1E1E1E",
66
- "font-size": f"24px",
67
- "animationDuration": f"20s",
68
- "width": "100%",
69
- "lineHeight": "35px"
70
- }
71
-
72
- # Add global marquee settings
73
- marquee_settings = display_marquee_controls()
74
-
75
  # Add available English voices for Edge TTS
76
  EDGE_TTS_VOICES = [
77
  "en-US-AriaNeural", # Default voice
@@ -87,26 +50,9 @@ EDGE_TTS_VOICES = [
87
 
88
  # Initialize session state variables
89
  if 'tts_voice' not in st.session_state:
90
- st.session_state['tts_voice'] = EDGE_TTS_VOICES[0] # Default voice
91
  if 'audio_format' not in st.session_state:
92
- st.session_state['audio_format'] = 'mp3' # 🆕 Default audio format
93
-
94
- # 🔑 2. API Setup & Clients
95
- openai_api_key = os.getenv('OPENAI_API_KEY', "")
96
- anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', "")
97
- xai_key = os.getenv('xai',"")
98
- if 'OPENAI_API_KEY' in st.secrets:
99
- openai_api_key = st.secrets['OPENAI_API_KEY']
100
- if 'ANTHROPIC_API_KEY' in st.secrets:
101
- anthropic_key = st.secrets["ANTHROPIC_API_KEY"]
102
-
103
- openai.api_key = openai_api_key
104
- claude_client = anthropic.Anthropic(api_key=anthropic_key)
105
- openai_client = OpenAI(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
106
- HF_KEY = os.getenv('HF_KEY')
107
- API_URL = os.getenv('API_URL')
108
-
109
- # 📝 3. Session State Management
110
  if 'transcript_history' not in st.session_state:
111
  st.session_state['transcript_history'] = []
112
  if 'chat_history' not in st.session_state:
@@ -130,109 +76,115 @@ if 'should_rerun' not in st.session_state:
130
  if 'old_val' not in st.session_state:
131
  st.session_state['old_val'] = None
132
  if 'last_query' not in st.session_state:
133
- st.session_state['last_query'] = "" # 🆕 Store the last query for zip naming
 
 
134
 
135
- # 🎨 4. Custom CSS
136
- st.markdown("""
137
- <style>
138
- .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
139
- .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
140
- .stButton>button {
141
- margin-right: 0.5rem;
142
- }
143
- </style>
144
- """, unsafe_allow_html=True)
145
 
 
 
 
 
 
 
 
146
  FILE_EMOJIS = {
147
  "md": "📝",
148
  "mp3": "🎵",
149
- "wav": "🔊" # 🆕 Add emoji for WAV
150
  }
151
 
152
- # 🧠 5. High-Information Content Extraction
153
- def get_high_info_terms(text: str, top_n=10) -> list:
154
- """Extract high-information terms from text, including key phrases."""
155
- stop_words = set([
156
- 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with',
157
- 'by', 'from', 'up', 'about', 'into', 'over', 'after', 'is', 'are', 'was', 'were',
158
- 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
159
- 'should', 'could', 'might', 'must', 'shall', 'can', 'may', 'this', 'that', 'these',
160
- 'those', 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'what', 'which', 'who',
161
- 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most',
162
- 'other', 'some', 'such', 'than', 'too', 'very', 'just', 'there'
163
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
- key_phrases = [
166
- 'artificial intelligence', 'machine learning', 'deep learning', 'neural network',
167
- 'personal assistant', 'natural language', 'computer vision', 'data science',
168
- 'reinforcement learning', 'knowledge graph', 'semantic search', 'time series',
169
- 'large language model', 'transformer model', 'attention mechanism',
170
- 'autonomous system', 'edge computing', 'quantum computing', 'blockchain technology',
171
- 'cognitive science', 'human computer', 'decision making', 'arxiv search',
172
- 'research paper', 'scientific study', 'empirical analysis'
173
- ]
174
 
175
- # Extract bi-grams and uni-grams
 
 
176
  words = re.findall(r'\b\w+(?:-\w+)*\b', text.lower())
177
  bi_grams = [' '.join(pair) for pair in zip(words, words[1:])]
178
  combined = words + bi_grams
179
-
180
- # Filter out stop words and short words
181
- filtered = [
182
- term for term in combined
183
- if term not in stop_words
184
- and len(term.split()) <= 2 # Limit to uni-grams and bi-grams
185
- and any(c.isalpha() for c in term)
186
- ]
187
-
188
- # Count frequencies
189
  counter = Counter(filtered)
190
- most_common = [term for term, freq in counter.most_common(top_n)]
191
- return most_common
192
 
193
  def clean_text_for_filename(text: str) -> str:
194
- """Remove punctuation and short filler words, return a compact string."""
195
  text = text.lower()
196
  text = re.sub(r'[^\w\s-]', '', text)
197
  words = text.split()
198
- stop_short = set(['the','and','for','with','this','that','from','just','very','then','been','only','also','about'])
199
- filtered = [w for w in words if len(w)>3 and w not in stop_short]
200
  return '_'.join(filtered)[:200]
201
 
202
- # 📁 6. File Operations
 
 
 
 
 
 
 
 
203
  def generate_filename(prompt, response, file_type="md"):
204
- """
205
- Generate filename with meaningful terms and short dense clips from prompt & response.
206
- The filename should be about 150 chars total, include high-info terms, and a clipped snippet.
207
- """
208
  prefix = datetime.now().strftime("%y%m_%H%M") + "_"
209
  combined = (prompt + " " + response).strip()
210
  info_terms = get_high_info_terms(combined, top_n=10)
211
-
212
- # Include a short snippet from prompt and response
213
  snippet = (prompt[:100] + " " + response[:100]).strip()
214
  snippet_cleaned = clean_text_for_filename(snippet)
215
-
216
- # Combine info terms and snippet
217
  name_parts = info_terms + [snippet_cleaned]
218
  full_name = '_'.join(name_parts)
219
-
220
- # Trim to ~150 chars
221
  if len(full_name) > 150:
222
  full_name = full_name[:150]
223
-
224
- filename = f"{prefix}{full_name}.{file_type}"
225
- return filename
226
 
227
  def create_file(prompt, response, file_type="md"):
228
- """Create file with intelligent naming"""
229
  filename = generate_filename(prompt.strip(), response.strip(), file_type)
230
  with open(filename, 'w', encoding='utf-8') as f:
231
  f.write(prompt + "\n\n" + response)
232
  return filename
233
 
234
  def get_download_link(file, file_type="zip"):
235
- """Generate download link for file"""
236
  with open(file, "rb") as f:
237
  b64 = base64.b64encode(f.read()).decode()
238
  if file_type == "zip":
@@ -240,37 +192,14 @@ def get_download_link(file, file_type="zip"):
240
  elif file_type == "mp3":
241
  return f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(file)}">🎵 Download {os.path.basename(file)}</a>'
242
  elif file_type == "wav":
243
- return f'<a href="data:audio/wav;base64,{b64}" download="{os.path.basename(file)}">🔊 Download {os.path.basename(file)}</a>' # 🆕 WAV download link
244
  elif file_type == "md":
245
  return f'<a href="data:text/markdown;base64,{b64}" download="{os.path.basename(file)}">📝 Download {os.path.basename(file)}</a>'
246
  else:
247
  return f'<a href="data:application/octet-stream;base64,{b64}" download="{os.path.basename(file)}">Download {os.path.basename(file)}</a>'
248
 
249
- # 🔊 7. Audio Processing
250
- def clean_for_speech(text: str) -> str:
251
- """Clean text for speech synthesis"""
252
- text = text.replace("\n", " ")
253
- text = text.replace("</s>", " ")
254
- text = text.replace("#", "")
255
- text = re.sub(r"\(https?:\/\/[^\)]+\)", "", text)
256
- text = re.sub(r"\s+", " ", text).strip()
257
- return text
258
-
259
- @st.cache_resource
260
- def speech_synthesis_html(result):
261
- """Create HTML for speech synthesis"""
262
- html_code = f"""
263
- <html><body>
264
- <script>
265
- var msg = new SpeechSynthesisUtterance("{result.replace('"', '')}");
266
- window.speechSynthesis.speak(msg);
267
- </script>
268
- </body></html>
269
- """
270
- components.html(html_code, height=0)
271
-
272
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0, file_format="mp3"):
273
- """Generate audio using Edge TTS"""
274
  text = clean_for_speech(text)
275
  if not text.strip():
276
  return None
@@ -282,88 +211,16 @@ async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=
282
  return out_fn
283
 
284
  def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0, file_format="mp3"):
285
- """Wrapper for edge TTS generation"""
286
  return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch, file_format))
287
 
288
  def play_and_download_audio(file_path, file_type="mp3"):
289
- """Play and provide download link for audio"""
290
  if file_path and os.path.exists(file_path):
291
- if file_type == "mp3":
292
- st.audio(file_path)
293
- elif file_type == "wav":
294
- st.audio(file_path)
295
  dl_link = get_download_link(file_path, file_type=file_type)
296
  st.markdown(dl_link, unsafe_allow_html=True)
297
 
298
- # 🎬 8. Media Processing
299
- def process_image(image_path, user_prompt):
300
- """Process image with GPT-4V"""
301
- with open(image_path, "rb") as imgf:
302
- image_data = imgf.read()
303
- b64img = base64.b64encode(image_data).decode("utf-8")
304
- resp = openai_client.chat.completions.create(
305
- model=st.session_state["openai_model"],
306
- messages=[
307
- {"role": "system", "content": "You are a helpful assistant."},
308
- {"role": "user", "content": [
309
- {"type": "text", "text": user_prompt},
310
- {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64img}"}}
311
- ]}
312
- ],
313
- temperature=0.0,
314
- )
315
- return resp.choices[0].message.content
316
-
317
- def process_audio_file(audio_path):
318
- """Process audio with Whisper"""
319
- with open(audio_path, "rb") as f:
320
- transcription = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
321
- st.session_state.messages.append({"role": "user", "content": transcription.text})
322
- return transcription.text
323
-
324
- def process_video(video_path, seconds_per_frame=1):
325
- """Extract frames from video"""
326
- vid = cv2.VideoCapture(video_path)
327
- total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
328
- fps = vid.get(cv2.CAP_PROP_FPS)
329
- skip = int(fps*seconds_per_frame)
330
- frames_b64 = []
331
- for i in range(0, total, skip):
332
- vid.set(cv2.CAP_PROP_POS_FRAMES, i)
333
- ret, frame = vid.read()
334
- if not ret:
335
- break
336
- _, buf = cv2.imencode(".jpg", frame)
337
- frames_b64.append(base64.b64encode(buf).decode("utf-8"))
338
- vid.release()
339
- return frames_b64
340
-
341
- def process_video_with_gpt(video_path, prompt):
342
- """Analyze video frames with GPT-4V"""
343
- frames = process_video(video_path)
344
- resp = openai_client.chat.completions.create(
345
- model=st.session_state["openai_model"],
346
- messages=[
347
- {"role":"system","content":"Analyze video frames."},
348
- {"role":"user","content":[
349
- {"type":"text","text":prompt},
350
- *[{"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{fr}"}} for fr in frames]
351
- ]}
352
- ]
353
- )
354
- return resp.choices[0].message.content
355
-
356
- # 🤖 9. AI Model Integration
357
-
358
- def save_full_transcript(query, text):
359
- """Save full transcript of Arxiv results as a file."""
360
- create_file(query, text, "md")
361
-
362
  def parse_arxiv_refs(ref_text: str):
363
- """
364
- Parse papers by finding lines with two pipe characters as title lines.
365
- Returns list of paper dictionaries with audio files.
366
- """
367
  if not ref_text:
368
  return []
369
 
@@ -372,21 +229,16 @@ def parse_arxiv_refs(ref_text: str):
372
  lines = ref_text.split('\n')
373
 
374
  for i, line in enumerate(lines):
375
- # Check if this is a title line (contains exactly 2 pipe characters)
376
  if line.count('|') == 2:
377
- # If we have a previous paper, add it to results
378
  if current_paper:
379
  results.append(current_paper)
380
- if len(results) >= 20: # Limit to 20 papers
381
  break
382
 
383
- # Parse new paper header
384
  try:
385
- # Remove ** and split by |
386
  header_parts = line.strip('* ').split('|')
387
  date = header_parts[0].strip()
388
  title = header_parts[1].strip()
389
- # Extract arXiv URL if present
390
  url_match = re.search(r'(https://arxiv.org/\S+)', line)
391
  url = url_match.group(1) if url_match else f"paper_{len(results)}"
392
 
@@ -396,85 +248,61 @@ def parse_arxiv_refs(ref_text: str):
396
  'url': url,
397
  'authors': '',
398
  'summary': '',
399
- 'content_start': i + 1 # Track where content begins
400
  }
401
  except Exception as e:
402
  st.warning(f"Error parsing paper header: {str(e)}")
403
  current_paper = {}
404
  continue
405
 
406
- # If we have a current paper and this isn't a title line, add to content
407
  elif current_paper:
408
- if not current_paper['authors']: # First line after title is authors
409
  current_paper['authors'] = line.strip('* ')
410
- else: # Rest is summary
411
  if current_paper['summary']:
412
  current_paper['summary'] += ' ' + line.strip()
413
  else:
414
  current_paper['summary'] = line.strip()
415
 
416
- # Don't forget the last paper
417
  if current_paper:
418
  results.append(current_paper)
419
 
420
- return results[:20] # Ensure we return maximum 20 papers
421
 
422
  def create_paper_audio_files(papers, input_question):
423
- """
424
- Create audio files for each paper's content and add file paths to paper dict.
425
- Also, display each audio as it's generated.
426
- """
427
- # Collect all content for combined summary
428
- combined_titles = []
429
-
430
  for paper in papers:
431
  try:
432
- # Generate audio for full content only
433
- full_text = f"{paper['title']} by {paper['authors']}. {paper['summary']}"
434
- full_text = clean_for_speech(full_text)
435
- # Determine file format based on user selection
436
  file_format = st.session_state['audio_format']
437
- full_file = speak_with_edge_tts(full_text, voice=st.session_state['tts_voice'], file_format=file_format)
438
- paper['full_audio'] = full_file
439
-
440
- # Display the audio immediately after generation
441
- st.write(f"### {FILE_EMOJIS.get(file_format, '')} {os.path.basename(full_file)}")
442
- play_and_download_audio(full_file, file_type=file_format)
 
 
443
 
444
- combined_titles.append(paper['title'])
445
-
446
  except Exception as e:
447
- st.warning(f"Error generating audio for paper {paper['title']}: {str(e)}")
448
  paper['full_audio'] = None
 
449
 
450
- # After all individual audios, create a combined summary audio
451
- if combined_titles:
452
- combined_text = f"Here are the titles of the papers related to your query: {'; '.join(combined_titles)}. Your original question was: {input_question}"
453
- file_format = st.session_state['audio_format']
454
- combined_file = speak_with_edge_tts(combined_text, voice=st.session_state['tts_voice'], file_format=file_format)
455
- st.write(f"### {FILE_EMOJIS.get(file_format, '')} Combined Summary Audio")
456
- play_and_download_audio(combined_file, file_type=file_format)
457
- papers.append({'title': 'Combined Summary', 'full_audio': combined_file})
458
-
459
-
460
-
461
- def display_papers(papers):
462
- """Display papers with their audio controls and marquee summaries."""
463
  st.write("## Research Papers")
464
- marquee_settings = display_marquee_controls_papers()
465
 
466
  papercount = 0
467
  for paper in papers:
468
- papercount = papercount + 1
469
  if papercount <= 20:
470
- # Add marquee display
471
- content = f"📄 {paper['title']} | 👤 {paper['authors']} | 📝 {paper['summary']}"
472
- streamlit_marquee(
473
- content=content,
474
- **marquee_settings,
475
- key=f"paper_{paper.get('id', random.randint(1000,9999))}"
476
- )
477
- st.write("")
478
 
479
  with st.expander(f"{papercount}. 📄 {paper['title']}", expanded=True):
480
  st.markdown(f"**{paper['date']} | {paper['title']} | ⬇️**")
@@ -487,9 +315,8 @@ def display_papers(papers):
487
  if file_ext in ['mp3', 'wav']:
488
  st.audio(paper['full_audio'])
489
 
490
-
491
  def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
492
- titles_summary=True, full_audio=False):
493
  """Perform Arxiv search with audio generation per paper."""
494
  start = time.time()
495
 
@@ -509,7 +336,10 @@ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
509
  papers = parse_arxiv_refs(refs)
510
  if papers:
511
  create_paper_audio_files(papers, input_question=q)
512
- display_papers(papers)
 
 
 
513
  else:
514
  st.warning("No papers found in the response.")
515
 
@@ -557,30 +387,44 @@ def process_with_claude(text):
557
  st.session_state.chat_history.append({"user":text,"claude":ans})
558
  return ans
559
 
560
- # 📂 10. File Management
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  def create_zip_of_files(md_files, mp3_files, wav_files, input_question):
562
- """Create zip with intelligent naming based on top 10 common words."""
563
- # Exclude 'readme.md'
564
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
565
  all_files = md_files + mp3_files + wav_files
566
  if not all_files:
567
  return None
568
 
569
- # Collect content for high-info term extraction
570
  all_content = []
571
  for f in all_files:
572
  if f.endswith('.md'):
573
  with open(f, 'r', encoding='utf-8') as file:
574
  all_content.append(file.read())
575
  elif f.endswith('.mp3') or f.endswith('.wav'):
576
- # Replace underscores with spaces and extract basename without extension
577
  basename = os.path.splitext(os.path.basename(f))[0]
578
  words = basename.replace('_', ' ')
579
  all_content.append(words)
580
 
581
- # Include the input question
582
  all_content.append(input_question)
583
-
584
  combined_content = " ".join(all_content)
585
  info_terms = get_high_info_terms(combined_content, top_n=10)
586
 
@@ -588,41 +432,12 @@ def create_zip_of_files(md_files, mp3_files, wav_files, input_question):
588
  name_text = '_'.join(term.replace(' ', '-') for term in info_terms[:10])
589
  zip_name = f"{timestamp}_{name_text}.zip"
590
 
591
- with zipfile.ZipFile(zip_name,'w') as z:
592
  for f in all_files:
593
  z.write(f)
594
 
595
  return zip_name
596
 
597
- def load_files_for_sidebar():
598
- """Load and group files for sidebar display based on first 9 characters of filename"""
599
- md_files = glob.glob("*.md")
600
- mp3_files = glob.glob("*.mp3")
601
- wav_files = glob.glob("*.wav")
602
-
603
- md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
604
- all_files = md_files + mp3_files + wav_files
605
-
606
- groups = defaultdict(list)
607
- for f in all_files:
608
- # Get first 9 characters of filename (timestamp) as group name
609
- basename = os.path.basename(f)
610
- group_name = basename[:9] if len(basename) >= 9 else 'Other'
611
- groups[group_name].append(f)
612
-
613
- # Sort groups based on latest file modification time
614
- sorted_groups = sorted(groups.items(), key=lambda x: max(os.path.getmtime(f) for f in x[1]), reverse=True)
615
- return sorted_groups
616
-
617
- def extract_keywords_from_md(files):
618
- """Extract keywords from markdown files"""
619
- text = ""
620
- for f in files:
621
- if f.endswith(".md"):
622
- c = open(f,'r',encoding='utf-8').read()
623
- text += " " + c
624
- return get_high_info_terms(text, top_n=5)
625
-
626
  def display_file_manager_sidebar(groups_sorted):
627
  """Display file manager in sidebar with timestamp-based groups"""
628
  st.sidebar.title("🎵 Audio & Docs Manager")
@@ -657,16 +472,18 @@ def display_file_manager_sidebar(groups_sorted):
657
  st.session_state.should_rerun = True
658
  with top_bar[3]:
659
  if st.button("⬇️ ZipAll"):
660
- zip_name = create_zip_of_files(all_md, all_mp3, all_wav, input_question=st.session_state.get('last_query', ''))
 
661
  if zip_name:
662
- st.sidebar.markdown(get_download_link(zip_name, file_type="zip"), unsafe_allow_html=True)
 
663
 
664
  for group_name, files in groups_sorted:
665
  timestamp_dt = datetime.strptime(group_name, "%y%m_%H%M") if len(group_name) == 9 else None
666
  group_label = timestamp_dt.strftime("%Y-%m-%d %H:%M") if timestamp_dt else group_name
667
 
668
  with st.sidebar.expander(f"📁 {group_label} ({len(files)})", expanded=True):
669
- c1,c2 = st.columns(2)
670
  with c1:
671
  if st.button("👀ViewGrp", key="view_group_"+group_name):
672
  st.session_state.viewing_prefix = group_name
@@ -684,57 +501,18 @@ def display_file_manager_sidebar(groups_sorted):
684
  ctime = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%H:%M:%S")
685
  st.write(f"{emoji} **{fname}** - {ctime}")
686
 
687
- # 🎯 11. Main Application
688
  def main():
 
 
689
 
690
- # Initialize content holder in session state if not present
691
- if 'marquee_content' not in st.session_state:
692
- st.session_state['marquee_content'] = "🚀 Welcome to TalkingAIResearcher | 🤖 Your Research Assistant"
693
-
694
- # Load files first
695
- groups_sorted = load_files_for_sidebar()
696
-
697
- # Marquee display with larger text and dynamic content
698
- streamlit_marquee(
699
- content=st.session_state['marquee_content'],
700
- **{
701
- **marquee_settings,
702
- "font-size": "28px",
703
- "lineHeight": "50px"
704
- },
705
- key="dynamic_marquee"
706
- )
707
-
708
- # Update marquee content when viewing files
709
- if st.session_state.viewing_prefix:
710
- for group_name, files in groups_sorted:
711
- if group_name == st.session_state.viewing_prefix:
712
- for f in files:
713
- if f.endswith('.md'):
714
- with open(f, 'r', encoding='utf-8') as file:
715
- st.session_state['marquee_content'] = file.read()[:500]
716
 
717
- # Rest of your main() code...
718
- st.sidebar.markdown("### 🚀 TalkingAIResearcher | 🤖 Your Research Assistant")
719
-
720
- # Add global marquee settings near top of main
721
- marquee_settings = display_marquee_controls()
722
-
723
- # Initialize content holder in session state if not present
724
- if 'marquee_content' not in st.session_state:
725
- st.session_state['marquee_content'] = "🚀 Welcome to TalkingAIResearcher | 🤖 Your Research Assistant"
726
 
727
- # Marquee display with larger text and dynamic content
728
- streamlit_marquee(
729
- content=st.session_state['marquee_content'],
730
- **{
731
- **marquee_settings,
732
- "font-size": "28px", # Larger default text
733
- "lineHeight": "50px" # Increased line height for larger text
734
- },
735
- key="dynamic_marquee"
736
- )
737
-
738
  # Update marquee content when viewing files
739
  if st.session_state.viewing_prefix:
740
  for group_name, files in groups_sorted:
@@ -742,11 +520,9 @@ def main():
742
  for f in files:
743
  if f.endswith('.md'):
744
  with open(f, 'r', encoding='utf-8') as file:
745
- st.session_state['marquee_content'] = file.read()[:500] # First 500 chars
746
-
747
 
748
-
749
- # Add voice selector to sidebar
750
  st.sidebar.markdown("### 🎤 Voice Settings")
751
  selected_voice = st.sidebar.selectbox(
752
  "Select TTS Voice:",
@@ -754,15 +530,14 @@ def main():
754
  index=EDGE_TTS_VOICES.index(st.session_state['tts_voice'])
755
  )
756
 
757
- # Add audio format selector to sidebar
758
  st.sidebar.markdown("### 🔊 Audio Format")
759
  selected_format = st.sidebar.radio(
760
  "Choose Audio Format:",
761
  options=["MP3", "WAV"],
762
- index=0 # Default to MP3
763
  )
764
 
765
- # Update session state if voice or format changes
766
  if selected_voice != st.session_state['tts_voice']:
767
  st.session_state['tts_voice'] = selected_voice
768
  st.rerun()
@@ -770,33 +545,33 @@ def main():
770
  st.session_state['audio_format'] = selected_format.lower()
771
  st.rerun()
772
 
773
- tab_main = st.radio("Action:",["🎤 Voice","📸 Media","🔍 ArXiv","📝 Editor"],horizontal=True)
 
 
774
 
775
  mycomponent = components.declare_component("mycomponent", path="mycomponent")
776
  val = mycomponent(my_input_value="Hello")
777
 
778
- # Show input in a text box for editing if detected
779
  if val:
780
  val_stripped = val.replace('\\n', ' ')
781
  edited_input = st.text_area("✏️ Edit Input:", value=val_stripped, height=100)
782
- #edited_input = edited_input.replace('\n', ' ')
783
 
784
  run_option = st.selectbox("Model:", ["Arxiv", "GPT-4o", "Claude-3.5"])
785
  col1, col2 = st.columns(2)
786
  with col1:
787
  autorun = st.checkbox("⚙ AutoRun", value=True)
788
  with col2:
789
- full_audio = st.checkbox("📚FullAudio", value=False,
790
- help="Generate full audio response")
791
 
792
  input_changed = (val != st.session_state.old_val)
793
 
794
  if autorun and input_changed:
795
  st.session_state.old_val = val
796
- st.session_state.last_query = edited_input # Store the last query for zip naming
797
  if run_option == "Arxiv":
798
  perform_ai_lookup(edited_input, vocal_summary=True, extended_refs=False,
799
- titles_summary=True, full_audio=full_audio)
 
800
  else:
801
  if run_option == "GPT-4o":
802
  process_with_gpt(edited_input)
@@ -805,23 +580,19 @@ def main():
805
  else:
806
  if st.button("▶ Run"):
807
  st.session_state.old_val = val
808
- st.session_state.last_query = edited_input # Store the last query for zip naming
809
  if run_option == "Arxiv":
810
  perform_ai_lookup(edited_input, vocal_summary=True, extended_refs=False,
811
- titles_summary=True, full_audio=full_audio)
 
812
  else:
813
  if run_option == "GPT-4o":
814
  process_with_gpt(edited_input)
815
  elif run_option == "Claude-3.5":
816
  process_with_claude(edited_input)
817
 
818
-
819
  if tab_main == "🔍 ArXiv":
820
- papers = parse_arxiv_refs(refs)
821
- if papers:
822
- paper_texts = [f"📄 {p['title']} | {p['authors']}" for p in papers]
823
- st.session_state['marquee_content'] = " ⭐ ".join(paper_texts)
824
-
825
  st.subheader("🔍 Query ArXiv")
826
  q = st.text_input("🔍 Query:")
827
 
@@ -829,35 +600,28 @@ def main():
829
  vocal_summary = st.checkbox("🎙ShortAudio", value=True)
830
  extended_refs = st.checkbox("📜LongRefs", value=False)
831
  titles_summary = st.checkbox("🔖TitlesOnly", value=True)
832
- full_audio = st.checkbox("📚FullAudio", value=False,
833
- help="Full audio of results")
834
- full_transcript = st.checkbox("🧾FullTranscript", value=False,
835
- help="Generate a full transcript file")
836
 
837
  if q and st.button("🔍Run"):
838
- st.session_state.last_query = q # Store the last query for zip naming
839
  result = perform_ai_lookup(q, vocal_summary=vocal_summary, extended_refs=extended_refs,
840
- titles_summary=titles_summary, full_audio=full_audio)
 
841
  if full_transcript:
842
- save_full_transcript(q, result)
843
-
844
- st.markdown("### Change Prompt & Re-Run")
845
- q_new = st.text_input("🔄 Modify Query:")
846
- if q_new and st.button("🔄 Re-Run with Modified Query"):
847
- st.session_state.last_query = q_new # Update last query
848
- result = perform_ai_lookup(q_new, vocal_summary=vocal_summary, extended_refs=extended_refs,
849
- titles_summary=titles_summary, full_audio=full_audio)
850
- if full_transcript:
851
- save_full_transcript(q_new, result)
852
 
 
853
  elif tab_main == "🎤 Voice":
854
  st.subheader("🎤 Voice Input")
855
  user_text = st.text_area("💬 Message:", height=100)
856
  user_text = user_text.strip().replace('\n', ' ')
 
857
  if st.button("📨 Send"):
858
  process_with_gpt(user_text)
 
859
  st.subheader("📜 Chat History")
860
- t1,t2=st.tabs(["Claude History","GPT-4o History"])
861
  with t1:
862
  for c in st.session_state.chat_history:
863
  st.write("**You:**", c["user"])
@@ -867,22 +631,33 @@ def main():
867
  with st.chat_message(m["role"]):
868
  st.markdown(m["content"])
869
 
 
870
  elif tab_main == "📸 Media":
871
  st.header("📸 Images & 🎥 Videos")
872
  tabs = st.tabs(["🖼 Images", "🎥 Video"])
873
  with tabs[0]:
874
- imgs = glob.glob("*.png")+glob.glob("*.jpg")
875
  if imgs:
876
- c = st.slider("Cols",1,5,3)
877
  cols = st.columns(c)
878
- for i,f in enumerate(imgs):
879
- with cols[i%c]:
880
- st.image(Image.open(f),use_container_width=True)
881
  if st.button(f"👀 Analyze {os.path.basename(f)}", key=f"analyze_{f}"):
882
- a = process_image(f,"Describe this image.")
883
- st.markdown(a)
 
 
 
 
 
 
 
 
 
884
  else:
885
  st.write("No images found.")
 
886
  with tabs[1]:
887
  vids = glob.glob("*.mp4")
888
  if vids:
@@ -890,27 +665,40 @@ def main():
890
  with st.expander(f"🎥 {os.path.basename(v)}"):
891
  st.video(v)
892
  if st.button(f"Analyze {os.path.basename(v)}", key=f"analyze_{v}"):
893
- a = process_video_with_gpt(v,"Describe video.")
894
- st.markdown(a)
 
 
 
 
 
 
 
 
 
 
 
895
  else:
896
  st.write("No videos found.")
897
 
 
898
  elif tab_main == "📝 Editor":
899
- if getattr(st.session_state,'current_file',None):
900
- st.subheader(f"Editing: {st.session_state.current_file}")
901
- new_text = st.text_area("✏️ Content:", st.session_state.file_content, height=300)
902
  if st.button("💾 Save"):
903
- with open(st.session_state.current_file,'w',encoding='utf-8') as f:
904
  f.write(new_text)
905
- st.success("Updated!")
906
  st.session_state.should_rerun = True
 
907
  else:
908
  st.write("Select a file from the sidebar to edit.")
909
 
910
- # Load and display files in the sidebar
911
- groups_sorted = load_files_for_sidebar()
912
  display_file_manager_sidebar(groups_sorted)
913
 
 
914
  if st.session_state.viewing_prefix and any(st.session_state.viewing_prefix == group for group, _ in groups_sorted):
915
  st.write("---")
916
  st.write(f"**Viewing Group:** {st.session_state.viewing_prefix}")
@@ -921,146 +709,30 @@ def main():
921
  ext = os.path.splitext(fname)[1].lower().strip('.')
922
  st.write(f"### {fname}")
923
  if ext == "md":
924
- content = open(f,'r',encoding='utf-8').read()
925
  st.markdown(content)
926
- elif ext == "mp3":
927
  st.audio(f)
928
- elif ext == "wav":
929
- st.audio(f) # 🆕 Handle WAV files
930
  else:
931
  st.markdown(get_download_link(f), unsafe_allow_html=True)
932
  break
933
  if st.button("❌ Close"):
934
  st.session_state.viewing_prefix = None
935
-
936
- markdownPapers = """
937
-
938
- # Levels of AGI
939
-
940
- ## 1. Performance (rows) x Generality (columns)
941
- - **Narrow**
942
- - *clearly scoped or set of tasks*
943
- - **General**
944
- - *wide range of non-physical tasks, including metacognitive abilities like learning new skills*
945
-
946
- ## 2. Levels of AGI
947
-
948
- ### 2.1 Level 0: No AI
949
- - **Narrow Non-AI**
950
- - Calculator software; compiler
951
- - **General Non-AI**
952
- - Human-in-the-loop computing, e.g., Amazon Mechanical Turk
953
-
954
- ### 2.2 Level 1: Emerging
955
- *equal to or somewhat better than an unskilled human*
956
- - **Emerging Narrow AI**
957
- - GOFAI; simple rule-based systems
958
- - Example: SHRDLU
959
- - *Reference:* Winograd, T. (1971). **Procedures as a Representation for Data in a Computer Program for Understanding Natural Language**. MIT AI Technical Report. [Link](https://dspace.mit.edu/handle/1721.1/7095)
960
- - **Emerging AGI**
961
- - ChatGPT (OpenAI, 2023)
962
- - Bard (Anil et al., 2023)
963
- - *Reference:* Anil, R., et al. (2023). **Bard: Google’s AI Chatbot**. [arXiv](https://arxiv.org/abs/2303.12712)
964
- - LLaMA 2 (Touvron et al., 2023)
965
- - *Reference:* Touvron, H., et al. (2023). **LLaMA 2: Open and Efficient Foundation Language Models**. [arXiv](https://arxiv.org/abs/2307.09288)
966
-
967
- ### 2.3 Level 2: Competent
968
- *at least 50th percentile of skilled adults*
969
- - **Competent Narrow AI**
970
- - Toxicity detectors such as Jigsaw
971
- - *Reference:* Das, S., et al. (2022). **Toxicity Detection at Scale with Jigsaw**. [arXiv](https://arxiv.org/abs/2204.06905)
972
- - Smart Speakers (Apple, Amazon, Google)
973
- - VQA systems (PaLI)
974
- - *Reference:* Chen, T., et al. (2023). **PaLI: Pathways Language and Image model**. [arXiv](https://arxiv.org/abs/2301.01298)
975
- - Watson (IBM)
976
- - SOTA LLMs for subsets of tasks
977
- - **Competent AGI**
978
- - Not yet achieved
979
-
980
- ### 2.4 Level 3: Expert
981
- *at least 90th percentile of skilled adults*
982
- - **Expert Narrow AI**
983
- - Spelling & grammar checkers (Grammarly, 2023)
984
- - Generative image models
985
- - Example: Imagen
986
- - *Reference:* Saharia, C., et al. (2022). **Imagen: Photorealistic Text-to-Image Diffusion Models**. [arXiv](https://arxiv.org/abs/2205.11487)
987
- - Example: DALL·E 2
988
- - *Reference:* Ramesh, A., et al. (2022). **Hierarchical Text-Conditional Image Generation with CLIP Latents**. [arXiv](https://arxiv.org/abs/2204.06125)
989
- - **Expert AGI**
990
- - Not yet achieved
991
-
992
- ### 2.5 Level 4: Virtuoso
993
- *at least 99th percentile of skilled adults*
994
- - **Virtuoso Narrow AI**
995
- - Deep Blue
996
- - *Reference:* Campbell, M., et al. (2002). **Deep Blue**. IBM Journal of Research and Development. [Link](https://research.ibm.com/publications/deep-blue)
997
- - AlphaGo
998
- - *Reference:* Silver, D., et al. (2016, 2017). **Mastering the Game of Go with Deep Neural Networks and Tree Search**. [Nature](https://www.nature.com/articles/nature16961)
999
- - **Virtuoso AGI**
1000
- - Not yet achieved
1001
-
1002
- ### 2.6 Level 5: Superhuman
1003
- *outperforms 100% of humans*
1004
- - **Superhuman Narrow AI**
1005
- - AlphaFold
1006
- - *Reference:* Jumper, J., et al. (2021). **Highly Accurate Protein Structure Prediction with AlphaFold**. [Nature](https://www.nature.com/articles/s41586-021-03819-2)
1007
- - AlphaZero
1008
- - *Reference:* Silver, D., et al. (2018). **A General Reinforcement Learning Algorithm that Masters Chess, Shogi, and Go through Self-Play**. [Science](https://www.science.org/doi/10.1126/science.aar6404)
1009
- - StockFish
1010
- - *Reference:* Stockfish (2023). **Stockfish Chess Engine**. [Website](https://stockfishchess.org)
1011
- - **Artificial Superintelligence (ASI)**
1012
- - Not yet achieved
1013
-
1014
-
1015
- # 🧬 Innovative Architecture of AlphaFold2: A Hybrid System
1016
-
1017
- ## 1. 🔢 Input Sequence
1018
- - The process starts with an **input sequence** (protein sequence).
1019
-
1020
- ## 2. 🗄️ Database Searches
1021
- - **Genetic database search** 🔍
1022
- - Searches genetic databases to retrieve related sequences.
1023
- - **Structure database search** 🔍
1024
- - Searches structural databases for template structures.
1025
- - **Pairing** 🤝
1026
- - Aligns sequences and structures for further analysis.
1027
-
1028
- ## 3. 🧩 MSA (Multiple Sequence Alignment)
1029
- - **MSA representation** 📊 (r,c)
1030
- - Representation of multiple aligned sequences used as input.
1031
-
1032
- ## 4. 📑 Templates
1033
- - Template structures are paired to assist the model.
1034
-
1035
- ## 5. 🔄 Evoformer (48 blocks)
1036
- - A **deep learning module** that refines representations:
1037
- - **MSA representation** 🧱
1038
- - **Pair representation** 🧱 (r,c)
1039
-
1040
- ## 6. 🧱 Structure Module (8 blocks)
1041
- - Converts the representations into:
1042
- - **Single representation** (r,c)
1043
- - **Pair representation** (r,c)
1044
-
1045
- ## 7. 🧬 3D Structure Prediction
1046
- - The structure module predicts the **3D protein structure**.
1047
- - **Confidence levels**:
1048
- - 🔵 *High confidence*
1049
- - 🟠 *Low confidence*
1050
-
1051
- ## 8. ♻️ Recycling (Three Times)
1052
- - The model **recycles** its output up to three times to refine the prediction.
1053
-
1054
- ## 9. 📚 Reference
1055
- **Jumper, J., et al. (2021).** Highly Accurate Protein Structure Prediction with AlphaFold. *Nature.*
1056
- 🔗 [Nature Publication Link](https://www.nature.com/articles/s41586-021-03819-2)
1057
-
1058
- """
1059
- st.sidebar.markdown(markdownPapers)
1060
-
1061
  if st.session_state.should_rerun:
1062
  st.session_state.should_rerun = False
1063
  st.rerun()
1064
 
1065
- if __name__=="__main__":
1066
  main()
 
19
  from streamlit.runtime.scriptrunner import get_script_run_ctx
20
  import asyncio
21
  import edge_tts
 
 
22
  from streamlit_marquee import streamlit_marquee
23
 
 
24
  # 🎯 1. Core Configuration & Setup
25
  st.set_page_config(
26
  page_title="🚲TalkingAIResearcher🏆",
 
35
  )
36
  load_dotenv()
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  # Add available English voices for Edge TTS
39
  EDGE_TTS_VOICES = [
40
  "en-US-AriaNeural", # Default voice
 
50
 
51
  # Initialize session state variables
52
  if 'tts_voice' not in st.session_state:
53
+ st.session_state['tts_voice'] = EDGE_TTS_VOICES[0]
54
  if 'audio_format' not in st.session_state:
55
+ st.session_state['audio_format'] = 'mp3'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  if 'transcript_history' not in st.session_state:
57
  st.session_state['transcript_history'] = []
58
  if 'chat_history' not in st.session_state:
 
76
  if 'old_val' not in st.session_state:
77
  st.session_state['old_val'] = None
78
  if 'last_query' not in st.session_state:
79
+ st.session_state['last_query'] = ""
80
+ if 'marquee_content' not in st.session_state:
81
+ st.session_state['marquee_content'] = "🚀 Welcome to TalkingAIResearcher | 🤖 Your Research Assistant"
82
 
83
+ # 🔑 2. API Setup & Clients
84
+ openai_api_key = os.getenv('OPENAI_API_KEY', "")
85
+ anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', "")
86
+ xai_key = os.getenv('xai',"")
87
+ if 'OPENAI_API_KEY' in st.secrets:
88
+ openai_api_key = st.secrets['OPENAI_API_KEY']
89
+ if 'ANTHROPIC_API_KEY' in st.secrets:
90
+ anthropic_key = st.secrets["ANTHROPIC_API_KEY"]
 
 
91
 
92
+ openai.api_key = openai_api_key
93
+ claude_client = anthropic.Anthropic(api_key=anthropic_key)
94
+ openai_client = OpenAI(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
95
+ HF_KEY = os.getenv('HF_KEY')
96
+ API_URL = os.getenv('API_URL')
97
+
98
+ # Constants
99
  FILE_EMOJIS = {
100
  "md": "📝",
101
  "mp3": "🎵",
102
+ "wav": "🔊"
103
  }
104
 
105
+ # Marquee Functions
106
+ def get_marquee_settings():
107
+ """Get global marquee settings from sidebar controls"""
108
+ st.sidebar.markdown("### 🎯 Marquee Settings")
109
+ cols = st.sidebar.columns(2)
110
+ with cols[0]:
111
+ bg_color = st.color_picker("🎨 Background", "#1E1E1E", key="bg_color_picker")
112
+ text_color = st.color_picker("✍️ Text", "#FFFFFF", key="text_color_picker")
113
+ with cols[1]:
114
+ font_size = st.slider("📏 Size", 10, 24, 14, key="font_size_slider")
115
+ duration = st.slider("⏱️ Speed", 1, 20, 10, key="duration_slider")
116
+
117
+ return {
118
+ "background": bg_color,
119
+ "color": text_color,
120
+ "font-size": f"{font_size}px",
121
+ "animationDuration": f"{duration}s",
122
+ "width": "100%",
123
+ "lineHeight": "35px"
124
+ }
125
+
126
+ def display_marquee(text, settings, key_suffix=""):
127
+ """Display marquee with given text and settings"""
128
+ truncated_text = text[:280] + "..." if len(text) > 280 else text
129
+ streamlit_marquee(
130
+ content=truncated_text,
131
+ **settings,
132
+ key=f"marquee_{key_suffix}"
133
+ )
134
+ st.write("")
135
 
136
+ def process_paper_content(paper):
137
+ """Process paper content for marquee and audio"""
138
+ marquee_text = f"📄 {paper['title']} | 👤 {paper['authors'][:100]} | 📝 {paper['summary'][:100]}"
139
+ audio_text = f"{paper['title']} by {paper['authors']}. {paper['summary']}"
140
+ return marquee_text, audio_text
 
 
 
 
141
 
142
+ # Text Processing Functions
143
+ def get_high_info_terms(text: str, top_n=10) -> list:
144
+ stop_words = set(['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with'])
145
  words = re.findall(r'\b\w+(?:-\w+)*\b', text.lower())
146
  bi_grams = [' '.join(pair) for pair in zip(words, words[1:])]
147
  combined = words + bi_grams
148
+ filtered = [term for term in combined if term not in stop_words and len(term.split()) <= 2]
 
 
 
 
 
 
 
 
 
149
  counter = Counter(filtered)
150
+ return [term for term, freq in counter.most_common(top_n)]
 
151
 
152
  def clean_text_for_filename(text: str) -> str:
 
153
  text = text.lower()
154
  text = re.sub(r'[^\w\s-]', '', text)
155
  words = text.split()
156
+ stop_short = set(['the', 'and', 'for', 'with', 'this', 'that'])
157
+ filtered = [w for w in words if len(w) > 3 and w not in stop_short]
158
  return '_'.join(filtered)[:200]
159
 
160
+ def clean_for_speech(text: str) -> str:
161
+ text = text.replace("\n", " ")
162
+ text = text.replace("</s>", " ")
163
+ text = text.replace("#", "")
164
+ text = re.sub(r"\(https?:\/\/[^\)]+\)", "", text)
165
+ text = re.sub(r"\s+", " ", text).strip()
166
+ return text
167
+
168
+ # File Operations
169
  def generate_filename(prompt, response, file_type="md"):
 
 
 
 
170
  prefix = datetime.now().strftime("%y%m_%H%M") + "_"
171
  combined = (prompt + " " + response).strip()
172
  info_terms = get_high_info_terms(combined, top_n=10)
 
 
173
  snippet = (prompt[:100] + " " + response[:100]).strip()
174
  snippet_cleaned = clean_text_for_filename(snippet)
 
 
175
  name_parts = info_terms + [snippet_cleaned]
176
  full_name = '_'.join(name_parts)
 
 
177
  if len(full_name) > 150:
178
  full_name = full_name[:150]
179
+ return f"{prefix}{full_name}.{file_type}"
 
 
180
 
181
  def create_file(prompt, response, file_type="md"):
 
182
  filename = generate_filename(prompt.strip(), response.strip(), file_type)
183
  with open(filename, 'w', encoding='utf-8') as f:
184
  f.write(prompt + "\n\n" + response)
185
  return filename
186
 
187
  def get_download_link(file, file_type="zip"):
 
188
  with open(file, "rb") as f:
189
  b64 = base64.b64encode(f.read()).decode()
190
  if file_type == "zip":
 
192
  elif file_type == "mp3":
193
  return f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(file)}">🎵 Download {os.path.basename(file)}</a>'
194
  elif file_type == "wav":
195
+ return f'<a href="data:audio/wav;base64,{b64}" download="{os.path.basename(file)}">🔊 Download {os.path.basename(file)}</a>'
196
  elif file_type == "md":
197
  return f'<a href="data:text/markdown;base64,{b64}" download="{os.path.basename(file)}">📝 Download {os.path.basename(file)}</a>'
198
  else:
199
  return f'<a href="data:application/octet-stream;base64,{b64}" download="{os.path.basename(file)}">Download {os.path.basename(file)}</a>'
200
 
201
+ # Audio Processing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0, file_format="mp3"):
 
203
  text = clean_for_speech(text)
204
  if not text.strip():
205
  return None
 
211
  return out_fn
212
 
213
  def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0, file_format="mp3"):
 
214
  return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch, file_format))
215
 
216
  def play_and_download_audio(file_path, file_type="mp3"):
 
217
  if file_path and os.path.exists(file_path):
218
+ st.audio(file_path)
 
 
 
219
  dl_link = get_download_link(file_path, file_type=file_type)
220
  st.markdown(dl_link, unsafe_allow_html=True)
221
 
222
+ # Paper Processing Functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  def parse_arxiv_refs(ref_text: str):
 
 
 
 
224
  if not ref_text:
225
  return []
226
 
 
229
  lines = ref_text.split('\n')
230
 
231
  for i, line in enumerate(lines):
 
232
  if line.count('|') == 2:
 
233
  if current_paper:
234
  results.append(current_paper)
235
+ if len(results) >= 20:
236
  break
237
 
 
238
  try:
 
239
  header_parts = line.strip('* ').split('|')
240
  date = header_parts[0].strip()
241
  title = header_parts[1].strip()
 
242
  url_match = re.search(r'(https://arxiv.org/\S+)', line)
243
  url = url_match.group(1) if url_match else f"paper_{len(results)}"
244
 
 
248
  'url': url,
249
  'authors': '',
250
  'summary': '',
251
+ 'content_start': i + 1
252
  }
253
  except Exception as e:
254
  st.warning(f"Error parsing paper header: {str(e)}")
255
  current_paper = {}
256
  continue
257
 
 
258
  elif current_paper:
259
+ if not current_paper['authors']:
260
  current_paper['authors'] = line.strip('* ')
261
+ else:
262
  if current_paper['summary']:
263
  current_paper['summary'] += ' ' + line.strip()
264
  else:
265
  current_paper['summary'] = line.strip()
266
 
 
267
  if current_paper:
268
  results.append(current_paper)
269
 
270
+ return results[:20]
271
 
272
  def create_paper_audio_files(papers, input_question):
 
 
 
 
 
 
 
273
  for paper in papers:
274
  try:
275
+ marquee_text, audio_text = process_paper_content(paper)
276
+
277
+ audio_text = clean_for_speech(audio_text)
 
278
  file_format = st.session_state['audio_format']
279
+ audio_file = speak_with_edge_tts(audio_text,
280
+ voice=st.session_state['tts_voice'],
281
+ file_format=file_format)
282
+ paper['full_audio'] = audio_file
283
+
284
+ st.write(f"### {FILE_EMOJIS.get(file_format, '')} {os.path.basename(audio_file)}")
285
+ play_and_download_audio(audio_file, file_type=file_format)
286
+ paper['marquee_text'] = marquee_text
287
 
 
 
288
  except Exception as e:
289
+ st.warning(f"Error processing paper {paper['title']}: {str(e)}")
290
  paper['full_audio'] = None
291
+ paper['marquee_text'] = None
292
 
293
+ def display_papers(papers, marquee_settings):
294
+ """Display papers with their audio controls and marquee summaries"""
 
 
 
 
 
 
 
 
 
 
 
295
  st.write("## Research Papers")
 
296
 
297
  papercount = 0
298
  for paper in papers:
299
+ papercount += 1
300
  if papercount <= 20:
301
+ # Display marquee if text exists
302
+ if paper.get('marquee_text'):
303
+ display_marquee(paper['marquee_text'],
304
+ marquee_settings,
305
+ key_suffix=f"paper_{papercount}")
 
 
 
306
 
307
  with st.expander(f"{papercount}. 📄 {paper['title']}", expanded=True):
308
  st.markdown(f"**{paper['date']} | {paper['title']} | ⬇️**")
 
315
  if file_ext in ['mp3', 'wav']:
316
  st.audio(paper['full_audio'])
317
 
 
318
  def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
319
+ titles_summary=True, full_audio=False, marquee_settings=None):
320
  """Perform Arxiv search with audio generation per paper."""
321
  start = time.time()
322
 
 
336
  papers = parse_arxiv_refs(refs)
337
  if papers:
338
  create_paper_audio_files(papers, input_question=q)
339
+ if marquee_settings:
340
+ display_papers(papers, marquee_settings)
341
+ else:
342
+ display_papers(papers, get_marquee_settings())
343
  else:
344
  st.warning("No papers found in the response.")
345
 
 
387
  st.session_state.chat_history.append({"user":text,"claude":ans})
388
  return ans
389
 
390
+ def load_files_for_sidebar():
391
+ """Load and group files for sidebar display based on first 9 characters of filename"""
392
+ md_files = glob.glob("*.md")
393
+ mp3_files = glob.glob("*.mp3")
394
+ wav_files = glob.glob("*.wav")
395
+
396
+ md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
397
+ all_files = md_files + mp3_files + wav_files
398
+
399
+ groups = defaultdict(list)
400
+ for f in all_files:
401
+ basename = os.path.basename(f)
402
+ group_name = basename[:9] if len(basename) >= 9 else 'Other'
403
+ groups[group_name].append(f)
404
+
405
+ sorted_groups = sorted(groups.items(),
406
+ key=lambda x: max(os.path.getmtime(f) for f in x[1]),
407
+ reverse=True)
408
+ return sorted_groups
409
+
410
  def create_zip_of_files(md_files, mp3_files, wav_files, input_question):
411
+ """Create zip with intelligent naming based on high-info terms"""
 
412
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
413
  all_files = md_files + mp3_files + wav_files
414
  if not all_files:
415
  return None
416
 
 
417
  all_content = []
418
  for f in all_files:
419
  if f.endswith('.md'):
420
  with open(f, 'r', encoding='utf-8') as file:
421
  all_content.append(file.read())
422
  elif f.endswith('.mp3') or f.endswith('.wav'):
 
423
  basename = os.path.splitext(os.path.basename(f))[0]
424
  words = basename.replace('_', ' ')
425
  all_content.append(words)
426
 
 
427
  all_content.append(input_question)
 
428
  combined_content = " ".join(all_content)
429
  info_terms = get_high_info_terms(combined_content, top_n=10)
430
 
 
432
  name_text = '_'.join(term.replace(' ', '-') for term in info_terms[:10])
433
  zip_name = f"{timestamp}_{name_text}.zip"
434
 
435
+ with zipfile.ZipFile(zip_name, 'w') as z:
436
  for f in all_files:
437
  z.write(f)
438
 
439
  return zip_name
440
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  def display_file_manager_sidebar(groups_sorted):
442
  """Display file manager in sidebar with timestamp-based groups"""
443
  st.sidebar.title("🎵 Audio & Docs Manager")
 
472
  st.session_state.should_rerun = True
473
  with top_bar[3]:
474
  if st.button("⬇️ ZipAll"):
475
+ zip_name = create_zip_of_files(all_md, all_mp3, all_wav,
476
+ input_question=st.session_state.get('last_query', ''))
477
  if zip_name:
478
+ st.sidebar.markdown(get_download_link(zip_name, file_type="zip"),
479
+ unsafe_allow_html=True)
480
 
481
  for group_name, files in groups_sorted:
482
  timestamp_dt = datetime.strptime(group_name, "%y%m_%H%M") if len(group_name) == 9 else None
483
  group_label = timestamp_dt.strftime("%Y-%m-%d %H:%M") if timestamp_dt else group_name
484
 
485
  with st.sidebar.expander(f"📁 {group_label} ({len(files)})", expanded=True):
486
+ c1, c2 = st.columns(2)
487
  with c1:
488
  if st.button("👀ViewGrp", key="view_group_"+group_name):
489
  st.session_state.viewing_prefix = group_name
 
501
  ctime = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%H:%M:%S")
502
  st.write(f"{emoji} **{fname}** - {ctime}")
503
 
 
504
  def main():
505
+ # Get marquee settings first
506
+ marquee_settings = get_marquee_settings()
507
 
508
+ # Initial welcome marquee
509
+ display_marquee(st.session_state['marquee_content'],
510
+ {**marquee_settings, "font-size": "28px", "lineHeight": "50px"},
511
+ key_suffix="welcome")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
 
513
+ # Load files for sidebar
514
+ groups_sorted = load_files_for_sidebar()
 
 
 
 
 
 
 
515
 
 
 
 
 
 
 
 
 
 
 
 
516
  # Update marquee content when viewing files
517
  if st.session_state.viewing_prefix:
518
  for group_name, files in groups_sorted:
 
520
  for f in files:
521
  if f.endswith('.md'):
522
  with open(f, 'r', encoding='utf-8') as file:
523
+ st.session_state['marquee_content'] = file.read()[:280]
 
524
 
525
+ # Voice Settings
 
526
  st.sidebar.markdown("### 🎤 Voice Settings")
527
  selected_voice = st.sidebar.selectbox(
528
  "Select TTS Voice:",
 
530
  index=EDGE_TTS_VOICES.index(st.session_state['tts_voice'])
531
  )
532
 
533
+ # Audio Format Settings
534
  st.sidebar.markdown("### 🔊 Audio Format")
535
  selected_format = st.sidebar.radio(
536
  "Choose Audio Format:",
537
  options=["MP3", "WAV"],
538
+ index=0
539
  )
540
 
 
541
  if selected_voice != st.session_state['tts_voice']:
542
  st.session_state['tts_voice'] = selected_voice
543
  st.rerun()
 
545
  st.session_state['audio_format'] = selected_format.lower()
546
  st.rerun()
547
 
548
+ # Main Interface
549
+ tab_main = st.radio("Action:", ["🎤 Voice", "📸 Media", "🔍 ArXiv", "📝 Editor"],
550
+ horizontal=True)
551
 
552
  mycomponent = components.declare_component("mycomponent", path="mycomponent")
553
  val = mycomponent(my_input_value="Hello")
554
 
 
555
  if val:
556
  val_stripped = val.replace('\\n', ' ')
557
  edited_input = st.text_area("✏️ Edit Input:", value=val_stripped, height=100)
 
558
 
559
  run_option = st.selectbox("Model:", ["Arxiv", "GPT-4o", "Claude-3.5"])
560
  col1, col2 = st.columns(2)
561
  with col1:
562
  autorun = st.checkbox("⚙ AutoRun", value=True)
563
  with col2:
564
+ full_audio = st.checkbox("📚FullAudio", value=False)
 
565
 
566
  input_changed = (val != st.session_state.old_val)
567
 
568
  if autorun and input_changed:
569
  st.session_state.old_val = val
570
+ st.session_state.last_query = edited_input
571
  if run_option == "Arxiv":
572
  perform_ai_lookup(edited_input, vocal_summary=True, extended_refs=False,
573
+ titles_summary=True, full_audio=full_audio,
574
+ marquee_settings=marquee_settings)
575
  else:
576
  if run_option == "GPT-4o":
577
  process_with_gpt(edited_input)
 
580
  else:
581
  if st.button("▶ Run"):
582
  st.session_state.old_val = val
583
+ st.session_state.last_query = edited_input
584
  if run_option == "Arxiv":
585
  perform_ai_lookup(edited_input, vocal_summary=True, extended_refs=False,
586
+ titles_summary=True, full_audio=full_audio,
587
+ marquee_settings=marquee_settings)
588
  else:
589
  if run_option == "GPT-4o":
590
  process_with_gpt(edited_input)
591
  elif run_option == "Claude-3.5":
592
  process_with_claude(edited_input)
593
 
594
+ # ArXiv Tab
595
  if tab_main == "🔍 ArXiv":
 
 
 
 
 
596
  st.subheader("🔍 Query ArXiv")
597
  q = st.text_input("🔍 Query:")
598
 
 
600
  vocal_summary = st.checkbox("🎙ShortAudio", value=True)
601
  extended_refs = st.checkbox("📜LongRefs", value=False)
602
  titles_summary = st.checkbox("🔖TitlesOnly", value=True)
603
+ full_audio = st.checkbox("📚FullAudio", value=False)
604
+ full_transcript = st.checkbox("🧾FullTranscript", value=False)
 
 
605
 
606
  if q and st.button("🔍Run"):
607
+ st.session_state.last_query = q
608
  result = perform_ai_lookup(q, vocal_summary=vocal_summary, extended_refs=extended_refs,
609
+ titles_summary=titles_summary, full_audio=full_audio,
610
+ marquee_settings=marquee_settings)
611
  if full_transcript:
612
+ create_file(q, result, "md")
 
 
 
 
 
 
 
 
 
613
 
614
+ # Voice Tab
615
  elif tab_main == "🎤 Voice":
616
  st.subheader("🎤 Voice Input")
617
  user_text = st.text_area("💬 Message:", height=100)
618
  user_text = user_text.strip().replace('\n', ' ')
619
+
620
  if st.button("📨 Send"):
621
  process_with_gpt(user_text)
622
+
623
  st.subheader("📜 Chat History")
624
+ t1, t2 = st.tabs(["Claude History", "GPT-4o History"])
625
  with t1:
626
  for c in st.session_state.chat_history:
627
  st.write("**You:**", c["user"])
 
631
  with st.chat_message(m["role"]):
632
  st.markdown(m["content"])
633
 
634
+ # Media Tab
635
  elif tab_main == "📸 Media":
636
  st.header("📸 Images & 🎥 Videos")
637
  tabs = st.tabs(["🖼 Images", "🎥 Video"])
638
  with tabs[0]:
639
+ imgs = glob.glob("*.png") + glob.glob("*.jpg")
640
  if imgs:
641
+ c = st.slider("Cols", 1, 5, 3)
642
  cols = st.columns(c)
643
+ for i, f in enumerate(imgs):
644
+ with cols[i % c]:
645
+ st.image(Image.open(f), use_container_width=True)
646
  if st.button(f"👀 Analyze {os.path.basename(f)}", key=f"analyze_{f}"):
647
+ response = openai_client.chat.completions.create(
648
+ model=st.session_state["openai_model"],
649
+ messages=[
650
+ {"role": "system", "content": "Analyze the image content."},
651
+ {"role": "user", "content": [
652
+ {"type": "image_url",
653
+ "image_url": {"url": f"data:image/jpeg;base64,{base64.b64encode(open(f, 'rb').read()).decode()}"}}
654
+ ]}
655
+ ]
656
+ )
657
+ st.markdown(response.choices[0].message.content)
658
  else:
659
  st.write("No images found.")
660
+
661
  with tabs[1]:
662
  vids = glob.glob("*.mp4")
663
  if vids:
 
665
  with st.expander(f"🎥 {os.path.basename(v)}"):
666
  st.video(v)
667
  if st.button(f"Analyze {os.path.basename(v)}", key=f"analyze_{v}"):
668
+ frames = process_video(v)
669
+ response = openai_client.chat.completions.create(
670
+ model=st.session_state["openai_model"],
671
+ messages=[
672
+ {"role": "system", "content": "Analyze video frames."},
673
+ {"role": "user", "content": [
674
+ {"type": "image_url",
675
+ "image_url": {"url": f"data:image/jpeg;base64,{frame}"}}
676
+ for frame in frames
677
+ ]}
678
+ ]
679
+ )
680
+ st.markdown(response.choices[0].message.content)
681
  else:
682
  st.write("No videos found.")
683
 
684
+ # Editor Tab
685
  elif tab_main == "📝 Editor":
686
+ if st.session_state.editing_file:
687
+ st.subheader(f"Editing: {st.session_state.editing_file}")
688
+ new_text = st.text_area("✏️ Content:", st.session_state.edit_new_content, height=300)
689
  if st.button("💾 Save"):
690
+ with open(st.session_state.editing_file, 'w', encoding='utf-8') as f:
691
  f.write(new_text)
692
+ st.success("File updated successfully!")
693
  st.session_state.should_rerun = True
694
+ st.session_state.editing_file = None
695
  else:
696
  st.write("Select a file from the sidebar to edit.")
697
 
698
+ # Display file manager in sidebar
 
699
  display_file_manager_sidebar(groups_sorted)
700
 
701
+ # Display viewed group content
702
  if st.session_state.viewing_prefix and any(st.session_state.viewing_prefix == group for group, _ in groups_sorted):
703
  st.write("---")
704
  st.write(f"**Viewing Group:** {st.session_state.viewing_prefix}")
 
709
  ext = os.path.splitext(fname)[1].lower().strip('.')
710
  st.write(f"### {fname}")
711
  if ext == "md":
712
+ content = open(f, 'r', encoding='utf-8').read()
713
  st.markdown(content)
714
+ elif ext in ["mp3", "wav"]:
715
  st.audio(f)
 
 
716
  else:
717
  st.markdown(get_download_link(f), unsafe_allow_html=True)
718
  break
719
  if st.button("❌ Close"):
720
  st.session_state.viewing_prefix = None
721
+ st.session_state['marquee_content'] = "🚀 Welcome to TalkingAIResearcher | 🤖 Your Research Assistant"
722
+
723
+ # Add custom CSS
724
+ st.markdown("""
725
+ <style>
726
+ .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
727
+ .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
728
+ .stButton>button { margin-right: 0.5rem; }
729
+ </style>
730
+ """, unsafe_allow_html=True)
731
+
732
+ # Handle rerun if needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
  if st.session_state.should_rerun:
734
  st.session_state.should_rerun = False
735
  st.rerun()
736
 
737
+ if __name__ == "__main__":
738
  main()