awacke1 commited on
Commit
a820539
ยท
verified ยท
1 Parent(s): 00c86b5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +265 -370
app.py CHANGED
@@ -1,24 +1,18 @@
1
  import streamlit as st
2
- import anthropic, openai, base64, cv2, glob, json, math, os, pytz, random, re, requests, textract, time, zipfile
3
- import plotly.graph_objects as go
4
- import streamlit.components.v1 as components
 
 
 
 
 
 
5
  from datetime import datetime
6
- from audio_recorder_streamlit import audio_recorder
7
- from bs4 import BeautifulSoup
8
- from collections import defaultdict, deque
9
  from dotenv import load_dotenv
10
  from gradio_client import Client
11
- from huggingface_hub import InferenceClient
12
- from io import BytesIO
13
  from PIL import Image
14
- from PyPDF2 import PdfReader
15
- from urllib.parse import quote
16
- from xml.etree import ElementTree as ET
17
- from openai import OpenAI
18
- 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
  # ๐ŸŽฏ 1. Core Configuration & Setup
24
  st.set_page_config(
@@ -36,8 +30,7 @@ load_dotenv()
36
 
37
  # ๐Ÿ”‘ 2. API Setup & Clients
38
  openai_api_key = os.getenv('OPENAI_API_KEY', "")
39
- anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', "")
40
- xai_key = os.getenv('xai',"")
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,41 +38,29 @@ 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(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
49
- HF_KEY = os.getenv('HF_KEY')
50
- API_URL = os.getenv('API_URL')
51
 
52
  # ๐Ÿ“ 3. Session State Management
53
- if 'transcript_history' not in st.session_state:
54
- st.session_state['transcript_history'] = []
55
- if 'chat_history' not in st.session_state:
56
- st.session_state['chat_history'] = []
57
- if 'openai_model' not in st.session_state:
58
- st.session_state['openai_model'] = "gpt-4o-2024-05-13"
59
- if 'messages' not in st.session_state:
60
- st.session_state['messages'] = []
61
- if 'last_voice_input' not in st.session_state:
62
- st.session_state['last_voice_input'] = ""
63
- if 'editing_file' not in st.session_state:
64
- st.session_state['editing_file'] = None
65
- if 'edit_new_name' not in st.session_state:
66
- st.session_state['edit_new_name'] = ""
67
- if 'edit_new_content' not in st.session_state:
68
- st.session_state['edit_new_content'] = ""
69
  if 'viewing_prefix' not in st.session_state:
70
  st.session_state['viewing_prefix'] = None
71
  if 'should_rerun' not in st.session_state:
72
  st.session_state['should_rerun'] = False
73
- if 'old_val' not in st.session_state:
74
- st.session_state['old_val'] = None
75
- if 'audio_generated' not in st.session_state:
76
- st.session_state['audio_generated'] = {}
77
 
78
  # ๐ŸŽจ 4. Custom CSS
79
  st.markdown("""
80
  <style>
81
- .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
82
- .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
 
 
 
 
 
83
  .stButton>button {
84
  margin-right: 0.5rem;
85
  }
@@ -153,46 +134,34 @@ def clean_text_for_filename(text: str) -> str:
153
  return '_'.join(filtered)[:200]
154
 
155
  # ๐Ÿ“ 6. File Operations
156
- def generate_filename(prompt, response, file_type="md"):
157
  """
158
- Generate filename with meaningful terms and short dense clips from prompt & response.
159
- The filename should be about 150 chars total, include high-info terms, and a clipped snippet.
160
  """
161
- prefix = datetime.now().strftime("%y%m_%H%M") + "_"
162
- combined = (prompt + " " + response).strip()
163
- info_terms = get_high_info_terms(combined)
164
-
165
- # Include a short snippet from prompt and response
166
- snippet = (prompt[:100] + " " + response[:100]).strip()
167
- snippet_cleaned = clean_text_for_filename(snippet)
168
-
169
- # Combine info terms and snippet
170
- name_parts = info_terms + [snippet_cleaned]
171
- full_name = '_'.join(name_parts)
172
-
173
- # Trim to ~150 chars
174
- if len(full_name) > 150:
175
- full_name = full_name[:150]
176
-
177
- filename = f"{prefix}{full_name}.{file_type}"
178
  return filename
179
 
180
- def create_file(prompt, response, file_type="md"):
181
- """Create file with intelligent naming"""
182
- filename = generate_filename(prompt.strip(), response.strip(), file_type)
 
183
  with open(filename, 'w', encoding='utf-8') as f:
184
- f.write(prompt.replace('\n', ' ') + "\n\n" + response)
185
  return filename
186
 
187
  def get_download_link(file):
188
- """Generate download link for file"""
189
- with open(file, "rb") as f:
190
- b64 = base64.b64encode(f.read()).decode()
191
- return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">๐Ÿ“‚ Download {os.path.basename(file)}</a>'
 
192
 
193
  # ๐Ÿ”Š 7. Audio Processing
194
  def clean_for_speech(text: str) -> str:
195
- """Clean text for speech synthesis"""
196
  text = text.replace("\n", " ")
197
  text = text.replace("</s>", " ")
198
  text = text.replace("#", "")
@@ -200,33 +169,20 @@ def clean_for_speech(text: str) -> str:
200
  text = re.sub(r"\s+", " ", text).strip()
201
  return text
202
 
203
- @st.cache_resource
204
- def speech_synthesis_html(result):
205
- """Create HTML for speech synthesis"""
206
- html_code = f"""
207
- <html><body>
208
- <script>
209
- var msg = new SpeechSynthesisUtterance("{result.replace('"', '')}");
210
- window.speechSynthesis.speak(msg);
211
- </script>
212
- </body></html>
213
- """
214
- components.html(html_code, height=0)
215
-
216
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
217
- """Generate audio using Edge TTS"""
218
  text = clean_for_speech(text)
219
  if not text.strip():
220
  return None
221
  rate_str = f"{rate:+d}%"
222
  pitch_str = f"{pitch:+d}Hz"
223
  communicate = edge_tts.Communicate(text, voice, rate=rate_str, pitch=pitch_str)
224
- out_fn = generate_filename(text, text, "mp3")
225
  await communicate.save(out_fn)
226
  return out_fn
227
 
228
  def speak_with_edge_tts(text, voice, rate=0, pitch=0):
229
- """Wrapper for edge TTS generation"""
230
  try:
231
  return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch))
232
  except Exception as e:
@@ -234,49 +190,45 @@ def speak_with_edge_tts(text, voice, rate=0, pitch=0):
234
  return None
235
 
236
  def play_and_download_audio(file_path):
237
- """Play and provide download link for audio"""
238
  if file_path and os.path.exists(file_path):
239
  st.audio(file_path)
240
- dl_link = f'<a href="data:audio/mpeg;base64,{base64.b64encode(open(file_path,"rb").read()).decode()}" download="{os.path.basename(file_path)}">Download {os.path.basename(file_path)}</a>'
241
  st.markdown(dl_link, unsafe_allow_html=True)
242
 
243
  # ๐ŸŽฌ 8. Media Processing
244
  def process_image(image_path, user_prompt):
245
- """Process image with GPT-4V"""
246
  with open(image_path, "rb") as imgf:
247
  image_data = imgf.read()
248
  b64img = base64.b64encode(image_data).decode("utf-8")
249
- resp = openai_client.chat.completions.create(
250
  model=st.session_state["openai_model"],
251
  messages=[
252
  {"role": "system", "content": "You are a helpful assistant."},
253
- {"role": "user", "content": [
254
- {"type": "text", "text": user_prompt},
255
- {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64img}"}}
256
- ]}
257
  ],
258
  temperature=0.0,
259
  )
260
  return resp.choices[0].message.content
261
 
262
- def process_audio(audio_path):
263
- """Process audio with Whisper"""
264
  with open(audio_path, "rb") as f:
265
- transcription = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
266
- st.session_state.messages.append({"role": "user", "content": transcription.text})
267
- return transcription.text
268
 
269
  def process_video(video_path, seconds_per_frame=1):
270
- """Extract frames from video"""
271
  vid = cv2.VideoCapture(video_path)
272
  total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
273
  fps = vid.get(cv2.CAP_PROP_FPS)
274
- skip = int(fps*seconds_per_frame)
275
  frames_b64 = []
276
  for i in range(0, total, skip):
277
  vid.set(cv2.CAP_PROP_POS_FRAMES, i)
278
  ret, frame = vid.read()
279
- if not ret:
280
  break
281
  _, buf = cv2.imencode(".jpg", frame)
282
  frames_b64.append(base64.b64encode(buf).decode("utf-8"))
@@ -284,75 +236,92 @@ def process_video(video_path, seconds_per_frame=1):
284
  return frames_b64
285
 
286
  def process_video_with_gpt(video_path, prompt):
287
- """Analyze video frames with GPT-4V"""
288
  frames = process_video(video_path)
289
- resp = openai_client.chat.completions.create(
 
290
  model=st.session_state["openai_model"],
291
  messages=[
292
- {"role":"system","content":"Analyze video frames."},
293
- {"role":"user","content":[
294
- {"type":"text","text":prompt},
295
- *[{"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{fr}"}} for fr in frames]
296
- ]}
297
  ]
298
  )
299
  return resp.choices[0].message.content
300
 
301
  # ๐Ÿค– 9. AI Model Integration
302
-
303
- def save_full_transcript(query, text):
304
- """Save full transcript of Arxiv results as a file."""
305
- create_file(query, text, "md")
306
-
307
- def parse_arxiv_refs(ref_text: str):
308
  """
309
- Parse the multi-line references returned by the RAG pipeline.
310
- Typical format lines like:
311
- 1) [Paper Title 2023] This is the summary ...
312
- 2) [Another Title (2024)] Another summary text ...
313
- We'll attempt to find a year with a small regex or fallback.
314
- Return list of dicts: { 'title': str, 'summary': str, 'year': int or None }
315
  """
316
- lines = ref_text.split('\n')
317
- results = []
318
- for line in lines:
319
- line = line.strip()
320
- if not line:
321
- continue
322
- # Attempt to find [Title ...]
323
- title_match = re.search(r"\[([^\]]+)\]", line)
324
- if title_match:
325
- raw_title = title_match.group(1).strip()
326
- else:
327
- # If no bracket found, skip or treat entire line as summary
328
- raw_title = "No Title"
329
-
330
- # Attempt to find trailing summary after bracket
331
- # Example line: " [Paper Title 2024] Paper summary blah blah"
332
- # So remove the bracketed portion from the line
333
- remainder = line.replace(title_match.group(0), "").strip() if title_match else line
334
- summary = remainder
335
-
336
- # Attempt to guess year from the raw title
337
- # We look for 4-digit patterns in raw_title or summary
338
- year_match = re.search(r'(20\d{2})', raw_title)
339
- if not year_match:
340
- # fallback: try summary
341
- year_match = re.search(r'(20\d{2})', summary)
342
- if year_match:
343
- year = int(year_match.group(1))
344
- else:
345
- year = None
346
-
347
- results.append({
348
- 'title': raw_title,
349
- 'summary': summary,
350
- 'year': year
351
- })
352
- if len(results) >= 20:
353
- break
354
- return results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
 
 
 
 
 
 
 
 
 
 
356
 
357
  def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
358
  titles_summary=True, full_audio=False, selected_voice="en-US-AriaNeural"):
@@ -361,123 +330,33 @@ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
361
 
362
  # ๐ŸŽฏ 1) Query the HF RAG pipeline
363
  client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
364
- refs = client.predict(q,20,"Semantic Search","mistralai/Mixtral-8x7B-Instruct-v0.1",api_name="/update_with_rag_md")[0]
365
- r2 = client.predict(q,"mistralai/Mixtral-8x7B-Instruct-v0.1",True,api_name="/ask_llm")
366
-
367
  # ๐ŸŽฏ 2) Combine for final text output
368
  clean_q = q.replace('\n', ' ')
369
  result = f"### ๐Ÿ”Ž {clean_q}\n\n{r2}\n\n{refs}"
370
  st.markdown(result)
371
-
372
- # ๐ŸŽฏ 3) Generate "all at once" audio if requested
373
- if full_audio:
374
- complete_text = f"Complete response for query: {clean_q}. {clean_for_speech(r2)} {clean_for_speech(refs)}"
375
- audio_file_full = speak_with_edge_tts(complete_text, selected_voice)
376
- st.write("### ๐Ÿ“š Full Audio")
377
- play_and_download_audio(audio_file_full)
378
-
379
- if vocal_summary:
380
- main_text = clean_for_speech(r2)
381
- audio_file_main = speak_with_edge_tts(main_text, selected_voice)
382
- st.write("### ๐ŸŽ™ Short Audio")
383
- play_and_download_audio(audio_file_main)
384
-
385
- if extended_refs:
386
- summaries_text = "Extended references: " + refs.replace('"','')
387
- summaries_text = clean_for_speech(summaries_text)
388
- audio_file_refs = speak_with_edge_tts(summaries_text, selected_voice)
389
- st.write("### ๐Ÿ“œ Long Refs")
390
- play_and_download_audio(audio_file_refs)
391
-
392
- # --------------------------------------
393
- # NEW: Parse references, show sorted list
394
- # --------------------------------------
395
- parsed_refs = parse_arxiv_refs(refs)
396
-
397
- st.write("## Individual Papers (Most Recent First)")
398
- for idx, paper in enumerate(parsed_refs):
399
- section_key = f"section_{idx}"
400
- with st.expander(f"{idx+1}. {paper['title']} - {paper['year'] if paper['year'] else 'Unknown Year'}", expanded=False):
401
- st.markdown(f"**Summary:** {paper['summary']}")
402
- colA, colB = st.columns(2)
403
- with colA:
404
- if st.checkbox(f"Generate Audio for Title", key=f"gen_title_{idx}"):
405
- if f"title_audio_{idx}" not in st.session_state['audio_generated']:
406
- text_tts = clean_for_speech(paper['title'])
407
- audio_file_title = speak_with_edge_tts(text_tts, selected_voice)
408
- st.session_state['audio_generated'][f"title_audio_{idx}"] = audio_file_title
409
- play_and_download_audio(st.session_state['audio_generated'].get(f"title_audio_{idx}"))
410
- with colB:
411
- if st.checkbox(f"Generate Audio for Title + Summary", key=f"gen_summary_{idx}"):
412
- if f"summary_audio_{idx}" not in st.session_state['audio_generated']:
413
- text_tts = clean_for_speech(paper['title'] + ". " + paper['summary'])
414
- audio_file_title_summary = speak_with_edge_tts(text_tts, selected_voice)
415
- st.session_state['audio_generated'][f"summary_audio_{idx}"] = audio_file_title_summary
416
- play_and_download_audio(st.session_state['audio_generated'].get(f"summary_audio_{idx}"))
417
- st.write("---")
418
-
419
- # Keep your original block for "Titles Only" if you want:
420
- if titles_summary:
421
- titles = []
422
- for line in refs.split('\n')[:20]:
423
- m = re.search(r"\[([^\]]+)\]", line)
424
- if m:
425
- titles.append(m.group(1))
426
- if titles:
427
- titles_text = "Titles: " + ", ".join(titles)
428
- titles_text = clean_for_speech(titles_text)
429
- audio_file_titles = speak_with_edge_tts(titles_text, selected_voice)
430
- st.write("### ๐Ÿ”– Titles (All-In-One)")
431
- play_and_download_audio(audio_file_titles)
432
-
433
- elapsed = time.time()-start
434
  st.write(f"**Total Elapsed:** {elapsed:.2f} s")
435
-
436
  # Always create a file with the result
437
- create_file(clean_q, result, "md")
438
-
439
  return result
440
 
441
- def process_with_gpt(text):
442
- """Process text with GPT-4"""
443
- if not text:
444
- return
445
- st.session_state.messages.append({"role":"user","content":text})
446
- with st.chat_message("user"):
447
- st.markdown(text)
448
- with st.chat_message("assistant"):
449
- c = openai_client.chat.completions.create(
450
- model=st.session_state["openai_model"],
451
- messages=st.session_state.messages,
452
- stream=False
453
- )
454
- ans = c.choices[0].message.content
455
- st.write("GPT-4o: " + ans)
456
- create_file(text, ans, "md")
457
- st.session_state.messages.append({"role":"assistant","content":ans})
458
- return ans
459
-
460
- def process_with_claude(text):
461
- """Process text with Claude"""
462
- if not text:
463
- return
464
- with st.chat_message("user"):
465
- st.markdown(text)
466
- with st.chat_message("assistant"):
467
- r = claude_client.messages.create(
468
- model="claude-3-sonnet-20240229",
469
- max_tokens=1000,
470
- messages=[{"role":"user","content":text}]
471
- )
472
- ans = r.content[0].text
473
- st.write("Claude-3.5: " + ans)
474
- create_file(text, ans, "md")
475
- st.session_state.chat_history.append({"user":text,"claude":ans})
476
- return ans
477
-
478
  # ๐Ÿ“‚ 10. File Management
479
  def create_zip_of_files(md_files, mp3_files):
480
- """Create zip with intelligent naming"""
481
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
482
  all_files = md_files + mp3_files
483
  if not all_files:
@@ -491,22 +370,22 @@ def create_zip_of_files(md_files, mp3_files):
491
  all_content.append(file.read())
492
  elif f.endswith('.mp3'):
493
  all_content.append(os.path.basename(f))
494
-
495
  combined_content = " ".join(all_content)
496
  info_terms = get_high_info_terms(combined_content)
497
-
498
  timestamp = datetime.now().strftime("%y%m_%H%M")
499
  name_text = '_'.join(term.replace(' ', '-') for term in info_terms[:3])
500
  zip_name = f"{timestamp}_{name_text}.zip"
501
-
502
  with zipfile.ZipFile(zip_name,'w') as z:
503
  for f in all_files:
504
  z.write(f)
505
-
506
  return zip_name
507
 
508
  def load_files_for_sidebar():
509
- """Load and group files for sidebar display"""
510
  md_files = glob.glob("*.md")
511
  mp3_files = glob.glob("*.mp3")
512
 
@@ -528,7 +407,7 @@ def load_files_for_sidebar():
528
  return groups, sorted_prefixes
529
 
530
  def extract_keywords_from_md(files):
531
- """Extract keywords from markdown files"""
532
  text = ""
533
  for f in files:
534
  if f.endswith(".md"):
@@ -537,7 +416,7 @@ def extract_keywords_from_md(files):
537
  return get_high_info_terms(text)
538
 
539
  def display_file_manager_sidebar(groups, sorted_prefixes):
540
- """Display file manager in sidebar"""
541
  st.sidebar.title("๐ŸŽต Audio & Docs Manager")
542
 
543
  all_md = []
@@ -564,19 +443,19 @@ def display_file_manager_sidebar(groups, sorted_prefixes):
564
  if st.button("โฌ‡๏ธ ZipAll"):
565
  z = create_zip_of_files(all_md, all_mp3)
566
  if z:
567
- st.sidebar.markdown(get_download_link(z),unsafe_allow_html=True)
568
 
569
  for prefix in sorted_prefixes:
570
  files = groups[prefix]
571
  kw = extract_keywords_from_md(files)
572
  keywords_str = " ".join(kw) if kw else "No Keywords"
573
  with st.sidebar.expander(f"{prefix} Files ({len(files)}) - KW: {keywords_str}", expanded=True):
574
- c1,c2 = st.columns(2)
575
  with c1:
576
- if st.button("๐Ÿ‘€ViewGrp", key="view_group_"+prefix):
577
  st.session_state.viewing_prefix = prefix
578
  with c2:
579
- if st.button("๐Ÿ—‘DelGrp", key="del_group_"+prefix):
580
  for f in files:
581
  os.remove(f)
582
  st.success(f"Deleted group {prefix}!")
@@ -598,12 +477,10 @@ def fetch_voices():
598
 
599
  def main():
600
  st.sidebar.markdown("### ๐ŸšฒBikeAI๐Ÿ† Multi-Agent Research")
601
- tab_main = st.radio("Action:",["๐ŸŽค Voice","๐Ÿ“ธ Media","๐Ÿ” ArXiv","๐Ÿ“ Editor"],horizontal=True)
602
-
603
- mycomponent = components.declare_component("mycomponent", path="mycomponent")
604
- val = mycomponent(my_input_value="Hello")
605
 
606
- if 'voices' not in st.session_state:
 
607
  st.session_state['voices'] = fetch_voices()
608
 
609
  st.sidebar.markdown("### ๐ŸŽค Select Voice for Audio Generation")
@@ -613,78 +490,41 @@ def main():
613
  index=st.session_state['voices'].index("en-US-AriaNeural") if "en-US-AriaNeural" in st.session_state['voices'] else 0
614
  )
615
 
616
- # Show input in a text box for editing if detected
617
- if val:
618
- val_stripped = val.replace('\n', ' ')
619
- edited_input = st.text_area("โœ๏ธ Edit Input:", value=val_stripped, height=100)
620
- run_option = st.selectbox("Model:", ["Arxiv", "GPT-4o", "Claude-3.5"])
621
- col1, col2 = st.columns(2)
622
- with col1:
623
- autorun = st.checkbox("โš™ AutoRun", value=True)
624
- with col2:
625
- full_audio = st.checkbox("๐Ÿ“šFullAudio", value=False,
626
- help="Generate full audio response")
627
-
628
- input_changed = (val != st.session_state.old_val)
629
-
630
- if autorun and input_changed:
631
- st.session_state.old_val = val
632
- if run_option == "Arxiv":
633
- perform_ai_lookup(
634
- edited_input,
635
- vocal_summary=True,
636
- extended_refs=False,
637
- titles_summary=True,
638
- full_audio=full_audio,
639
- selected_voice=selected_voice
640
- )
641
- else:
642
- if run_option == "GPT-4o":
643
- process_with_gpt(edited_input)
644
- elif run_option == "Claude-3.5":
645
- process_with_claude(edited_input)
646
- else:
647
- if st.button("โ–ถ Run"):
648
- st.session_state.old_val = val
649
- if run_option == "Arxiv":
650
- perform_ai_lookup(
651
- edited_input,
652
- vocal_summary=True,
653
- extended_refs=False,
654
- titles_summary=True,
655
- full_audio=full_audio,
656
- selected_voice=selected_voice
657
- )
658
- else:
659
- if run_option == "GPT-4o":
660
- process_with_gpt(edited_input)
661
- elif run_option == "Claude-3.5":
662
- process_with_claude(edited_input)
663
-
664
  if tab_main == "๐Ÿ” ArXiv":
665
  st.subheader("๐Ÿ” Query ArXiv")
666
  q = st.text_input("๐Ÿ” Query:").replace('\n', ' ')
667
 
668
  st.markdown("### ๐ŸŽ› Options")
669
- vocal_summary = st.checkbox("๐ŸŽ™ShortAudio", value=True)
670
- extended_refs = st.checkbox("๐Ÿ“œLongRefs", value=False)
671
- titles_summary = st.checkbox("๐Ÿ”–TitlesOnly", value=True)
672
- full_audio = st.checkbox("๐Ÿ“šFullAudio", value=False,
673
- help="Full audio of results")
674
- full_transcript = st.checkbox("๐ŸงพFullTranscript", value=False,
675
- help="Generate a full transcript file")
676
-
677
- if q and st.button("๐Ÿ”Run"):
678
- result = perform_ai_lookup(q, vocal_summary=vocal_summary, extended_refs=extended_refs,
679
- titles_summary=titles_summary, full_audio=full_audio, selected_voice=selected_voice)
 
 
 
 
680
  if full_transcript:
681
  save_full_transcript(q, result)
682
 
683
  st.markdown("### Change Prompt & Re-Run")
684
  q_new = st.text_input("๐Ÿ”„ Modify Query:").replace('\n', ' ')
685
  if q_new and st.button("๐Ÿ”„ Re-Run with Modified Query"):
686
- result = perform_ai_lookup(q_new, vocal_summary=vocal_summary, extended_refs=extended_refs,
687
- titles_summary=titles_summary, full_audio=full_audio, selected_voice=selected_voice)
 
 
 
 
 
 
688
  if full_transcript:
689
  save_full_transcript(q_new, result)
690
 
@@ -695,13 +535,13 @@ def main():
695
  if st.button("๐Ÿ“จ Send"):
696
  process_with_gpt(user_text)
697
  st.subheader("๐Ÿ“œ Chat History")
698
- t1,t2=st.tabs(["Claude History","GPT-4o History"])
699
  with t1:
700
- for c in st.session_state.chat_history:
701
  st.write("**You:**", c["user"])
702
  st.write("**Claude:**", c["claude"])
703
  with t2:
704
- for m in st.session_state.messages:
705
  with st.chat_message(m["role"]):
706
  st.markdown(m["content"])
707
 
@@ -709,18 +549,18 @@ def main():
709
  st.header("๐Ÿ“ธ Images & ๐ŸŽฅ Videos")
710
  tabs = st.tabs(["๐Ÿ–ผ Images", "๐ŸŽฅ Video"])
711
  with tabs[0]:
712
- imgs = glob.glob("*.png")+glob.glob("*.jpg")
713
  if imgs:
714
- c = st.slider("Cols",1,5,3)
715
- cols = st.columns(c)
716
- for i,f in enumerate(imgs[:20]):
717
- with cols[i%c]:
718
- st.image(Image.open(f),use_container_width=True)
719
  if st.button(f"๐Ÿ‘€ Analyze {os.path.basename(f)}", key=f"analyze_{f}"):
720
- a = process_image(f,"Describe this image.")
721
- st.markdown(a)
722
  else:
723
  st.write("No images found.")
 
724
  with tabs[1]:
725
  vids = glob.glob("*.mp4")[:20]
726
  if vids:
@@ -728,23 +568,29 @@ def main():
728
  with st.expander(f"๐ŸŽฅ {os.path.basename(v)}"):
729
  st.video(v)
730
  if st.button(f"Analyze {os.path.basename(v)}", key=f"analyze_{v}"):
731
- a = process_video_with_gpt(v,"Describe video.")
732
- st.markdown(a)
733
  else:
734
  st.write("No videos found.")
735
 
736
  elif tab_main == "๐Ÿ“ Editor":
737
- if getattr(st.session_state,'current_file',None):
738
- st.subheader(f"Editing: {st.session_state.current_file}")
739
- new_text = st.text_area("โœ๏ธ Content:", st.session_state.file_content, height=300)
740
- if st.button("๐Ÿ’พ Save"):
741
- with open(st.session_state.current_file,'w',encoding='utf-8') as f:
742
- f.write(new_text)
743
- st.success("Updated!")
744
- st.session_state.should_rerun = True
 
 
 
 
 
745
  else:
746
- st.write("Select a file from the sidebar to edit.")
747
 
 
748
  groups, sorted_prefixes = load_files_for_sidebar()
749
  display_file_manager_sidebar(groups, sorted_prefixes)
750
 
@@ -756,7 +602,8 @@ def main():
756
  ext = os.path.splitext(fname)[1].lower().strip('.')
757
  st.write(f"### {fname}")
758
  if ext == "md":
759
- content = open(f,'r',encoding='utf-8').read()
 
760
  st.markdown(content)
761
  elif ext == "mp3":
762
  st.audio(f)
@@ -767,7 +614,55 @@ def main():
767
 
768
  if st.session_state.should_rerun:
769
  st.session_state.should_rerun = False
770
- st.rerun()
771
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  if __name__=="__main__":
773
  main()
 
1
  import streamlit as st
2
+ import anthropic
3
+ import openai
4
+ import base64
5
+ import cv2
6
+ import glob
7
+ import os
8
+ import re
9
+ import asyncio
10
+ import edge_tts
11
  from datetime import datetime
12
+ from collections import defaultdict
 
 
13
  from dotenv import load_dotenv
14
  from gradio_client import Client
 
 
15
  from PIL import Image
 
 
 
 
 
 
 
 
16
 
17
  # ๐ŸŽฏ 1. Core Configuration & Setup
18
  st.set_page_config(
 
30
 
31
  # ๐Ÿ”‘ 2. API Setup & Clients
32
  openai_api_key = os.getenv('OPENAI_API_KEY', "")
33
+ anthropic_key = os.getenv('ANTHROPIC_API_KEY', "")
 
34
  if 'OPENAI_API_KEY' in st.secrets:
35
  openai_api_key = st.secrets['OPENAI_API_KEY']
36
  if 'ANTHROPIC_API_KEY' in st.secrets:
 
38
 
39
  openai.api_key = openai_api_key
40
  claude_client = anthropic.Anthropic(api_key=anthropic_key)
 
 
 
41
 
42
  # ๐Ÿ“ 3. Session State Management
43
+ if 'parsed_papers' not in st.session_state:
44
+ st.session_state['parsed_papers'] = []
45
+ if 'audio_generated' not in st.session_state:
46
+ st.session_state['audio_generated'] = {}
47
+ if 'voices' not in st.session_state:
48
+ st.session_state['voices'] = []
 
 
 
 
 
 
 
 
 
 
49
  if 'viewing_prefix' not in st.session_state:
50
  st.session_state['viewing_prefix'] = None
51
  if 'should_rerun' not in st.session_state:
52
  st.session_state['should_rerun'] = False
 
 
 
 
53
 
54
  # ๐ŸŽจ 4. Custom CSS
55
  st.markdown("""
56
  <style>
57
+ .main {
58
+ background: linear-gradient(to right, #1a1a1a, #2d2d2d);
59
+ color: #fff;
60
+ }
61
+ .stMarkdown {
62
+ font-family: 'Helvetica Neue', sans-serif;
63
+ }
64
  .stButton>button {
65
  margin-right: 0.5rem;
66
  }
 
134
  return '_'.join(filtered)[:200]
135
 
136
  # ๐Ÿ“ 6. File Operations
137
+ def generate_filename(prefix, title, file_type="md"):
138
  """
139
+ Generate filename with meaningful terms and prefix.
140
+ The filename includes a timestamp and a cleaned title.
141
  """
142
+ timestamp = datetime.now().strftime("%y%m_%H%M")
143
+ title_cleaned = clean_text_for_filename(title)
144
+ filename = f"{timestamp}_{prefix}_{title_cleaned}.{file_type}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  return filename
146
 
147
+ def create_md_file(paper):
148
+ """Create Markdown file for a paper."""
149
+ filename = generate_filename("paper", paper['title'], "md")
150
+ content = f"# {paper['title']}\n\n**Year:** {paper['year'] if paper['year'] else 'Unknown'}\n\n**Summary:**\n{paper['summary']}"
151
  with open(filename, 'w', encoding='utf-8') as f:
152
+ f.write(content)
153
  return filename
154
 
155
  def get_download_link(file):
156
+ """Generate download link for file."""
157
+ with open(file, "rb") as f_file:
158
+ b64 = base64.b64encode(f_file.read()).decode()
159
+ mime_type = "audio/mpeg" if file.endswith(".mp3") else "text/markdown"
160
+ return f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file)}">๐Ÿ“‚ Download {os.path.basename(file)}</a>'
161
 
162
  # ๐Ÿ”Š 7. Audio Processing
163
  def clean_for_speech(text: str) -> str:
164
+ """Clean text for speech synthesis."""
165
  text = text.replace("\n", " ")
166
  text = text.replace("</s>", " ")
167
  text = text.replace("#", "")
 
169
  text = re.sub(r"\s+", " ", text).strip()
170
  return text
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
173
+ """Generate audio using Edge TTS."""
174
  text = clean_for_speech(text)
175
  if not text.strip():
176
  return None
177
  rate_str = f"{rate:+d}%"
178
  pitch_str = f"{pitch:+d}Hz"
179
  communicate = edge_tts.Communicate(text, voice, rate=rate_str, pitch=pitch_str)
180
+ out_fn = generate_filename("audio", text[:50], "mp3")
181
  await communicate.save(out_fn)
182
  return out_fn
183
 
184
  def speak_with_edge_tts(text, voice, rate=0, pitch=0):
185
+ """Wrapper for Edge TTS generation."""
186
  try:
187
  return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch))
