awacke1 commited on
Commit
9fc0f69
·
verified ·
1 Parent(s): 322d94b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +453 -333
app.py CHANGED
@@ -20,89 +20,66 @@ from streamlit.runtime.scriptrunner import get_script_run_ctx
20
  import asyncio
21
  import edge_tts
22
 
23
- # Available English voices
24
- ENGLISH_VOICES = [
25
- "en-US-AriaNeural", # Female, conversational
26
- "en-US-JennyNeural", # Female, customer service
27
- "en-US-GuyNeural", # Male, newscast
28
- "en-US-RogerNeural", # Male, calm
29
- "en-GB-SoniaNeural", # British female
30
- "en-GB-RyanNeural", # British male
31
- "en-AU-NatashaNeural", # Australian female
32
- "en-AU-WilliamNeural", # Australian male
33
- "en-CA-ClaraNeural", # Canadian female
34
- "en-CA-LiamNeural", # Canadian male
35
- "en-IE-EmilyNeural", # Irish female
36
- "en-IE-ConnorNeural", # Irish male
37
- "en-IN-NeerjaNeural", # Indian female
38
- "en-IN-PrabhatNeural", # Indian male
39
- ]
40
-
41
- # Core Configuration & Setup
42
  st.set_page_config(
43
- page_title="ARIA Research Assistant",
44
- page_icon="🔬",
45
  layout="wide",
46
  initial_sidebar_state="auto",
47
  menu_items={
48
  'Get Help': 'https://huggingface.co/awacke1',
49
  'Report a bug': 'https://huggingface.co/spaces/awacke1',
50
- 'About': "ARIA: Academic Research Interactive Assistant"
51
  }
52
  )
53
  load_dotenv()
54
 
55
- # API Setup
56
- openai_api_key = os.getenv('OPENAI_API_KEY', st.secrets.get('OPENAI_API_KEY', ''))
57
- anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', st.secrets.get('ANTHROPIC_API_KEY', ''))
 
 
 
 
 
58
 
59
- openai_client = OpenAI(api_key=openai_api_key)
60
  claude_client = anthropic.Anthropic(api_key=anthropic_key)
 
 
 
61
 
62
- # Session State Management
63
  if 'transcript_history' not in st.session_state:
64
  st.session_state['transcript_history'] = []
65
  if 'chat_history' not in st.session_state:
66
  st.session_state['chat_history'] = []
67
  if 'openai_model' not in st.session_state:
68
- st.session_state['openai_model'] = "gpt-4-vision-preview"
69
  if 'messages' not in st.session_state:
70
  st.session_state['messages'] = []
71
  if 'last_voice_input' not in st.session_state:
72
  st.session_state['last_voice_input'] = ""
73
- if 'current_audio' not in st.session_state:
74
- st.session_state['current_audio'] = None
75
- if 'autoplay_audio' not in st.session_state:
76
- st.session_state['autoplay_audio'] = True
 
 
 
 
77
  if 'should_rerun' not in st.session_state:
78
  st.session_state['should_rerun'] = False
79
- if 'autorun' not in st.session_state:
80
- st.session_state.autorun = True
81
- if 'run_option' not in st.session_state:
82
- st.session_state.run_option = "Arxiv"
83
- if 'last_processed_text' not in st.session_state:
84
- st.session_state.last_processed_text = ""
85
-
86
- # Custom CSS
87
  st.markdown("""
88
  <style>
89
- .main {
90
- background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
91
- color: #ffffff;
92
- }
93
- .stMarkdown {
94
- font-family: 'Helvetica Neue', sans-serif;
95
- }
96
  .stButton>button {
97
- background-color: #4CAF50;
98
- color: white;
99
- padding: 0.5rem 1rem;
100
- border-radius: 5px;
101
- border: none;
102
- transition: background-color 0.3s;
103
- }
104
- .stButton>button:hover {
105
- background-color: #45a049;
106
  }
107
  .audio-player {
108
  margin: 1rem 0;
@@ -111,166 +88,267 @@ st.markdown("""
111
  background: #f5f5f5;
112
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
113
  }
