awacke1 commited on
Commit
e05ce15
β€’
1 Parent(s): b0840f9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1061 -0
app.py ADDED
@@ -0,0 +1,1061 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import anthropic
3
+ import openai
4
+ import base64
5
+ from datetime import datetime
6
+ import plotly.graph_objects as go
7
+ import cv2
8
+ import glob
9
+ import json
10
+ import math
11
+ import os
12
+ import pytz
13
+ import random
14
+ import re
15
+ import requests
16
+ import streamlit.components.v1 as components
17
+ import textract
18
+ import time
19
+ import zipfile
20
+ from audio_recorder_streamlit import audio_recorder
21
+ from bs4 import BeautifulSoup
22
+ from collections import deque
23
+ from dotenv import load_dotenv
24
+ from gradio_client import Client
25
+ from huggingface_hub import InferenceClient
26
+ from io import BytesIO
27
+ from PIL import Image
28
+ from PyPDF2 import PdfReader
29
+ from urllib.parse import quote
30
+ from xml.etree import ElementTree as ET
31
+ from openai import OpenAI
32
+ import extra_streamlit_components as stx
33
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
34
+
35
+
36
+ # 1. 🚲BikeAIπŸ† Configuration and Setup
37
+ Site_Name = '🚲BikeAIπŸ† Claude and GPT Multi-Agent Research AI'
38
+ title = "🚲BikeAIπŸ† Claude and GPT Multi-Agent Research AI"
39
+ helpURL = 'https://huggingface.co/awacke1'
40
+ bugURL = 'https://huggingface.co/spaces/awacke1'
41
+ icons = 'πŸš²πŸ†'
42
+
43
+ st.set_page_config(
44
+ page_title=title,
45
+ page_icon=icons,
46
+ layout="wide",
47
+ initial_sidebar_state="auto",
48
+ menu_items={
49
+ 'Get Help': helpURL,
50
+ 'Report a bug': bugURL,
51
+ 'About': title
52
+ }
53
+ )
54
+
55
+ # 2. 🚲BikeAIπŸ† Load environment variables and initialize clients
56
+ load_dotenv()
57
+
58
+ # OpenAI setup
59
+ openai.api_key = os.getenv('OPENAI_API_KEY')
60
+ if openai.api_key == None:
61
+ openai.api_key = st.secrets['OPENAI_API_KEY']
62
+
63
+ openai_client = OpenAI(
64
+ api_key=os.getenv('OPENAI_API_KEY'),
65
+ organization=os.getenv('OPENAI_ORG_ID')
66
+ )
67
+
68
+ # 3.🚲BikeAIπŸ† Claude setup
69
+ anthropic_key = os.getenv("ANTHROPIC_API_KEY_3")
70
+ if anthropic_key == None:
71
+ anthropic_key = st.secrets["ANTHROPIC_API_KEY"]
72
+ claude_client = anthropic.Anthropic(api_key=anthropic_key)
73
+
74
+ # 4.🚲BikeAIπŸ† Initialize session states
75
+ if 'transcript_history' not in st.session_state:
76
+ st.session_state.transcript_history = []
77
+ if "chat_history" not in st.session_state:
78
+ st.session_state.chat_history = []
79
+ if "openai_model" not in st.session_state:
80
+ st.session_state["openai_model"] = "gpt-4o-2024-05-13"
81
+ if "messages" not in st.session_state:
82
+ st.session_state.messages = []
83
+ if 'last_voice_input' not in st.session_state:
84
+ st.session_state.last_voice_input = ""
85
+
86
+ # 5. 🚲BikeAIπŸ† HuggingFace AI setup
87
+ API_URL = os.getenv('API_URL')
88
+ HF_KEY = os.getenv('HF_KEY')
89
+ MODEL1 = "meta-llama/Llama-2-7b-chat-hf"
90
+ MODEL2 = "openai/whisper-small.en"
91
+ headers = {
92
+ "Authorization": f"Bearer {HF_KEY}",
93
+ "Content-Type": "application/json"
94
+ }
95
+
96
+ # 6. 🚲BikeAIπŸ† Custom CSS
97
+ st.markdown("""
98
+ <style>
99
+ .main {
100
+ background: linear-gradient(to right, #1a1a1a, #2d2d2d);
101
+ color: #ffffff;
102
+ }
103
+ .stMarkdown {
104
+ font-family: 'Helvetica Neue', sans-serif;
105
+ }
106
+ .category-header {
107
+ background: linear-gradient(45deg, #2b5876, #4e4376);
108
+ padding: 20px;
109
+ border-radius: 10px;
110
+ margin: 10px 0;
111
+ }
112
+ .scene-card {
113
+ background: rgba(0,0,0,0.3);
114
+ padding: 15px;
115
+ border-radius: 8px;
116
+ margin: 10px 0;
117
+ border: 1px solid rgba(255,255,255,0.1);
118
+ }
119
+ .media-gallery {
120
+ display: grid;
121
+ gap: 1rem;
122
+ padding: 1rem;
123
+ }
124
+ .bike-card {
125
+ background: rgba(255,255,255,0.05);
126
+ border-radius: 10px;
127
+ padding: 15px;
128
+ transition: transform 0.3s;
129
+ }
130
+ .bike-card:hover {
131
+ transform: scale(1.02);
132
+ }
133
+ </style>
134
+ """, unsafe_allow_html=True)
135
+
136
+
137
+ # 7. Helper Functions
138
+ def generate_filename(prompt, file_type):
139
+ """Generate a safe filename using the prompt and file type."""
140
+ central = pytz.timezone('US/Central')
141
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
142
+ replaced_prompt = re.sub(r'[<>:"/\\|?*\n]', ' ', prompt)
143
+ safe_prompt = re.sub(r'\s+', ' ', replaced_prompt).strip()[:230]
144
+ return f"{safe_date_time}_{safe_prompt}.{file_type}"
145
+
146
+
147
+
148
+
149
+ # 8. Function to create and save a file (and avoid the black hole of lost data πŸ•³)
150
+ def create_file(filename, prompt, response, should_save=True):
151
+ if not should_save:
152
+ return
153
+ with open(filename, 'w', encoding='utf-8') as file:
154
+ file.write(prompt + "\n\n" + response)
155
+ def create_and_save_file(content, file_type="md", prompt=None, is_image=False, should_save=True):
156
+ """Create and save file with proper handling of different types."""
157
+ if not should_save:
158
+ return None
159
+ filename = generate_filename(prompt if prompt else content, file_type)
160
+ with open(filename, "w", encoding="utf-8") as f:
161
+ if is_image:
162
+ f.write(content)
163
+ else:
164
+ f.write(prompt + "\n\n" + content if prompt else content)
165
+ return filename
166
+
167
+
168
+ def get_download_link(file_path):
169
+ """Create download link for file."""
170
+ with open(file_path, "rb") as file:
171
+ contents = file.read()
172
+ b64 = base64.b64encode(contents).decode()
173
+ return f'<a href="data:file/txt;base64,{b64}" download="{os.path.basename(file_path)}">Download {os.path.basename(file_path)}πŸ“‚</a>'
174
+
175
+ @st.cache_resource
176
+ def SpeechSynthesis(result):
177
+ """HTML5 Speech Synthesis."""
178
+ documentHTML5 = f'''
179
+ <!DOCTYPE html>
180
+ <html>
181
+ <head>
182
+ <title>Read It Aloud</title>
183
+ <script type="text/javascript">
184
+ function readAloud() {{
185
+ const text = document.getElementById("textArea").value;
186
+ const speech = new SpeechSynthesisUtterance(text);
187
+ window.speechSynthesis.speak(speech);
188
+ }}
189
+ </script>
190
+ </head>
191
+ <body>
192
+ <h1>πŸ”Š Read It Aloud</h1>
193
+ <textarea id="textArea" rows="10" cols="80">{result}</textarea>
194
+ <br>
195
+ <button onclick="readAloud()">πŸ”Š Read Aloud</button>
196
+ </body>
197
+ </html>
198
+ '''
199
+ components.html(documentHTML5, width=1280, height=300)
200
+
201
+ # Media Processing Functions
202
+ def process_image(image_input, user_prompt):
203
+ """Process image with GPT-4o vision."""
204
+ if isinstance(image_input, str):
205
+ with open(image_input, "rb") as image_file:
206
+ image_input = image_file.read()
207
+
208
+ base64_image = base64.b64encode(image_input).decode("utf-8")
209
+
210
+ response = openai_client.chat.completions.create(
211
+ model=st.session_state["openai_model"],
212
+ messages=[
213
+ {"role": "system", "content": "You are a helpful assistant that responds in Markdown."},
214
+ {"role": "user", "content": [
215
+ {"type": "text", "text": user_prompt},
216
+ {"type": "image_url", "image_url": {
217
+ "url": f"data:image/png;base64,{base64_image}"
218
+ }}
219
+ ]}
220
+ ],
221
+ temperature=0.0,
222
+ )
223
+
224
+ return response.choices[0].message.content
225
+
226
+ def process_audio(audio_input, text_input=''):
227
+ """Process audio with Whisper and GPT."""
228
+ if isinstance(audio_input, str):
229
+ with open(audio_input, "rb") as file:
230
+ audio_input = file.read()
231
+
232
+ transcription = openai_client.audio.transcriptions.create(
233
+ model="whisper-1",
234
+ file=audio_input,
235
+ )
236
+
237
+ st.session_state.messages.append({"role": "user", "content": transcription.text})
238
+
239
+ with st.chat_message("assistant"):
240
+ st.markdown(transcription.text)
241
+ SpeechSynthesis(transcription.text)
242
+
243
+ filename = generate_filename(transcription.text, "wav")
244
+ create_and_save_file(audio_input, "wav", transcription.text, True)
245
+
246
+ # Modified video processing function without moviepy dependency
247
+ def process_video(video_path, seconds_per_frame=1):
248
+ """Process video files for frame extraction."""
249
+ base64Frames = []
250
+ video = cv2.VideoCapture(video_path)
251
+ total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
252
+ fps = video.get(cv2.CAP_PROP_FPS)
253
+ frames_to_skip = int(fps * seconds_per_frame)
254
+
255
+ for frame_idx in range(0, total_frames, frames_to_skip):
256
+ video.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
257
+ success, frame = video.read()
258
+ if not success:
259
+ break
260
+ _, buffer = cv2.imencode(".jpg", frame)
261
+ base64Frames.append(base64.b64encode(buffer).decode("utf-8"))
262
+
263
+ video.release()
264
+ return base64Frames, None
265
+
266
+ def process_video_with_gpt(video_input, user_prompt):
267
+ """Process video with GPT-4 vision."""
268
+ base64Frames, _ = process_video(video_input)
269
+
270
+ response = openai_client.chat.completions.create(
271
+ model=st.session_state["openai_model"],
272
+ messages=[
273
+ {"role": "system", "content": "Analyze the video frames and provide a detailed description."},
274
+ {"role": "user", "content": [
275
+ {"type": "text", "text": user_prompt},
276
+ *[{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{frame}"}}
277
+ for frame in base64Frames]
278
+ ]}
279
+ ]
280
+ )
281
+ return response.choices[0].message.content
282
+
283
+
284
+ def extract_urls(text):
285
+ try:
286
+ date_pattern = re.compile(r'### (\d{2} \w{3} \d{4})')
287
+ abs_link_pattern = re.compile(r'\[(.*?)\]\((https://arxiv\.org/abs/\d+\.\d+)\)')
288
+ pdf_link_pattern = re.compile(r'\[⬇️\]\((https://arxiv\.org/pdf/\d+\.\d+)\)')
289
+ title_pattern = re.compile(r'### \d{2} \w{3} \d{4} \| \[(.*?)\]')
290
+ date_matches = date_pattern.findall(text)
291
+ abs_link_matches = abs_link_pattern.findall(text)
292
+ pdf_link_matches = pdf_link_pattern.findall(text)
293
+ title_matches = title_pattern.findall(text)
294
+
295
+ # markdown with the extracted fields
296
+ markdown_text = ""
297
+ for i in range(len(date_matches)):
298
+ date = date_matches[i]
299
+ title = title_matches[i]
300
+ abs_link = abs_link_matches[i][1]
301
+ pdf_link = pdf_link_matches[i]
302
+ markdown_text += f"**Date:** {date}\n\n"
303
+ markdown_text += f"**Title:** {title}\n\n"
304
+ markdown_text += f"**Abstract Link:** [{abs_link}]({abs_link})\n\n"
305
+ markdown_text += f"**PDF Link:** [{pdf_link}]({pdf_link})\n\n"
306
+ markdown_text += "---\n\n"
307
+ return markdown_text
308
+
309
+ except:
310
+ st.write('.')
311
+ return ''
312
+
313
+
314
+ def search_arxiv(query):
315
+
316
+ st.write("Performing AI Lookup...")
317
+ client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
318
+
319
+ result1 = client.predict(
320
+ prompt=query,
321
+ llm_model_picked="mistralai/Mixtral-8x7B-Instruct-v0.1",
322
+ stream_outputs=True,
323
+ api_name="/ask_llm"
324
+ )
325
+ st.markdown("### Mixtral-8x7B-Instruct-v0.1 Result")
326
+ st.markdown(result1)
327
+
328
+ result2 = client.predict(
329
+ prompt=query,
330
+ llm_model_picked="mistralai/Mistral-7B-Instruct-v0.2",
331
+ stream_outputs=True,
332
+ api_name="/ask_llm"
333
+ )
334
+ st.markdown("### Mistral-7B-Instruct-v0.2 Result")
335
+ st.markdown(result2)
336
+ combined_result = f"{result1}\n\n{result2}"
337
+ return combined_result
338
+
339
+ #return responseall
340
+
341
+
342
+ # Function to generate a filename based on prompt and time (because names matter πŸ•’)
343
+ def generate_filename(prompt, file_type):
344
+ central = pytz.timezone('US/Central')
345
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
346
+ safe_prompt = re.sub(r'\W+', '_', prompt)[:90]
347
+ return f"{safe_date_time}_{safe_prompt}.{file_type}"
348
+
349
+ # Function to create and save a file (and avoid the black hole of lost data πŸ•³)
350
+ def create_file(filename, prompt, response):
351
+ with open(filename, 'w', encoding='utf-8') as file:
352
+ file.write(prompt + "\n\n" + response)
353
+
354
+
355
+ def perform_ai_lookup(query):
356
+ start_time = time.strftime("%Y-%m-%d %H:%M:%S")
357
+ client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
358
+ response1 = client.predict(
359
+ query,
360
+ 20,
361
+ "Semantic Search",
362
+ "mistralai/Mixtral-8x7B-Instruct-v0.1",
363
+ api_name="/update_with_rag_md"
364
+ )
365
+ Question = '### πŸ”Ž ' + query + '\r\n' # Format for markdown display with links
366
+ References = response1[0]
367
+ ReferenceLinks = extract_urls(References)
368
+
369
+ RunSecondQuery = True
370
+ results=''
371
+ if RunSecondQuery:
372
+ # Search 2 - Retrieve the Summary with Papers Context and Original Query
373
+ response2 = client.predict(
374
+ query,
375
+ "mistralai/Mixtral-8x7B-Instruct-v0.1",
376
+ True,
377
+ api_name="/ask_llm"
378
+ )
379
+ if len(response2) > 10:
380
+ Answer = response2
381
+ SpeechSynthesis(Answer)
382
+ # Restructure results to follow format of Question, Answer, References, ReferenceLinks
383
+ results = Question + '\r\n' + Answer + '\r\n' + References + '\r\n' + ReferenceLinks
384
+ st.markdown(results)
385
+
386
+ st.write('πŸ”Run of Multi-Agent System Paper Summary Spec is Complete')
387
+ end_time = time.strftime("%Y-%m-%d %H:%M:%S")
388
+ start_timestamp = time.mktime(time.strptime(start_time, "%Y-%m-%d %H:%M:%S"))
389
+ end_timestamp = time.mktime(time.strptime(end_time, "%Y-%m-%d %H:%M:%S"))
390
+ elapsed_seconds = end_timestamp - start_timestamp
391
+ st.write(f"Start time: {start_time}")
392
+ st.write(f"Finish time: {end_time}")
393
+ st.write(f"Elapsed time: {elapsed_seconds:.2f} seconds")
394
+
395
+
396
+ filename = generate_filename(query, "md")
397
+ create_file(filename, query, results)
398
+ return results
399
+
400
+ # Chat Processing Functions
401
+ def process_with_gpt(text_input):
402
+ """Process text with GPT-4o."""
403
+ if text_input:
404
+ st.session_state.messages.append({"role": "user", "content": text_input})
405
+
406
+ with st.chat_message("user"):
407
+ st.markdown(text_input)
408
+
409
+ with st.chat_message("assistant"):
410
+ completion = openai_client.chat.completions.create(
411
+ model=st.session_state["openai_model"],
412
+ messages=[
413
+ {"role": m["role"], "content": m["content"]}
414
+ for m in st.session_state.messages
415
+ ],
416
+ stream=False
417
+ )
418
+ return_text = completion.choices[0].message.content
419
+ st.write("GPT-4o: " + return_text)
420
+
421
+ #filename = generate_filename(text_input, "md")
422
+ filename = generate_filename("GPT-4o: " + return_text, "md")
423
+ create_file(filename, text_input, return_text)
424
+ st.session_state.messages.append({"role": "assistant", "content": return_text})
425
+ return return_text
426
+
427
+ def process_with_claude(text_input):
428
+ """Process text with Claude."""
429
+ if text_input:
430
+
431
+ with st.chat_message("user"):
432
+ st.markdown(text_input)
433
+
434
+ with st.chat_message("assistant"):
435
+ response = claude_client.messages.create(
436
+ model="claude-3-sonnet-20240229",
437
+ max_tokens=1000,
438
+ messages=[
439
+ {"role": "user", "content": text_input}
440
+ ]
441
+ )
442
+ response_text = response.content[0].text
443
+ st.write("Claude: " + response_text)
444
+
445
+ #filename = generate_filename(text_input, "md")
446
+ filename = generate_filename("Claude: " + response_text, "md")
447
+ create_file(filename, text_input, response_text)
448
+
449
+ st.session_state.chat_history.append({
450
+ "user": text_input,
451
+ "claude": response_text
452
+ })
453
+ return response_text
454
+
455
+ # File Management Functions
456
+ def load_file(file_name):
457
+ """Load file content."""
458
+ with open(file_name, "r", encoding='utf-8') as file:
459
+ content = file.read()
460
+ return content
461
+
462
+ def create_zip_of_files(files):
463
+ """Create zip archive of files."""
464
+ zip_name = "all_files.zip"
465
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
466
+ for file in files:
467
+ zipf.write(file)
468
+ return zip_name
469
+
470
+
471
+
472
+ def get_media_html(media_path, media_type="video", width="100%"):
473
+ """Generate HTML for media player."""
474
+ media_data = base64.b64encode(open(media_path, 'rb').read()).decode()
475
+ if media_type == "video":
476
+ return f'''
477
+ <video width="{width}" controls autoplay muted loop>
478
+ <source src="data:video/mp4;base64,{media_data}" type="video/mp4">
479
+ Your browser does not support the video tag.
480
+ </video>
481
+ '''
482
+ else: # audio
483
+ return f'''
484
+ <audio controls style="width: {width};">
485
+ <source src="data:audio/mpeg;base64,{media_data}" type="audio/mpeg">
486
+ Your browser does not support the audio element.
487
+ </audio>
488
+ '''
489
+
490
+ def create_media_gallery():
491
+ """Create the media gallery interface."""
492
+ st.header("🎬 Media Gallery")
493
+
494
+ tabs = st.tabs(["πŸ–ΌοΈ Images", "🎡 Audio", "πŸŽ₯ Video"])
495
+
496
+ with tabs[0]:
497
+ image_files = glob.glob("*.png") + glob.glob("*.jpg")
498
+ if image_files:
499
+ num_cols = st.slider("Number of columns", 1, 5, 3)
500
+ cols = st.columns(num_cols)
501
+ for idx, image_file in enumerate(image_files):
502
+ with cols[idx % num_cols]:
503
+ img = Image.open(image_file)
504
+ st.image(img, use_container_width=True)
505
+
506
+ # Add GPT vision analysis option
507
+ if st.button(f"Analyze {os.path.basename(image_file)}"):
508
+ analysis = process_image(image_file,
509
+ "Describe this image in detail and identify key elements.")
510
+ st.markdown(analysis)
511
+
512
+ with tabs[1]:
513
+ audio_files = glob.glob("*.mp3") + glob.glob("*.wav")
514
+ for audio_file in audio_files:
515
+ with st.expander(f"🎡 {os.path.basename(audio_file)}"):
516
+ st.markdown(get_media_html(audio_file, "audio"), unsafe_allow_html=True)
517
+ if st.button(f"Transcribe {os.path.basename(audio_file)}"):
518
+ with open(audio_file, "rb") as f:
519
+ transcription = process_audio(f)
520
+ st.write(transcription)
521
+
522
+ with tabs[2]:
523
+ video_files = glob.glob("*.mp4")
524
+ for video_file in video_files:
525
+ with st.expander(f"πŸŽ₯ {os.path.basename(video_file)}"):
526
+ st.markdown(get_media_html(video_file, "video"), unsafe_allow_html=True)
527
+ if st.button(f"Analyze {os.path.basename(video_file)}"):
528
+ analysis = process_video_with_gpt(video_file,
529
+ "Describe what's happening in this video.")
530
+ st.markdown(analysis)
531
+
532
+
533
+
534
+ def display_file_manager():
535
+ """Display file management sidebar with guaranteed unique button keys."""
536
+ st.sidebar.title("πŸ“ File Management")
537
+
538
+ all_files = glob.glob("*.md")
539
+ all_files.sort(reverse=True)
540
+
541
+ if st.sidebar.button("πŸ—‘ Delete All", key="delete_all_files_button"):
542
+ for file in all_files:
543
+ os.remove(file)
544
+ st.rerun()
545
+
546
+ if st.sidebar.button("⬇️ Download All", key="download_all_files_button"):
547
+ zip_file = create_zip_of_files(all_files)
548
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
549
+
550
+ # Create unique keys using file attributes
551
+ for idx, file in enumerate(all_files):
552
+ # Get file stats for unique identification
553
+ file_stat = os.stat(file)
554
+ unique_id = f"{idx}_{file_stat.st_size}_{file_stat.st_mtime}"
555
+
556
+ col1, col2, col3, col4 = st.sidebar.columns([1,3,1,1])
557
+ with col1:
558
+ if st.button("🌐", key=f"view_{unique_id}"):
559
+ st.session_state.current_file = file
560
+ st.session_state.file_content = load_file(file)
561
+ with col2:
562
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
563
+ with col3:
564
+ if st.button("πŸ“‚", key=f"edit_{unique_id}"):
565
+ st.session_state.current_file = file
566
+ st.session_state.file_content = load_file(file)
567
+ with col4:
568
+ if st.button("πŸ—‘", key=f"delete_{unique_id}"):
569
+ os.remove(file)
570
+ st.rerun()
571
+
572
+
573
+
574
+
575
+ # Speech Recognition HTML Component
576
+ speech_recognition_html = """
577
+ <!DOCTYPE html>
578
+ <html>
579
+ <head>
580
+ <title>Continuous Speech Demo</title>
581
+ <style>
582
+ body {
583
+ font-family: sans-serif;
584
+ padding: 20px;
585
+ max-width: 800px;
586
+ margin: 0 auto;
587
+ }
588
+ button {
589
+ padding: 10px 20px;
590
+ margin: 10px 5px;
591
+ font-size: 16px;
592
+ }
593
+ #status {
594
+ margin: 10px 0;
595
+ padding: 10px;
596
+ background: #e8f5e9;
597
+ border-radius: 4px;
598
+ }
599
+ #output {
600
+ white-space: pre-wrap;
601
+ padding: 15px;
602
+ background: #f5f5f5;
603
+ border-radius: 4px;
604
+ margin: 10px 0;
605
+ min-height: 100px;
606
+ max-height: 400px;
607
+ overflow-y: auto;
608
+ }
609
+ .controls {
610
+ margin: 10px 0;
611
+ }
612
+ </style>
613
+ </head>
614
+ <body>
615
+ <div class="controls">
616
+ <button id="start">Start Listening</button>
617
+ <button id="stop" disabled>Stop Listening</button>
618
+ <button id="clear">Clear Text</button>
619
+ </div>
620
+ <div id="status">Ready</div>
621
+ <div id="output"></div>
622
+
623
+ <!-- Add the hidden input here -->
624
+ <input type="hidden" id="streamlit-data" value="">
625
+
626
+ <script>
627
+ if (!('webkitSpeechRecognition' in window)) {
628
+ alert('Speech recognition not supported');
629
+ } else {
630
+ const recognition = new webkitSpeechRecognition();
631
+ const startButton = document.getElementById('start');
632
+ const stopButton = document.getElementById('stop');
633
+ const clearButton = document.getElementById('clear');
634
+ const status = document.getElementById('status');
635
+ const output = document.getElementById('output');
636
+ let fullTranscript = '';
637
+ let lastUpdateTime = Date.now();
638
+
639
+ // Configure recognition
640
+ recognition.continuous = true;
641
+ recognition.interimResults = true;
642
+
643
+ // Function to start recognition
644
+ const startRecognition = () => {
645
+ try {
646
+ recognition.start();
647
+ status.textContent = 'Listening...';
648
+ startButton.disabled = true;
649
+ stopButton.disabled = false;
650
+ } catch (e) {
651
+ console.error(e);
652
+ status.textContent = 'Error: ' + e.message;
653
+ }
654
+ };
655
+
656
+ // Auto-start on load
657
+ window.addEventListener('load', () => {
658
+ setTimeout(startRecognition, 1000);
659
+ });
660
+
661
+ startButton.onclick = startRecognition;
662
+
663
+ stopButton.onclick = () => {
664
+ recognition.stop();
665
+ status.textContent = 'Stopped';
666
+ startButton.disabled = false;
667
+ stopButton.disabled = true;
668
+ };
669
+
670
+ clearButton.onclick = () => {
671
+ fullTranscript = '';
672
+ output.textContent = '';
673
+ window.parent.postMessage({
674
+ type: 'clear_transcript',
675
+ }, '*');
676
+ };
677
+
678
+ recognition.onresult = (event) => {
679
+ let interimTranscript = '';
680
+ let finalTranscript = '';
681
+
682
+ for (let i = event.resultIndex; i < event.results.length; i++) {
683
+ const transcript = event.results[i][0].transcript;
684
+ if (event.results[i].isFinal) {
685
+ finalTranscript += transcript + '\\n';
686
+ } else {
687
+ interimTranscript += transcript;
688
+ }
689
+ }
690
+
691
+ if (finalTranscript || (Date.now() - lastUpdateTime > 5000)) {
692
+ if (finalTranscript) {
693
+ fullTranscript += finalTranscript;
694
+
695
+ // Update the hidden input value
696
+ document.getElementById('streamlit-data').value = fullTranscript;
697
+ }
698
+ lastUpdateTime = Date.now();
699
+ }
700
+
701
+ output.textContent = fullTranscript + (interimTranscript ? '... ' + interimTranscript : '');
702
+ output.scrollTop = output.scrollHeight;
703
+
704
+ document.getElementById('streamlit-data').value = fullTranscript;
705
+
706
+ };
707
+
708
+ recognition.onend = () => {
709
+ if (!stopButton.disabled) {
710
+ try {
711
+ recognition.start();
712
+ console.log('Restarted recognition');
713
+ } catch (e) {
714
+ console.error('Failed to restart recognition:', e);
715
+ status.textContent = 'Error restarting: ' + e.message;
716
+ startButton.disabled = false;
717
+ stopButton.disabled = true;
718
+ }
719
+ }
720
+ };
721
+
722
+ recognition.onerror = (event) => {
723
+ console.error('Recognition error:', event.error);
724
+ status.textContent = 'Error: ' + event.error;
725
+
726
+ if (event.error === 'not-allowed' || event.error === 'service-not-allowed') {
727
+ startButton.disabled = false;
728
+ stopButton.disabled = true;
729
+ }
730
+ };
731
+ }
732
+ </script>
733
+ </body>
734
+ </html>
735
+ """
736
+
737
+ # Helper Functions
738
+ def generate_filename(prompt, file_type):
739
+ central = pytz.timezone('US/Central')
740
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
741
+ replaced_prompt = re.sub(r'[<>:"/\\|?*\n]', ' ', prompt)
742
+ safe_prompt = re.sub(r'\s+', ' ', replaced_prompt).strip()[:230]
743
+ return f"{safe_date_time}_{safe_prompt}.{file_type}"
744
+
745
+ # File Management Functions
746
+ def load_file(file_name):
747
+ """Load file content."""
748
+ with open(file_name, "r", encoding='utf-8') as file:
749
+ content = file.read()
750
+ return content
751
+
752
+ def create_zip_of_files(files):
753
+ """Create zip archive of files."""
754
+ zip_name = "all_files.zip"
755
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
756
+ for file in files:
757
+ zipf.write(file)
758
+ return zip_name
759
+
760
+ def get_download_link(file):
761
+ """Create download link for file."""
762
+ with open(file, "rb") as f:
763
+ contents = f.read()
764
+ b64 = base64.b64encode(contents).decode()
765
+ return f'<a href="data:file/txt;base64,{b64}" download="{os.path.basename(file)}">Download {os.path.basename(file)}πŸ“‚</a>'
766
+
767
+ def display_file_manager():
768
+ """Display file management sidebar."""
769
+ st.sidebar.title("πŸ“ File Management")
770
+
771
+ all_files = glob.glob("*.md")
772
+ all_files.sort(reverse=True)
773
+
774
+ if st.sidebar.button("πŸ—‘ Delete All"):
775
+ for file in all_files:
776
+ os.remove(file)
777
+ st.rerun()
778
+
779
+ if st.sidebar.button("⬇️ Download All"):
780
+ zip_file = create_zip_of_files(all_files)
781
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
782
+
783
+ for file in all_files:
784
+ col1, col2, col3, col4 = st.sidebar.columns([1,3,1,1])
785
+ with col1:
786
+ if st.button("🌐", key="view_"+file):
787
+ st.session_state.current_file = file
788
+ st.session_state.file_content = load_file(file)
789
+ with col2:
790
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
791
+ with col3:
792
+ if st.button("πŸ“‚", key="edit_"+file):
793
+ st.session_state.current_file = file
794
+ st.session_state.file_content = load_file(file)
795
+ with col4:
796
+ if st.button("πŸ—‘", key="delete_"+file):
797
+ os.remove(file)
798
+ st.rerun()
799
+
800
+ def create_media_gallery():
801
+ """Create the media gallery interface."""
802
+ st.header("🎬 Media Gallery")
803
+
804
+ tabs = st.tabs(["πŸ–ΌοΈ Images", "🎡 Audio", "πŸŽ₯ Video"])
805
+
806
+ with tabs[0]:
807
+ image_files = glob.glob("*.png") + glob.glob("*.jpg")
808
+ if image_files:
809
+ num_cols = st.slider("Number of columns", 1, 5, 3)
810
+ cols = st.columns(num_cols)
811
+ for idx, image_file in enumerate(image_files):
812
+ with cols[idx % num_cols]:
813
+ img = Image.open(image_file)
814
+ st.image(img, use_container_width=True)
815
+
816
+ # Add GPT vision analysis option
817
+ if st.button(f"Analyze {os.path.basename(image_file)}"):
818
+ analysis = process_image(image_file,
819
+ "Describe this image in detail and identify key elements.")
820
+ st.markdown(analysis)
821
+
822
+ with tabs[1]:
823
+ audio_files = glob.glob("*.mp3") + glob.glob("*.wav")
824
+ for audio_file in audio_files:
825
+ with st.expander(f"🎡 {os.path.basename(audio_file)}"):
826
+ st.markdown(get_media_html(audio_file, "audio"), unsafe_allow_html=True)
827
+ if st.button(f"Transcribe {os.path.basename(audio_file)}"):
828
+ with open(audio_file, "rb") as f:
829
+ transcription = process_audio(f)
830
+ st.write(transcription)
831
+
832
+ with tabs[2]:
833
+ video_files = glob.glob("*.mp4")
834
+ for video_file in video_files:
835
+ with st.expander(f"πŸŽ₯ {os.path.basename(video_file)}"):
836
+ st.markdown(get_media_html(video_file, "video"), unsafe_allow_html=True)
837
+ if st.button(f"Analyze {os.path.basename(video_file)}"):
838
+ analysis = process_video_with_gpt(video_file,
839
+ "Describe what's happening in this video.")
840
+ st.markdown(analysis)
841
+
842
+
843
+
844
+ def get_media_html(media_path, media_type="video", width="100%"):
845
+ """Generate HTML for media player."""
846
+ media_data = base64.b64encode(open(media_path, 'rb').read()).decode()
847
+ if media_type == "video":
848
+ return f'''
849
+ <video width="{width}" controls autoplay muted loop>
850
+ <source src="data:video/mp4;base64,{media_data}" type="video/mp4">
851
+ Your browser does not support the video tag.
852
+ </video>
853
+ '''
854
+ else: # audio
855
+ return f'''
856
+ <audio controls style="width: {width};">
857
+ <source src="data:audio/mpeg;base64,{media_data}" type="audio/mpeg">
858
+ Your browser does not support the audio element.
859
+ </audio>
860
+ '''
861
+
862
+ @st.cache_resource
863
+ def set_transcript(text):
864
+ """Set transcript in session state."""
865
+ st.session_state.voice_transcript = text
866
+
867
+ def main():
868
+ st.sidebar.markdown("### 🚲BikeAIπŸ† Claude and GPT Multi-Agent Research AI")
869
+
870
+ # Main navigation
871
+ tab_main = st.radio("Choose Action:",
872
+ ["🎀 Voice Input", "πŸ’¬ Chat", "πŸ“Έ Media Gallery", "πŸ” Search ArXiv", "πŸ“ File Editor"],
873
+ horizontal=True)
874
+
875
+ if tab_main == "🎀 Voice Input":
876
+ st.subheader("Voice Recognition")
877
+
878
+ # Initialize session state for the transcript
879
+ if 'voice_transcript' not in st.session_state:
880
+ st.session_state.voice_transcript = ""
881
+
882
+ # Display speech recognition component and capture returned value
883
+ transcript = st.components.v1.html(speech_recognition_html, height=400)
884
+
885
+ # Update session state if there's new data
886
+ if transcript is not None and transcript != "":
887
+ st.session_state.voice_transcript = transcript
888
+
889
+ # Display the transcript in a Streamlit text area
890
+ st.markdown("### Processed Voice Input:")
891
+ st.text_area("Voice Transcript", st.session_state.voice_transcript, height=100)
892
+
893
+ # Add functionality to process the transcript
894
+ if st.button("Process Transcript"):
895
+ st.subheader("AI Response to Transcript")
896
+ gpt_response = process_with_gpt(st.session_state.voice_transcript)
897
+ st.markdown(gpt_response)
898
+
899
+ # Option to clear the transcript
900
+ if st.button("Clear Transcript"):
901
+ st.session_state.voice_transcript = ""
902
+ st.rerun()
903
+
904
+
905
+ # Buttons to process the transcript
906
+ if st.button("Search with GPT"):
907
+ st.subheader("GPT-4o Response")
908
+ gpt_response = process_with_gpt(st.session_state.voice_transcript)
909
+ st.markdown(gpt_response)
910
+
911
+ if st.button("Search with Claude"):
912
+ st.subheader("Claude Response")
913
+ claude_response = process_with_claude(st.session_state.voice_transcript)
914
+ st.markdown(claude_response)
915
+
916
+ if st.button("Search ArXiv"):
917
+ st.subheader("ArXiv Search Results")
918
+ arxiv_results = perform_ai_lookup(st.session_state.voice_transcript)
919
+ st.markdown(arxiv_results)
920
+
921
+
922
+ # Display last voice input
923
+ if st.session_state.last_voice_input:
924
+ st.text_area("Last Voice Input:", st.session_state.last_voice_input, height=100)
925
+
926
+
927
+ # Model Selection
928
+ model_choice = st.sidebar.radio(
929
+ "Choose AI Model:",
930
+ ["GPT-4o", "Claude-3", "GPT+Claude+Arxiv"]
931
+ )
932
+
933
+ # Chat Interface
934
+ user_input = st.text_area("Message:", height=100)
935
+
936
+ if st.button("Send πŸ“¨"):
937
+ if user_input:
938
+ if model_choice == "GPT-4o":
939
+ gpt_response = process_with_gpt(user_input)
940
+ elif model_choice == "Claude-3":
941
+ claude_response = process_with_claude(user_input)
942
+ else: # Both
943
+ col1, col2, col3 = st.columns(3)
944
+ with col2:
945
+ st.subheader("Claude-3.5 Sonnet:")
946
+ try:
947
+ claude_response = process_with_claude(user_input)
948
+ except:
949
+ st.write('Claude 3.5 Sonnet out of tokens.')
950
+ with col1:
951
+ st.subheader("GPT-4o Omni:")
952
+ try:
953
+ gpt_response = process_with_gpt(user_input)
954
+ except:
955
+ st.write('GPT 4o out of tokens')
956
+ with col3:
957
+ st.subheader("Arxiv and Mistral Research:")
958
+ with st.spinner("Searching ArXiv..."):
959
+ #results = search_arxiv(user_input)
960
+ results = perform_ai_lookup(user_input)
961
+
962
+ st.markdown(results)
963
+
964
+ # Display Chat History
965
+ st.subheader("Chat History οΏ½οΏ½οΏ½")
966
+ tab1, tab2 = st.tabs(["Claude History", "GPT-4o History"])
967
+
968
+ with tab1:
969
+ for chat in st.session_state.chat_history:
970
+ st.text_area("You:", chat["user"], height=100)
971
+ st.text_area("Claude:", chat["claude"], height=200)
972
+ st.markdown(chat["claude"])
973
+
974
+ with tab2:
975
+ for message in st.session_state.messages:
976
+ with st.chat_message(message["role"]):
977
+ st.markdown(message["content"])
978
+
979
+
980
+ # ------------------------------------------------------- ************************* --->
981
+
982
+
983
+
984
+ if tab_main == "πŸ’¬ Chat":
985
+ # Model Selection
986
+ model_choice = st.sidebar.radio(
987
+ "Choose AI Model:",
988
+ ["GPT-4o", "Claude-3", "GPT+Claude+Arxiv"]
989
+ )
990
+
991
+ # Chat Interface
992
+ user_input = st.text_area("Message:", height=100)
993
+
994
+ if st.button("Send πŸ“¨"):
995
+ if user_input:
996
+ if model_choice == "GPT-4o":
997
+ gpt_response = process_with_gpt(user_input)
998
+ elif model_choice == "Claude-3":
999
+ claude_response = process_with_claude(user_input)
1000
+ else: # Both
1001
+ col1, col2, col3 = st.columns(3)
1002
+ with col2:
1003
+ st.subheader("Claude-3.5 Sonnet:")
1004
+ try:
1005
+ claude_response = process_with_claude(user_input)
1006
+ except:
1007
+ st.write('Claude 3.5 Sonnet out of tokens.')
1008
+ with col1:
1009
+ st.subheader("GPT-4o Omni:")
1010
+ try:
1011
+ gpt_response = process_with_gpt(user_input)
1012
+ except:
1013
+ st.write('GPT 4o out of tokens')
1014
+ with col3:
1015
+ st.subheader("Arxiv and Mistral Research:")
1016
+ with st.spinner("Searching ArXiv..."):
1017
+ #results = search_arxiv(user_input)
1018
+ results = perform_ai_lookup(user_input)
1019
+
1020
+ st.markdown(results)
1021
+
1022
+ # Display Chat History
1023
+ st.subheader("Chat History πŸ“œ")
1024
+ tab1, tab2 = st.tabs(["Claude History", "GPT-4o History"])
1025
+
1026
+ with tab1:
1027
+ for chat in st.session_state.chat_history:
1028
+ st.text_area("You:", chat["user"], height=100)
1029
+ st.text_area("Claude:", chat["claude"], height=200)
1030
+ st.markdown(chat["claude"])
1031
+
1032
+ with tab2:
1033
+ for message in st.session_state.messages:
1034
+ with st.chat_message(message["role"]):
1035
+ st.markdown(message["content"])
1036
+
1037
+ elif tab_main == "πŸ“Έ Media Gallery":
1038
+ create_media_gallery()
1039
+
1040
+ elif tab_main == "πŸ” Search ArXiv":
1041
+ query = st.text_input("Enter your research query:")
1042
+ if query:
1043
+ with st.spinner("Searching ArXiv..."):
1044
+ results = search_arxiv(query)
1045
+ st.markdown(results)
1046
+
1047
+ elif tab_main == "πŸ“ File Editor":
1048
+ if hasattr(st.session_state, 'current_file'):
1049
+ st.subheader(f"Editing: {st.session_state.current_file}")
1050
+ new_content = st.text_area("Content:", st.session_state.file_content, height=300)
1051
+ if st.button("Save Changes"):
1052
+ with open(st.session_state.current_file, 'w', encoding='utf-8') as file:
1053
+ file.write(new_content)
1054
+ st.success("File updated successfully!")
1055
+
1056
+
1057
+ # Always show file manager in sidebar
1058
+ display_file_manager()
1059
+
1060
+ if __name__ == "__main__":
1061
+ main()