188
  except Exception as e:
 
190
  return None
191
 
192
  def play_and_download_audio(file_path):
193
+ """Play and provide download link for audio."""
194
  if file_path and os.path.exists(file_path):
195
  st.audio(file_path)
196
+ dl_link = get_download_link(file_path)
197
  st.markdown(dl_link, unsafe_allow_html=True)
198
 
199
  # ๐ŸŽฌ 8. Media Processing
200
  def process_image(image_path, user_prompt):
201
+ """Process image with GPT-4V."""
202
  with open(image_path, "rb") as imgf:
203
  image_data = imgf.read()
204
  b64img = base64.b64encode(image_data).decode("utf-8")
205
+ resp = openai.ChatCompletion.create(
206
  model=st.session_state["openai_model"],
207
  messages=[
208
  {"role": "system", "content": "You are a helpful assistant."},
209
+ {"role": "user", "content": f"{user_prompt} Image data: data:image/png;base64,{b64img}"}
 
 
 
210
  ],
211
  temperature=0.0,
212
  )
213
  return resp.choices[0].message.content
214
 
215
+ def process_audio_file(audio_path):
216
+ """Process audio with Whisper."""
217
  with open(audio_path, "rb") as f:
218
+ transcription = openai.Audio.transcribe("whisper-1", f)
219
+ return transcription['text']
 