114
- .voice-container {
115
- padding: 1rem;
116
- background: white;
117
- border-radius: 10px;
118
- margin: 1rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
120
- .text-display {
121
- margin: 1rem 0;
122
- padding: 1rem;
123
- background: #f9f9f9;
124
- border-radius: 5px;
125
- font-size: 1.1em;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  }
127
- .model-selector {
128
  margin: 1rem 0;
129
- padding: 0.5rem;
130
- background: #ffffff;
131
- border-radius: 5px;
132
- }
133
- .response-container {
134
- margin-top: 2rem;
135
  padding: 1rem;
136
- background: rgba(255, 255, 255, 0.05);
137
  border-radius: 10px;
 
 
138
  }
139
  </style>
140
  """, unsafe_allow_html=True)
141
 
142
- def create_voice_component():
143
- """Create auto-searching voice recognition component"""
144
- return components.html(
145
- """
146
- <div style="padding: 20px; border-radius: 10px; background: #f0f2f6;">
147
- <div id="status" style="margin-bottom: 10px; color: #666;">Starting voice recognition...</div>
148
- <div id="interim" style="color: #666; min-height: 24px;"></div>
149
- <div id="output" style="margin-top: 10px; padding: 10px; min-height: 100px;
150
- background: white; border-radius: 5px; white-space: pre-wrap;"></div>
151
- <script>
152
- if ('webkitSpeechRecognition' in window) {
153
- const recognition = new webkitSpeechRecognition();
154
- recognition.continuous = true;
155
- recognition.interimResults = true;
156
-
157
- const status = document.getElementById('status');
158
- const interim = document.getElementById('interim');
159
- const output = document.getElementById('output');
160
- let fullTranscript = '';
161
- let lastPauseTime = Date.now();
162
- let pauseThreshold = 1500;
163
-
164
- window.addEventListener('load', () => {
165
- setTimeout(() => {
166
- try {
167
- recognition.start();
168
- status.textContent = 'Listening...';
169
- } catch (e) {
170
- console.error('Start error:', e);
171
- status.textContent = 'Error starting recognition';
172
- }
173
- }, 1000);
174
- });
175
-
176
- recognition.onresult = (event) => {
177
- let interimTranscript = '';
178
- let finalTranscript = '';
179
-
180
- for (let i = event.resultIndex; i < event.results.length; i++) {
181
- const transcript = event.results[i][0].transcript;
182
- if (event.results[i].isFinal) {
183
- finalTranscript += transcript + ' ';
184
- lastPauseTime = Date.now();
185
- } else {
186
- interimTranscript += transcript;
187
- }
188
- }
189
-
190
- if (finalTranscript) {
191
- fullTranscript += finalTranscript;
192
- interim.textContent = '';
193
- output.textContent = fullTranscript;
194
-
195
- window.parent.postMessage({
196
- type: 'streamlit:setComponentValue',
197
- value: {
198
- text: fullTranscript,
199
- trigger: 'speech'
200
- },
201
- dataType: 'json',
202
- }, '*');
203
- } else if (interimTranscript) {
204
- interim.textContent = '... ' + interimTranscript;
205
- }
206
-
207
- output.scrollTop = output.scrollHeight;
208
- };
209
-
210
- setInterval(() => {
211
- if (fullTranscript && Date.now() - lastPauseTime > pauseThreshold) {
212
- if (output.dataset.lastProcessed !== fullTranscript) {
213
- output.dataset.lastProcessed = fullTranscript;
214
- window.parent.postMessage({
215
- type: 'streamlit:setComponentValue',
216
- value: {
217
- text: fullTranscript,
218
- trigger: 'pause'
219
- },
220
- dataType: 'json',
221
- }, '*');
222
- }
223
- }
224
- }, 500);
225
-
226
- recognition.onend = () => {
227
- try {
228
- recognition.start();
229
- status.textContent = 'Listening...';
230
- } catch (e) {
231
- console.error('Restart error:', e);
232
- status.textContent = 'Recognition stopped. Refresh to restart.';
233
- }
234
- };
235
-
236
- recognition.onerror = (event) => {
237
- console.error('Recognition error:', event.error);
238
- status.textContent = 'Error: ' + event.error;
239
- };
240
- } else {
241
- document.getElementById('status').textContent = 'Speech recognition not supported in this browser';
242
- }
243
- </script>
244
- </div>
245
- """,
246
- height=200
247
- )
248
-
249
- def get_audio_autoplay_html(audio_path):
250
- """Create HTML for autoplaying audio with controls and download"""
251
- try:
252
- with open(audio_path, "rb") as audio_file:
253
- audio_bytes = audio_file.read()
254
- audio_b64 = base64.b64encode(audio_bytes).decode()
255
- return f'''
256
- <div class="audio-player">
257
- <audio controls autoplay style="width: 100%;">
258
- <source src="data:audio/mpeg;base64,{audio_b64}" type="audio/mpeg">
259
- Your browser does not support the audio element.
260
- </audio>
261
- <div style="margin-top: 5px;">
262
- <a href="data:audio/mpeg;base64,{audio_b64}"
263
- download="{os.path.basename(audio_path)}"
264
- style="text-decoration: none; color: #4CAF50;">
265
- ⬇️ Download Audio
266
- </a>
267
- </div>
268
- </div>
269
- '''
270
- except Exception as e:
271
- return f"Error loading audio: {str(e)}"
272
-
273
- # Audio Processing Functions
274
  def clean_for_speech(text: str) -> str:
275
  """Clean text for speech synthesis"""
276
  text = text.replace("\n", " ")
@@ -280,151 +358,176 @@ def clean_for_speech(text: str) -> str:
280
  text = re.sub(r"\s+", " ", text).strip()
281
  return text
282
 
283
- async def generate_audio(text, voice="en-US-AriaNeural", rate="+0%", pitch="+0Hz"):
284
- """Generate audio using Edge TTS with automatic playback"""
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  text = clean_for_speech(text)
286
  if not text.strip():
287
  return None
288
-
289
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
290
- output_file = f"response_{timestamp}.mp3"
291
-
292
- communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch)
293
- await communicate.save(output_file)
294
-
295
- return output_file
296
-
297
- def render_audio_result(audio_file, title="Generated Audio"):
298
- """Render audio result with autoplay in Streamlit"""
299
- if audio_file and os.path.exists(audio_file):
300
- st.markdown(f"### {title}")
301
- st.markdown(get_audio_autoplay_html(audio_file), unsafe_allow_html=True)
302
-
303
- async def process_voice_search(query, voice="en-US-AriaNeural"):
304
- """Process voice search with automatic audio using selected voice"""
305
- response, refs = perform_arxiv_search(query)
306
-
307
- audio_file = await generate_audio(response, voice=voice)
308
- st.session_state.current_audio = audio_file
309
-
310
- return response, audio_file
311
-
312
- # Arxiv Search Functions
313
- def perform_arxiv_search(query):
314
- """Enhanced Arxiv search with summary"""
315
- client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
316
-
317
- refs = client.predict(
318
- query, 20, "Semantic Search",
319
- "mistralai/Mixtral-8x7B-Instruct-v0.1",
320
- api_name="/update_with_rag_md"
321
- )[0]
322
-
323
- summary = client.predict(
324
- query,
325
- "mistralai/Mixtral-8x7B-Instruct-v0.1",
326
- True,
327
- api_name="/ask_llm"
328
- )
329
-
330
- response = f"### Search Results for: {query}\n\n{summary}\n\n### References\n\n{refs}"
331
- return response, refs
332
-
333
- def perform_ai_lookup(q, vocal_summary=True, extended_refs=False, titles_summary=True,
334
- full_audio=False, voice="en-US-AriaNeural"):
335
- """Full Arxiv search with audio summaries"""
336
  start = time.time()
337
- response, refs = perform_arxiv_search(q)
338
-
339
- st.markdown(response)
340
-
341
- # Generate audio responses
 
 
 
 
342
  if full_audio:
343
- audio_file = asyncio.run(generate_audio(response, voice=voice))
344
- if audio_file:
345
- render_audio_result(audio_file, "Complete Response")
346
-
 
347
  if vocal_summary:
348
- summary_audio = asyncio.run(generate_audio(
349
- f"Summary of results for query: {q}",
350
- voice=voice
351
- ))
352
- if summary_audio:
353
- render_audio_result(summary_audio, "Summary")
354
-
355
- elapsed = time.time() - start
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  st.write(f"**Total Elapsed:** {elapsed:.2f} s")
357
-
358
- return response
359
- def render_search_interface():
360
- """Main search interface with voice recognition and model selection"""
361
- st.header("🔍 Voice Search & Research")
362
 
363
- # Get voice component value and set up model selection
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  mycomponent = components.declare_component("mycomponent", path="mycomponent")
365
  val = mycomponent(my_input_value="Hello")
366
 
367
- # Show input in edit box if detected
368
  if val:
369
  val_stripped = val.replace('\n', ' ')
370
  edited_input = st.text_area("✏️ Edit Input:", value=val_stripped, height=100)
371
  run_option = st.selectbox("Model:", ["Arxiv", "GPT-4o", "Claude-3.5"])
372
-
373
  col1, col2 = st.columns(2)
374
  with col1:
375
  autorun = st.checkbox("⚙ AutoRun", value=True)
376
  with col2:
377
- full_audio = st.checkbox("📚FullAudio", value=False,
378
- help="Generate full audio response")
379
-
380
- input_changed = (val != st.session_state.get('old_val', None))
381
-
382
- if autorun and input_changed:
383
- st.session_state.old_val = val
384
- if run_option == "Arxiv":
385
- perform_ai_lookup(edited_input, vocal_summary=True, extended_refs=False,
386
- titles_summary=True, full_audio=full_audio)
387
- else:
388
- if run_option == "GPT-4o":
389
- process_with_gpt(edited_input)
390
- elif run_option == "Claude-3.5":
391
- process_with_claude(edited_input)
392
- else:
393
- if st.button("▶ Run"):
394
- st.session_state.old_val = val
395
- if run_option == "Arxiv":
396
- perform_ai_lookup(edited_input, vocal_summary=True, extended_refs=False,
397
- titles_summary=True, full_audio=full_audio)
398
- else:
399
- if run_option == "GPT-4o":
400
- process_with_gpt(edited_input)
401
- elif run_option == "Claude-3.5":
402
- process_with_claude(edited_input)
403
-
404
-
405
- def main():
406
- st.sidebar.markdown("### 🚲BikeAI🏆 Multi-Agent Research")
407
- tab_main = st.radio("Action:", ["🎤 Voice", "📸 Media", "🔍 ArXiv", "📝 Editor"], horizontal=True)
408
-
409
- if tab_main == "🎤 Voice":
410
- render_search_interface()
411
-
412
- elif tab_main == "🔍 ArXiv":
413
- st.subheader("🔍 Query ArXiv")
414
- q = st.text_input("🔍 Query:")
415
-
416
- st.markdown("### 🎛 Options")
417
- vocal_summary = st.checkbox("🎙ShortAudio", value=True)
418
- extended_refs = st.checkbox("📜LongRefs", value=False)
419
- titles_summary = st.checkbox("🔖TitlesOnly", value=True)
420
- full_audio = st.checkbox("📚FullAudio", value=False,
421
- help="Full audio of results")
422
  full_transcript = st.checkbox("🧾FullTranscript", value=False,
423
- help="Generate a full transcript file")
424
 
425
  if q and st.button("🔍Run"):
426
  result = perform_ai_lookup(q, vocal_summary=vocal_summary, extended_refs=extended_refs,
427
- titles_summary=titles_summary, full_audio=full_audio)
428
  if full_transcript:
429
  save_full_transcript(q, result)
430
 
@@ -432,14 +535,30 @@ def main():
432
  q_new = st.text_input("🔄 Modify Query:")
433
  if q_new and st.button("🔄 Re-Run with Modified Query"):
434
  result = perform_ai_lookup(q_new, vocal_summary=vocal_summary, extended_refs=extended_refs,
435
- titles_summary=titles_summary, full_audio=full_audio)
436
  if full_transcript:
437
  save_full_transcript(q_new, result)
438
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  elif tab_main == "📸 Media":
440
  st.header("📸 Images & 🎥 Videos")
441
  tabs = st.tabs(["🖼 Images", "🎥 Video"])
442
-
443
  with tabs[0]:
444
  imgs = glob.glob("*.png")+glob.glob("*.jpg")
445
  if imgs:
@@ -453,7 +572,6 @@ def main():
453
  st.markdown(a)
454
  else:
455
  st.write("No images found.")
456
-
457
  with tabs[1]:
458
  vids = glob.glob("*.mp4")
459
  if vids:
@@ -501,4 +619,6 @@ def main():
501
  if st.session_state.should_rerun:
502
  st.session_state.should_rerun = False
503
  st.rerun()
504
-
 
 
 
20
  import asyncio
21
  import edge_tts
22
 
23
+ # 🎯 1. Core Configuration & Setup
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  st.set_page_config(
25
+ page_title="🚲BikeAI🏆 Claude/GPT Research",
26
+ page_icon="🚲🏆",
27
  layout="wide",
28
  initial_sidebar_state="auto",
29
  menu_items={
30
  'Get Help': 'https://huggingface.co/awacke1',
31
  'Report a bug': 'https://huggingface.co/spaces/awacke1',
32
+ 'About': "🚲BikeAI🏆 Claude/GPT Research AI"
33
  }
34
  )
35
  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:
44
+ anthropic_key = st.secrets["ANTHROPIC_API_KEY"]
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
+
76
+ # 🎨 4. Custom CSS
 
 
 
 
77
  st.markdown("""
78
  <style>
79
+ .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
80
+ .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
 
 
 
 
 
81
  .stButton>button {
82
+ margin-right: 0.5rem;
 
 
 
 
 
 
 
 
83
  }
84
  .audio-player {
85
  margin: 1rem 0;
 
88
  background: #f5f5f5;
89
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
90
  }
91
+ </style>
92
+ """, unsafe_allow_html=True)
93
+
94
+ FILE_EMOJIS = {
95
+ "md": "📝",
96
+ "mp3": "🎵",
97
+ }
98
+
99
+ def clean_for_speech(text: str) -> str:
100
+ """Clean text for speech synthesis"""
101
+ text = text.replace("\n", " ")
102
+ text = text.replace("</s>", " ")
103
+ text = text.replace("#", "")
104
+ text = re.sub(r"\(https?:\/\/[^\)]+\)", "", text)
105
+ text = re.sub(r"\s+", " ", text).strip()
106
+ return text
107
+
108
+ @st.cache_resource
109
+ def speech_synthesis_html(result):
110
+ """Create HTML for speech synthesis"""
111
+ html_code = f"""
112
+ <html><body>
113
+ <script>
114
+ var msg = new SpeechSynthesisUtterance("{result.replace('"', '')}");
115
+ window.speechSynthesis.speak(msg);
116
+ </script>
117
+ </body></html>
118
+ """
119
+ components.html(html_code, height=0)
120
+
121
+ async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
122
+ """Generate audio using Edge TTS"""
123
+ text = clean_for_speech(text)
124
+ if not text.strip():
125
+ return None
126
+ rate_str = f"{rate:+d}%"
127
+ pitch_str = f"{pitch:+d}Hz"
128
+ communicate = edge_tts.Communicate(text, voice, rate=rate_str, pitch=pitch_str)
129
+ out_fn = generate_filename(text, text, "mp3")
130
+ await communicate.save(out_fn)
131
+ return out_fn
132
+
133
+ def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0):
134
+ """Wrapper for edge TTS generation"""
135
+ return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch))
136
+
137
+ def play_and_download_audio(file_path):
138
+ """Play and provide download link for audio"""
139
+ if file_path and os.path.exists(file_path):
140
+ st.audio(file_path)
141
+ 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>'
142
+ st.markdown(dl_link, unsafe_allow_html=True)
143
+
144
+ def save_full_transcript(query, text):
145
+ """Save full transcript of Arxiv results as a file."""
146
+ create_file(query, text, "md")
147
+
148
+ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False, titles_summary=True, full_audio=False):
149
+ """Perform Arxiv search and generate audio summaries"""
150
+ start = time.time()
151
+ client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
152
+ refs = client.predict(q,20,"Semantic Search","mistralai/Mixtral-8x7B-Instruct-v0.1",api_name="/update_with_rag_md")[0]
153
+ r2 = client.predict(q,"mistralai/Mixtral-8x7B-Instruct-v0.1",True,api_name="/ask_llm")
154
+
155
+ result = f"### 🔎 {q}\n\n{r2}\n\n{refs}"
156
+
157
+ st.markdown(result)
158
+
159
+ # Generate full audio version if requested
160
+ if full_audio:
161
+ complete_text = f"Complete response for query: {q}. {clean_for_speech(r2)} {clean_for_speech(refs)}"
162
+ audio_file_full = speak_with_edge_tts(complete_text)
163
+ st.write("### 📚 Full Audio")
164
+ play_and_download_audio(audio_file_full)
165
+
166
+ if vocal_summary:
167
+ main_text = clean_for_speech(r2)
168
+ audio_file_main = speak_with_edge_tts(main_text)
169
+ st.write("### 🎙 Short Audio")
170
+ play_and_download_audio(audio_file_main)
171
+
172
+ if extended_refs:
173
+ summaries_text = "Extended references: " + refs.replace('"','')
174
+ summaries_text = clean_for_speech(summaries_text)
175
+ audio_file_refs = speak_with_edge_tts(summaries_text)
176
+ st.write("### 📜 Long Refs")
177
+ play_and_download_audio(audio_file_refs)
178
+
179
+ if titles_summary:
180
+ titles = []
181
+ for line in refs.split('\n'):
182
+ m = re.search(r"\[([^\]]+)\]", line)
183
+ if m:
184
+ titles.append(m.group(1))
185
+ if titles:
186
+ titles_text = "Titles: " + ", ".join(titles)
187
+ titles_text = clean_for_speech(titles_text)
188
+ audio_file_titles = speak_with_edge_tts(titles_text)
189
+ st.write("### 🔖 Titles")
190
+ play_and_download_audio(audio_file_titles)
191
+
192
+ elapsed = time.time()-start
193
+ st.write(f"**Total Elapsed:** {elapsed:.2f} s")
194
+
195
+ # Create file with result
196
+ create_file(q, result, "md")
197
+
198
+ return result
199
+
200
+ def process_with_gpt(text):
201
+ """Process text with GPT-4"""
202
+ if not text: return
203
+ st.session_state.messages.append({"role":"user","content":text})
204
+ with st.chat_message("user"):
205
+ st.markdown(text)
206
+ with st.chat_message("assistant"):
207
+ c = openai_client.chat.completions.create(
208
+ model=st.session_state["openai_model"],
209
+ messages=st.session_state.messages,
210
+ stream=False
211
+ )
212
+ ans = c.choices[0].message.content
213
+ st.write("GPT-4o: " + ans)
214
+ create_file(text, ans, "md")
215
+ st.session_state.messages.append({"role":"assistant","content":ans})
216
+ return ans
217
+
218
+ def process_with_claude(text):
219
+ """Process text with Claude"""
220
+ if not text: return
221
+ with st.chat_message("user"):
222
+ st.markdown(text)
223
+ with st.chat_message("assistant"):
224
+ r = claude_client.messages.create(
225
+ model="claude-3-sonnet-20240229",
226
+ max_tokens=1000,
227
+ messages=[{"role":"user","content":text}]
228
+ )
229
+ ans = r.content[0].text
230
+ st.write("Claude-3.5: " + ans)
231
+ create_file(text, ans, "md")
232
+ st.session_state.chat_history.append({"user":text,"claude":ans})
233
+ return ans
234
+
235
+ def generate_filename(prompt, response, file_type="md"):
236
+ """Generate filename with timestamp and cleaned text."""
237
+ timestamp = datetime.now().strftime("%y%m_%H%M")
238
+ safe_text = re.sub(r'[^\w\s-]', '', prompt[:50])
239
+ return f"{timestamp}_{safe_text}.{file_type}"
240
+
241
+ def create_file(prompt, response, file_type="md"):
242
+ """Create file with content."""
243
+ filename = generate_filename(prompt.strip(), response.strip(), file_type)
244
+ with open(filename, 'w', encoding='utf-8') as f:
245
+ f.write(prompt + "\n\n" + response)
246
+ return filename
247
+
248
+ def get_download_link(file):
249
+ """Generate download link for file"""
250
+ with open(file, "rb") as f:
251
+ b64 = base64.b64encode(f.read()).decode()
252
+ return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">📂 Download {os.path.basename(file)}</a>'
253
+
254
+ import streamlit as st
255
+ import anthropic, openai, base64, cv2, glob, json, math, os, pytz, random, re, requests, time, zipfile
256
+ import plotly.graph_objects as go
257
+ import streamlit.components.v1 as components
258
+ from datetime import datetime
259
+ from audio_recorder_streamlit import audio_recorder
260
+ from bs4 import BeautifulSoup
261
+ from collections import defaultdict, deque
262
+ from dotenv import load_dotenv
263
+ from gradio_client import Client
264
+ from huggingface_hub import InferenceClient
265
+ from io import BytesIO
266
+ from PIL import Image
267
+ from PyPDF2 import PdfReader
268
+ from urllib.parse import quote
269
+ from xml.etree import ElementTree as ET
270
+ from openai import OpenAI
271
+ import extra_streamlit_components as stx
272
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
273
+ import asyncio
274
+ import edge_tts
275
+
276
+ # 🎯 1. Core Configuration & Setup
277
+ st.set_page_config(
278
+ page_title="🚲BikeAI🏆 Claude/GPT Research",
279
+ page_icon="🚲🏆",
280
+ layout="wide",
281
+ initial_sidebar_state="auto",
282
+ menu_items={
283
+ 'Get Help': 'https://huggingface.co/awacke1',
284
+ 'Report a bug': 'https://huggingface.co/spaces/awacke1',
285
+ 'About': "🚲BikeAI🏆 Claude/GPT Research AI"
286
  }
287
+ )
288
+ load_dotenv()
289
+
290
+ # 🔑 2. API Setup & Clients
291
+ openai_api_key = os.getenv('OPENAI_API_KEY', "")
292
+ anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', "")
293
+ xai_key = os.getenv('xai',"")
294
+ if 'OPENAI_API_KEY' in st.secrets:
295
+ openai_api_key = st.secrets['OPENAI_API_KEY']
296
+ if 'ANTHROPIC_API_KEY' in st.secrets:
297
+ anthropic_key = st.secrets["ANTHROPIC_API_KEY"]
298
+
299
+ openai.api_key = openai_api_key
300
+ claude_client = anthropic.Anthropic(api_key=anthropic_key)
301
+ openai_client = OpenAI(api_key=openai.api_key, organization=os.getenv('OPENAI_ORG_ID'))
302
+ HF_KEY = os.getenv('HF_KEY')
303
+ API_URL = os.getenv('API_URL')
304
+
305
+ # 📝 3. Session State Management
306
+ if 'transcript_history' not in st.session_state:
307
+ st.session_state['transcript_history'] = []
308
+ if 'chat_history' not in st.session_state:
309
+ st.session_state['chat_history'] = []
310
+ if 'openai_model' not in st.session_state:
311
+ st.session_state['openai_model'] = "gpt-4o-2024-05-13"
312
+ if 'messages' not in st.session_state:
313
+ st.session_state['messages'] = []
314
+ if 'last_voice_input' not in st.session_state:
315
+ st.session_state['last_voice_input'] = ""
316
+ if 'editing_file' not in st.session_state:
317
+ st.session_state['editing_file'] = None
318
+ if 'edit_new_name' not in st.session_state:
319
+ st.session_state['edit_new_name'] = ""
320
+ if 'edit_new_content' not in st.session_state:
321
+ st.session_state['edit_new_content'] = ""
322
+ if 'viewing_prefix' not in st.session_state:
323
+ st.session_state['viewing_prefix'] = None
324
+ if 'should_rerun' not in st.session_state:
325
+ st.session_state['should_rerun'] = False
326
+ if 'old_val' not in st.session_state:
327
+ st.session_state['old_val'] = None
328
+
329
+ # 🎨 4. Custom CSS
330
+ st.markdown("""
331
+ <style>
332
+ .main { background: linear-gradient(to right, #1a1a1a, #2d2d2d); color: #fff; }
333
+ .stMarkdown { font-family: 'Helvetica Neue', sans-serif; }
334
+ .stButton>button {
335
+ margin-right: 0.5rem;
336
  }
337
+ .audio-player {
338
  margin: 1rem 0;
 
 
 
 
 
 
339
  padding: 1rem;
 
340
  border-radius: 10px;
341
+ background: #f5f5f5;
342
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
343
  }
344
  </style>
345
  """, unsafe_allow_html=True)
346
 
347
+ FILE_EMOJIS = {
348
+ "md": "📝",
349
+ "mp3": "🎵",
350
+ }
351
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  def clean_for_speech(text: str) -> str:
353
  """Clean text for speech synthesis"""
354
  text = text.replace("\n", " ")
 
358
  text = re.sub(r"\s+", " ", text).strip()
359
  return text
360
 
361
+ @st.cache_resource
362
+ def speech_synthesis_html(result):
363
+ """Create HTML for speech synthesis"""
364
+ html_code = f"""
365
+ <html><body>
366
+ <script>
367
+ var msg = new SpeechSynthesisUtterance("{result.replace('"', '')}");
368
+ window.speechSynthesis.speak(msg);
369
+ </script>
370
+ </body></html>
371
+ """
372
+ components.html(html_code, height=0)
373
+
374
+ async def edge_tts_generate_audio(text, voice="en-US-AriaNeural", rate=0, pitch=0):
375
+ """Generate audio using Edge TTS"""
376
  text = clean_for_speech(text)
377
  if not text.strip():
378
  return None
379
+ rate_str = f"{rate:+d}%"
380
+ pitch_str = f"{pitch:+d}Hz"
381
+ communicate = edge_tts.Communicate(text, voice, rate=rate_str, pitch=pitch_str)
382
+ out_fn = generate_filename(text, text, "mp3")
383
+ await communicate.save(out_fn)
384
+ return out_fn
385
+
386
+ def speak_with_edge_tts(text, voice="en-US-AriaNeural", rate=0, pitch=0):
387
+ """Wrapper for edge TTS generation"""
388
+ return asyncio.run(edge_tts_generate_audio(text, voice, rate, pitch))
389
+
390
+ def play_and_download_audio(file_path):
391
+ """Play and provide download link for audio"""
392
+ if file_path and os.path.exists(file_path):
393
+ st.audio(file_path)
394
+ 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>'
395
+ st.markdown(dl_link, unsafe_allow_html=True)
396
+
397
+ def save_full_transcript(query, text):
398
+ """Save full transcript of Arxiv results as a file."""
399
+ create_file(query, text, "md")
400
+
401
+ def perform_ai_lookup(q, vocal_summary=True, extended_refs=False, titles_summary=True, full_audio=False):
402
+ """Perform Arxiv search and generate audio summaries"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  start = time.time()
404
+ client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
405
+ refs = client.predict(q,20,"Semantic Search","mistralai/Mixtral-8x7B-Instruct-v0.1",api_name="/update_with_rag_md")[0]
406
+ r2 = client.predict(q,"mistralai/Mixtral-8x7B-Instruct-v0.1",True,api_name="/ask_llm")
407
+
408
+ result = f"### 🔎 {q}\n\n{r2}\n\n{refs}"
409
+
410
+ st.markdown(result)
411
+
412
+ # Generate full audio version if requested
413
  if full_audio:
414
+ complete_text = f"Complete response for query: {q}. {clean_for_speech(r2)} {clean_for_speech(refs)}"
415
+ audio_file_full = speak_with_edge_tts(complete_text)
416
+ st.write("### 📚 Full Audio")
417
+ play_and_download_audio(audio_file_full)
418
+
419
  if vocal_summary:
420
+ main_text = clean_for_speech(r2)
421
+ audio_file_main = speak_with_edge_tts(main_text)
422
+ st.write("### 🎙 Short Audio")
423
+ play_and_download_audio(audio_file_main)
424
+
425
+ if extended_refs:
426
+ summaries_text = "Extended references: " + refs.replace('"','')
427
+ summaries_text = clean_for_speech(summaries_text)
428
+ audio_file_refs = speak_with_edge_tts(summaries_text)
429
+ st.write("### 📜 Long Refs")
430
+ play_and_download_audio(audio_file_refs)
431
+
432
+ if titles_summary:
433
+ titles = []
434
+ for line in refs.split('\n'):
435
+ m = re.search(r"\[([^\]]+)\]", line)
436
+ if m:
437
+ titles.append(m.group(1))
438
+ if titles:
439
+ titles_text = "Titles: " + ", ".join(titles)
440
+ titles_text = clean_for_speech(titles_text)
441
+ audio_file_titles = speak_with_edge_tts(titles_text)
442
+ st.write("### 🔖 Titles")
443
+ play_and_download_audio(audio_file_titles)
444
+
445
+ elapsed = time.time()-start
446
  st.write(f"**Total Elapsed:** {elapsed:.2f} s")
 
 
 
 
 
447
 
448
+ # Create file with result
449
+ create_file(q, result, "md")
450
+
451
+ return result
452
+
453
+ def process_with_gpt(text):
454
+ """Process text with GPT-4"""
455
+ if not text: return
456
+ st.session_state.messages.append({"role":"user","content":text})
457
+ with st.chat_message("user"):
458
+ st.markdown(text)
459
+ with st.chat_message("assistant"):
460
+ c = openai_client.chat.completions.create(
461
+ model=st.session_state["openai_model"],
462
+ messages=st.session_state.messages,
463
+ stream=False
464
+ )
465
+ ans = c.choices[0].message.content
466
+ st.write("GPT-4o: " + ans)
467
+ create_file(text, ans, "md")
468
+ st.session_state.messages.append({"role":"assistant","content":ans})
469
+ return ans
470
+
471
+ def process_with_claude(text):
472
+ """Process text with Claude"""
473
+ if not text: return
474
+ with st.chat_message("user"):
475
+ st.markdown(text)
476
+ with st.chat_message("assistant"):
477
+ r = claude_client.messages.create(
478
+ model="claude-3-sonnet-20240229",
479
+ max_tokens=1000,
480
+ messages=[{"role":"user","content":text}]
481
+ )
482
+ ans = r.content[0].text
483
+ st.write("Claude-3.5: " + ans)
484
+ create_file(text, ans, "md")
485
+ st.session_state.chat_history.append({"user":text,"claude":ans})
486
+ return ans
487
+
488
+ def generate_filename(prompt, response, file_type="md"):
489
+ """Generate filename with timestamp and cleaned text."""
490
+ timestamp = datetime.now().strftime("%y%m_%H%M")
491
+ safe_text = re.sub(r'[^\w\s-]', '', prompt[:50])
492
+ return f"{timestamp}_{safe_text}.{file_type}"
493
+
494
+ def create_file(prompt, response, file_type="md"):
495
+ """Create file with content."""
496
+ filename = generate_filename(prompt.strip(), response.strip(), file_type)
497
+ with open(filename, 'w', encoding='utf-8') as f:
498
+ f.write(prompt + "\n\n" + response)
499
+ return filename
500
+
501
+ def get_download_link(file):
502
+ """Generate download link for file"""
503
+ with open(file, "rb") as f:
504
+ b64 = base64.b64encode(f.read()).decode()
505
+ return f'<a href="data:file/zip;base64,{b64}" download="{os.path.basename(file)}">📂 Download {os.path.basename(file)}</a>'
506
+
507
+ def main():
508
+ st.sidebar.markdown("### 🚲BikeAI🏆 Multi-Agent Research")
509
+ tab_main = st.radio("Action:",["🎤 Voice","📸 Media","🔍 ArXiv","📝 Editor"],horizontal=True)
510
+
511
  mycomponent = components.declare_component("mycomponent", path="mycomponent")
512
  val = mycomponent(my_input_value="Hello")
513
 
514
+ # Show input in text box for editing if detected
515
  if val:
516
  val_stripped = val.replace('\n', ' ')
517
  edited_input = st.text_area("✏️ Edit Input:", value=val_stripped, height=100)
518
  run_option = st.selectbox("Model:", ["Arxiv", "GPT-4o", "Claude-3.5"])
 
519
  col1, col2 = st.columns(2)
520
  with col1:
521
  autorun = st.checkbox("⚙ AutoRun", value=True)
522
  with col2:
523
+ full_audio = st.checkbox("📚FullAudio", value=False,
524
+ help="Full audio of results")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  full_transcript = st.checkbox("🧾FullTranscript", value=False,
526
+ help="Generate a full transcript file")
527
 
528
  if q and st.button("🔍Run"):
529
  result = perform_ai_lookup(q, vocal_summary=vocal_summary, extended_refs=extended_refs,
530
+ titles_summary=titles_summary, full_audio=full_audio)
531
  if full_transcript:
532
  save_full_transcript(q, result)
533
 
 
535
  q_new = st.text_input("🔄 Modify Query:")
536
  if q_new and st.button("🔄 Re-Run with Modified Query"):
537
  result = perform_ai_lookup(q_new, vocal_summary=vocal_summary, extended_refs=extended_refs,
538
+ titles_summary=titles_summary, full_audio=full_audio)
539
  if full_transcript:
540
  save_full_transcript(q_new, result)
541
 
542
+ elif tab_main == "🎤 Voice":
543
+ st.subheader("🎤 Voice Input")
544
+ user_text = st.text_area("💬 Message:", height=100)
545
+ user_text = user_text.strip().replace('\n', ' ')
546
+ if st.button("📨 Send"):
547
+ process_with_gpt(user_text)
548
+ st.subheader("📜 Chat History")
549
+ t1,t2=st.tabs(["Claude History","GPT-4o History"])
550
+ with t1:
551
+ for c in st.session_state.chat_history:
552
+ st.write("**You:**", c["user"])
553
+ st.write("**Claude:**", c["claude"])
554
+ with t2:
555
+ for m in st.session_state.messages:
556
+ with st.chat_message(m["role"]):
557
+ st.markdown(m["content"])
558
+
559
  elif tab_main == "📸 Media":
560
  st.header("📸 Images & 🎥 Videos")
561
  tabs = st.tabs(["🖼 Images", "🎥 Video"])
 
562
  with tabs[0]:
563
  imgs = glob.glob("*.png")+glob.glob("*.jpg")
564
  if imgs:
 
572
  st.markdown(a)
573
  else:
574
  st.write("No images found.")
 
575
  with tabs[1]:
576
  vids = glob.glob("*.mp4")
577
  if vids:
 
619
  if st.session_state.should_rerun:
620
  st.session_state.should_rerun = False
621
  st.rerun()
622
+
623
+ if __name__ == "__main__":
624
+ main()