220
 
221
  def process_video(video_path, seconds_per_frame=1):
222
+ """Extract frames from video."""
223
  vid = cv2.VideoCapture(video_path)
224
  total = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
225
  fps = vid.get(cv2.CAP_PROP_FPS)
226
+ skip = int(fps * seconds_per_frame)
227
  frames_b64 = []
228
  for i in range(0, total, skip):
229
  vid.set(cv2.CAP_PROP_POS_FRAMES, i)
230
  ret, frame = vid.read()
231
+ if not ret:
232
  break
233
  _, buf = cv2.imencode(".jpg", frame)
234
  frames_b64.append(base64.b64encode(buf).decode("utf-8"))
 
236
  return frames_b64
237
 
238
  def process_video_with_gpt(video_path, prompt):
239
+ """Analyze video frames with GPT-4V."""
240
  frames = process_video(video_path)
241
+ combined_images = " ".join([f"data:image/jpeg;base64,{fr}" for fr in frames])
242
+ resp = openai.ChatCompletion.create(
243
  model=st.session_state["openai_model"],
244
  messages=[
245
+ {"role":"system","content":"Analyze the following video frames."},
246
+ {"role":"user","content": f"{prompt} Frames: {combined_images}"}
 
 
 
247
  ]
248
  )
249
  return resp.choices[0].message.content
250
 
251
  # ๐Ÿค– 9. AI Model Integration
252
+ def parse_papers(transcript_text: str):
 
 
 
 
 
253
  """
254
+ Parse the transcript text into individual papers.
255
+ Assumes that each paper starts with a number and is enclosed in brackets for the title and year.
256
+ Example:
257
+ 1) [Paper Title (2023)] This is the summary...
 
 
258
  """
259
+ papers = []
260
+ # Split based on numbered entries
261
+ paper_blocks = re.split(r'\d+\)\s*\[', transcript_text)
262
+ for block in paper_blocks[1:]: # Skip the first split as it doesn't contain paper info
263
+ try:
264
+ title_year, summary = block.split(']', 1)
265
+ # Extract title and year using regex
266
+ title_match = re.match(r"(.+?)\s*\((\d{4})\)", title_year)
267
+ if title_match:
268
+ title = title_match.group(1).strip()
269
+ year = int(title_match.group(2))
270
+ else:
271
+ title = title_year.strip()
272
+ year = None
273
+ summary = summary.strip()
274
+ papers.append({
275
+ 'title': title,
276
+ 'year': year,
277
+ 'summary': summary
278
+ })
279
+ except ValueError:
280
+ continue # Skip blocks that don't match the expected format
281
+ return papers
282
+
283
+ def save_paper_files(paper, voice):
284
+ """Generate and save Markdown and MP3 files for a paper."""
285
+ # Create Markdown file
286
+ md_filename = create_md_file(paper)
287
+
288
+ # Generate audio for the entire paper
289
+ audio_text = f"{paper['title']}. {paper['summary']}"
290
+ audio_filename = speak_with_edge_tts(audio_text, voice)
291
+
292
+ return md_filename, audio_filename
293
+
294
+ def display_papers(papers, voice):
295
+ """Display all papers with options to generate audio."""
296
+ for idx, paper in enumerate(papers):
297
+ st.markdown(f"### {idx + 1}. {paper['title']} ({paper['year'] if paper['year'] else 'Unknown Year'})")
298
+ st.markdown(f"**Summary:** {paper['summary']}")
299
+
300
+ # Button to generate and play audio
301
+ if st.button(f"๐Ÿ”Š Read Aloud - {paper['title']}", key=f"read_aloud_{idx}"):
302
+ md_file, audio_file = save_paper_files(paper, voice)
303
+ if audio_file:
304
+ st.success("Audio generated successfully!")
305
+ play_and_download_audio(audio_file)
306
+ else:
307
+ st.error("Failed to generate audio.")
308
+
309
+ st.write("---")
310
+
311
+ def cache_parsed_papers(papers):
312
+ """Cache the parsed papers."""
313
+ st.session_state['parsed_papers'] = papers
314
 
315
+ def get_cached_papers():
316
+ """Retrieve cached papers."""
317
+ return st.session_state.get('parsed_papers', [])
318
+
319
+ def save_full_transcript(query, text):
320
+ """Save full transcript of Arxiv results as a file."""
321
+ filename = generate_filename("transcript", query, "md")
322
+ with open(filename, 'w', encoding='utf-8') as f:
323
+ f.write(text)
324
+ return filename
325
 
326
  def perform_ai_lookup(q, vocal_summary=True, extended_refs=False,
327
  titles_summary=True, full_audio=False, selected_voice="en-US-AriaNeural"):
 
330
 
331
  # ๐ŸŽฏ 1) Query the HF RAG pipeline
332
  client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
333
+ refs = client.predict(q, 20, "Semantic Search", "mistralai/Mixtral-8x7B-Instruct-v0.1", api_name="/update_with_rag_md")[0]
334
+ r2 = client.predict(q, "mistralai/Mixtral-8x7B-Instruct-v0.1", True, api_name="/ask_llm")
335
+
336
  # ๐ŸŽฏ 2) Combine for final text output
337
  clean_q = q.replace('\n', ' ')
338
  result = f"### ๐Ÿ”Ž {clean_q}\n\n{r2}\n\n{refs}"
339
  st.markdown(result)
340
+
341
+ # ๐ŸŽฏ 3) Parse papers from the references
342
+ parsed_papers = parse_papers(refs)
343
+ cache_parsed_papers(parsed_papers)
344
+
345
+ # ๐ŸŽฏ 4) Display all parsed papers with options
346
+ st.write("## Individual Papers")
347
+ display_papers(parsed_papers, selected_voice)
348
+
349
+ elapsed = time.time() - start
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  st.write(f"**Total Elapsed:** {elapsed:.2f} s")
351
+
352
  # Always create a file with the result
353
+ save_full_transcript(clean_q, result)
354
+
355
  return result
356
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  # ๐Ÿ“‚ 10. File Management
358
  def create_zip_of_files(md_files, mp3_files):
359
+ """Create zip with intelligent naming."""
360
  md_files = [f for f in md_files if os.path.basename(f).lower() != 'readme.md']
361
  all_files = md_files + mp3_files
362
  if not all_files:
 
370
  all_content.append(file.read())
371
  elif f.endswith('.mp3'):
372
  all_content.append(os.path.basename(f))
373
+
374
  combined_content = " ".join(all_content)
375
  info_terms = get_high_info_terms(combined_content)
376
+
377
  timestamp = datetime.now().strftime("%y%m_%H%M")
378
  name_text = '_'.join(term.replace(' ', '-') for term in info_terms[:3])
379
  zip_name = f"{timestamp}_{name_text}.zip"
380
+
381
  with zipfile.ZipFile(zip_name,'w') as z:
382
  for f in all_files:
383
  z.write(f)
384
+
385
  return zip_name
386
 
387
  def load_files_for_sidebar():
388
+ """Load and group files for sidebar display."""
389
  md_files = glob.glob("*.md")
390
  mp3_files = glob.glob("*.mp3")
391
 
 
407
  return groups, sorted_prefixes
408
 
409
  def extract_keywords_from_md(files):
410
+ """Extract keywords from markdown files."""
411
  text = ""
412
  for f in files:
413
  if f.endswith(".md"):
 
416
  return get_high_info_terms(text)
417
 
418
  def display_file_manager_sidebar(groups, sorted_prefixes):
419
+ """Display file manager in sidebar."""
420
  st.sidebar.title("๐ŸŽต Audio & Docs Manager")
421
 
422
  all_md = []
 
443
  if st.button("โฌ‡๏ธ ZipAll"):
444
  z = create_zip_of_files(all_md, all_mp3)
445
  if z:
446
+ st.sidebar.markdown(get_download_link(z), unsafe_allow_html=True)
447
 
448
  for prefix in sorted_prefixes:
449
  files = groups[prefix]
450
  kw = extract_keywords_from_md(files)
451
  keywords_str = " ".join(kw) if kw else "No Keywords"
452
  with st.sidebar.expander(f"{prefix} Files ({len(files)}) - KW: {keywords_str}", expanded=True):
453
+ c1, c2 = st.columns(2)
454
  with c1:
455
+ if st.button("๐Ÿ‘€ View Group", key="view_group_"+prefix):
456
  st.session_state.viewing_prefix = prefix
457
  with c2:
458
+ if st.button("๐Ÿ—‘ Delete Group", key="del_group_"+prefix):
459
  for f in files:
460
  os.remove(f)
461
  st.success(f"Deleted group {prefix}!")
 
477
 
478
  def main():
479
  st.sidebar.markdown("### ๐ŸšฒBikeAI๐Ÿ† Multi-Agent Research")
480
+ tab_main = st.radio("Action:", ["๐ŸŽค Voice", "๐Ÿ“ธ Media", "๐Ÿ” ArXiv", "๐Ÿ“ Editor"], horizontal=True)
 
 
 
481
 
482
+ # Initialize voices if not already done
483
+ if not st.session_state['voices']:
484
  st.session_state['voices'] = fetch_voices()
485
 
486
  st.sidebar.markdown("### ๐ŸŽค Select Voice for Audio Generation")
 
490
  index=st.session_state['voices'].index("en-US-AriaNeural") if "en-US-AriaNeural" in st.session_state['voices'] else 0
491
  )
492
 
493
+ # Main Tabs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  if tab_main == "๐Ÿ” ArXiv":
495
  st.subheader("๐Ÿ” Query ArXiv")
496
  q = st.text_input("๐Ÿ” Query:").replace('\n', ' ')
497
 
498
  st.markdown("### ๐ŸŽ› Options")
499
+ vocal_summary = st.checkbox("๐ŸŽ™ Short Audio", value=True)
500
+ extended_refs = st.checkbox("๐Ÿ“œ Long References", value=False)
501
+ titles_summary = st.checkbox("๐Ÿ”– Titles Only", value=True)
502
+ full_audio = st.checkbox("๐Ÿ“š Full Audio", value=False, help="Generate full audio response")
503
+ full_transcript = st.checkbox("๐Ÿงพ Full Transcript", value=False, help="Generate a full transcript file")
504
+
505
+ if q and st.button("๐Ÿ” Run"):
506
+ result = perform_ai_lookup(
507
+ q,
508
+ vocal_summary=vocal_summary,
509
+ extended_refs=extended_refs,
510
+ titles_summary=titles_summary,
511
+ full_audio=full_audio,
512
+ selected_voice=selected_voice
513
+ )
514
  if full_transcript:
515
  save_full_transcript(q, result)
516
 
517
  st.markdown("### Change Prompt & Re-Run")
518
  q_new = st.text_input("๐Ÿ”„ Modify Query:").replace('\n', ' ')
519
  if q_new and st.button("๐Ÿ”„ Re-Run with Modified Query"):
520
+ result = perform_ai_lookup(
521
+ q_new,
522
+ vocal_summary=vocal_summary,
523
+ extended_refs=extended_refs,
524
+ titles_summary=titles_summary,
525
+ full_audio=full_audio,
526
+ selected_voice=selected_voice
527
+ )
528
  if full_transcript:
529
  save_full_transcript(q_new, result)
530
 
 
535
  if st.button("๐Ÿ“จ Send"):
536
  process_with_gpt(user_text)
537
  st.subheader("๐Ÿ“œ Chat History")
538
+ t1, t2 = st.tabs(["Claude History", "GPT-4o History"])
539
  with t1:
540
+ for c in st.session_state.get('chat_history', []):
541
  st.write("**You:**", c["user"])
542
  st.write("**Claude:**", c["claude"])
543
  with t2:
544
+ for m in st.session_state.get('messages', []):
545
  with st.chat_message(m["role"]):
546
  st.markdown(m["content"])
547
 
 
549
  st.header("๐Ÿ“ธ Images & ๐ŸŽฅ Videos")
550
  tabs = st.tabs(["๐Ÿ–ผ Images", "๐ŸŽฅ Video"])
551
  with tabs[0]:
552
+ imgs = glob.glob("*.png") + glob.glob("*.jpg")
553
  if imgs:
554
+ cols = st.columns(min(5, len(imgs)))
555
+ for i, f in enumerate(imgs[:20]):
556
+ with cols[i % len(cols)]:
557
+ st.image(Image.open(f), use_container_width=True)
 
558
  if st.button(f"๐Ÿ‘€ Analyze {os.path.basename(f)}", key=f"analyze_{f}"):
559
+ analysis = process_image(f, "Describe this image.")
560
+ st.markdown(analysis)
561
  else:
562
  st.write("No images found.")
563
+
564
  with tabs[1]:
565
  vids = glob.glob("*.mp4")[:20]
566
  if vids:
 
568
  with st.expander(f"๐ŸŽฅ {os.path.basename(v)}"):
569
  st.video(v)
570
  if st.button(f"Analyze {os.path.basename(v)}", key=f"analyze_{v}"):
571
+ analysis = process_video_with_gpt(v, "Describe video.")
572
+ st.markdown(analysis)
573
  else:
574
  st.write("No videos found.")
575
 
576
  elif tab_main == "๐Ÿ“ Editor":
577
+ st.subheader("๐Ÿ“ Editor")
578
+ files = glob.glob("*.md")
579
+ if files:
580
+ selected_file = st.selectbox("Select a file to edit:", files)
581
+ if selected_file:
582
+ with open(selected_file, 'r', encoding='utf-8') as f:
583
+ file_content = f.read()
584
+ new_text = st.text_area("โœ๏ธ Content:", file_content, height=300)
585
+ if st.button("๐Ÿ’พ Save"):
586
+ with open(selected_file, 'w', encoding='utf-8') as f:
587
+ f.write(new_text)
588
+ st.success("File updated successfully!")
589
+ st.session_state.should_rerun = True
590
  else:
591
+ st.write("No Markdown files available for editing.")
592
 
593
+ # File Manager Sidebar
594
  groups, sorted_prefixes = load_files_for_sidebar()
595
  display_file_manager_sidebar(groups, sorted_prefixes)
596
 
 
602
  ext = os.path.splitext(fname)[1].lower().strip('.')
603
  st.write(f"### {fname}")
604
  if ext == "md":
605
+ with open(f, 'r', encoding='utf-8') as file:
606
+ content = file.read()
607
  st.markdown(content)
608
  elif ext == "mp3":
609
  st.audio(f)
 
614
 
615
  if st.session_state.should_rerun:
616
  st.session_state.should_rerun = False
617
+ st.experimental_rerun()
618
 
619
+ def process_with_gpt(text):
620
+ """Process text with GPT-4."""
621
+ if not text:
622
+ return
623
+ # Initialize messages if not present
624
+ if 'messages' not in st.session_state:
625
+ st.session_state['messages'] = []
626
+ st.session_state['messages'].append({"role":"user","content":text})
627
+ with st.chat_message("user"):
628
+ st.markdown(text)
629
+ with st.chat_message("assistant"):
630
+ try:
631
+ response = openai.ChatCompletion.create(
632
+ model=st.session_state["openai_model"],
633
+ messages=st.session_state['messages'],
634
+ stream=False
635
+ )
636
+ ans = response.choices[0].message.content
637
+ st.write("GPT-4o: " + ans)
638
+ create_md_file({"title": "User Query", "year": None, "summary": ans})
639
+ st.session_state['messages'].append({"role":"assistant","content":ans})
640
+ except Exception as e:
641
+ st.error(f"Error processing with GPT-4: {e}")
642
+
643
+ def process_with_claude(text):
644
+ """Process text with Claude."""
645
+ if not text:
646
+ return
647
+ # Initialize chat_history if not present
648
+ if 'chat_history' not in st.session_state:
649
+ st.session_state['chat_history'] = []
650
+ with st.chat_message("user"):
651
+ st.markdown(text)
652
+ with st.chat_message("assistant"):
653
+ try:
654
+ response = claude_client.messages.create(
655
+ model="claude-3-sonnet-20240229",
656
+ max_tokens=1000,
657
+ messages=[{"role":"user","content":text}]
658
+ )
659
+ ans = response.content[0].text
660
+ st.write("Claude-3.5: " + ans)
661
+ create_md_file({"title": "User Query", "year": None, "summary": ans})
662
+ st.session_state['chat_history'].append({"user":text,"claude":ans})
663
+ except Exception as e:
664
+ st.error(f"Error processing with Claude: {e}")
665
+
666
+ # Run the application
667
  if __name__=="__main__":
668
  main()