import streamlit as st
import anthropic, openai, base64, cv2, glob, json, math, os, pytz, random, re, requests, time, zipfile
from datetime import datetime
from audio_recorder_streamlit import audio_recorder
from collections import defaultdict, deque, Counter
from dotenv import load_dotenv
from gradio_client import Client
from huggingface_hub import InferenceClient
from PIL import Image
from openai import OpenAI
from streamlit_marquee import streamlit_marquee
import asyncio
import edge_tts
# Core Configuration
st.set_page_config(
page_title="π²TalkingAIResearcherπ",
page_icon="π²π",
layout="wide",
initial_sidebar_state="auto",
menu_items={
'Get Help': 'https://huggingface.co/awacke1',
'Report a bug': 'https://huggingface.co/spaces/awacke1',
'About': "π²TalkingAIResearcherπ"
}
)
# Custom CSS
st.markdown("""
""", unsafe_allow_html=True)
# Constants
EDGE_TTS_VOICES = [
"en-US-AriaNeural", # Default voice
"en-US-GuyNeural",
"en-US-JennyNeural",
"en-GB-SoniaNeural",
"en-GB-RyanNeural",
"en-AU-NatashaNeural",
"en-AU-WilliamNeural",
"en-CA-ClaraNeural",
"en-CA-LiamNeural"
]
FILE_EMOJIS = {
"md": "π",
"mp3": "π΅",
"wav": "π",
"txt": "π",
"pdf": "π",
"html": "π"
}
# Load environment variables
load_dotenv()
# API Setup
openai_api_key = os.getenv('OPENAI_API_KEY', "")
anthropic_key = os.getenv('ANTHROPIC_API_KEY', "")
if 'OPENAI_API_KEY' in st.secrets:
openai_api_key = st.secrets['OPENAI_API_KEY']
if 'ANTHROPIC_API_KEY' in st.secrets:
anthropic_key = st.secrets["ANTHROPIC_API_KEY"]
openai_client = OpenAI(api_key=openai_api_key)
claude_client = anthropic.Anthropic(api_key=anthropic_key)
# Initialize Session State
state_vars = {
'tts_voice': EDGE_TTS_VOICES[0],
'audio_format': 'mp3',
'messages': [],
'chat_history': [],
'transcript_history': [],
'viewing_prefix': None,
'should_rerun': False,
'editing_mode': False,
'current_file': None,
'file_content': None,
'old_val': None,
'last_query': ''
}
for key, default in state_vars.items():
if key not in st.session_state:
st.session_state[key] = default
# Core Functions
@st.cache_resource
def get_cached_audio_b64(file_path):
with open(file_path, "rb") as f:
return base64.b64encode(f.read()).decode()
def beautify_filename(filename):
name = os.path.splitext(filename)[0]
return name.replace('_', ' ').replace('.', ' ')
def display_marquee_controls():
st.sidebar.markdown("### π― Marquee Settings")
cols = st.sidebar.columns(2)
with cols[0]:
bg_color = st.color_picker("π¨ Background", "#1E1E1E")
text_color = st.color_picker("βοΈ Text", "#FFFFFF")
with cols[1]:
font_size = st.slider("π Size", 10, 24, 14)
duration = st.slider("β±οΈ Speed", 1, 20, 10)
return {
"background": bg_color,
"color": text_color,
"font-size": f"{font_size}px",
"animationDuration": f"{duration}s",
"width": "100%",
"lineHeight": "35px"
}
def get_high_info_terms(text: str, top_n=10) -> list:
stop_words = set(['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for'])
words = re.findall(r'\b\w+(?:-\w+)*\b', text.lower())
bi_grams = [' '.join(pair) for pair in zip(words, words[1:])]
combined = words + bi_grams
filtered = [term for term in combined if term not in stop_words and len(term) > 3]
return Counter(filtered).most_common(top_n)
def generate_filename(prompt, response, file_type="md"):
prefix = datetime.now().strftime("%y%m_%H%M") + "_"
combined = (prompt + " " + response).strip()
name_parts = [term for term, _ in get_high_info_terms(combined, top_n=5)]
filename = prefix + "_".join(name_parts)[:150] + "." + file_type
return filename
def create_file(prompt, response, file_type="md"):
filename = generate_filename(prompt.strip(), response.strip(), file_type)
with open(filename, 'w', encoding='utf-8') as f:
f.write(f"{prompt}\n\n{response}")
return filename
def get_download_link(file_path, file_type="zip"):
with open(file_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
ext_map = {'zip': 'π¦', 'mp3': 'π΅', 'wav': 'π', 'md': 'π'}
emoji = ext_map.get(file_type, '')
return f'{emoji} Download {os.path.basename(file_path)}'
# Audio Processing
def clean_speech_text(text):
text = re.sub(r'\s+', ' ', text.strip())
text = text.replace("", "").replace("#", "")
text = re.sub(r"\(https?:\/\/[^\)]+\)", "", text)
return text
async def edge_tts_generate(text, voice="en-US-AriaNeural", rate=0, pitch=0, file_format="mp3"):
text = clean_speech_text(text)
if not text: return None
communicate = edge_tts.Communicate(text, voice, rate=f"{rate}%", pitch=f"{pitch}Hz")
filename = f"{datetime.now().strftime('%y%m_%H%M')}_{voice}.{file_format}"
await communicate.save(filename)
return filename
def speak_text(text, voice=None, file_format=None):
if not text: return None
voice = voice or st.session_state['tts_voice']
fmt = file_format or st.session_state['audio_format']
return asyncio.run(edge_tts_generate(text, voice, file_format=fmt))
def process_audio_file(audio_path):
with open(audio_path, "rb") as f:
transcript = openai_client.audio.transcriptions.create(model="whisper-1", file=f)
text = transcript.text
st.session_state.messages.append({"role": "user", "content": text})
return text
# File Management
def load_files_for_sidebar():
files = []
for f in glob.glob("*.*"):
basename = os.path.basename(f)
if f.endswith('.md'):
if len(basename) >= 9 and re.match(r'\d{4}_\d{4}', basename[:9]):
files.append(f)
else:
files.append(f)
groups = defaultdict(list)
for f in files:
basename = os.path.basename(f)
group_name = basename[:9] if len(basename) >= 9 else 'Other'
groups[group_name].append(f)
return sorted(groups.items(),
key=lambda x: max(os.path.getmtime(f) for f in x[1]),
reverse=True)
def display_file_manager_sidebar(groups_sorted):
st.sidebar.title("π File Manager")
all_files = {'md': [], 'mp3': [], 'wav': []}
for _, files in groups_sorted:
for f in files:
ext = os.path.splitext(f)[1].lower().strip('.')
if ext in all_files:
all_files[ext].append(f)
cols = st.sidebar.columns(4)
for i, (ext, files) in enumerate(all_files.items()):
with cols[i]:
if st.button(f"ποΈ {ext.upper()}"):
[os.remove(f) for f in files]
st.session_state.should_rerun = True
if st.sidebar.button("π¦ Zip All"):
zip_name = create_zip_of_files(all_files['md'], all_files['mp3'], all_files['wav'])
if zip_name:
st.sidebar.markdown(get_download_link(zip_name), unsafe_allow_html=True)
for group_name, files in groups_sorted:
try:
timestamp = datetime.strptime(group_name, "%y%m_%H%M").strftime("%Y-%m-%d %H:%M") if len(group_name) == 9 and group_name != 'Other' else group_name
except ValueError:
timestamp = group_name
with st.sidebar.expander(f"π {timestamp} ({len(files)})", expanded=True):
c1, c2 = st.columns(2)
with c1:
if st.button("π", key=f"view_{group_name}"):
st.session_state.viewing_prefix = group_name
with c2:
if st.button("ποΈ", key=f"del_{group_name}"):
[os.remove(f) for f in files]
st.session_state.should_rerun = True
for f in files:
ext = os.path.splitext(f)[1].lower().strip('.')
emoji = FILE_EMOJIS.get(ext, 'π')
pretty_name = beautify_filename(os.path.basename(f))
st.write(f"{emoji} **{pretty_name}**")
if ext in ['mp3', 'wav']:
st.audio(f)
if st.button("π", key=f"loop_{f}"):
audio_b64 = get_cached_audio_b64(f)
st.components.v1.html(
f'''
''',
height=0
)
# ArXiv Integration
def perform_arxiv_search(query):
client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
papers = client.predict(
query, 20, "Semantic Search",
"mistralai/Mixtral-8x7B-Instruct-v0.1",
api_name="/update_with_rag_md"
)[0]
summary = client.predict(
query,
"mistralai/Mixtral-8x7B-Instruct-v0.1",
True,
api_name="/ask_llm"
)
result = f"### π {query}\n\n{summary}\n\n{papers}"
st.markdown(result)
papers_data = parse_arxiv_refs(papers)
if papers_data:
create_paper_audio(papers_data, query)
display_papers(papers_data)
create_file(query, result, "md")
return result
def parse_arxiv_refs(text):
papers = []
current = None
for line in text.split('\n'):
if '|' in line:
if current: papers.append(current)
parts = line.strip('* ').split('|')
current = {
'date': parts[0].strip(),
'title': parts[1].strip(),
'authors': '',
'summary': '',
'id': re.search(r'(\d{4}\.\d{5})', line).group(1) if re.search(r'(\d{4}\.\d{5})', line) else ''
}
elif current:
if not current['authors']:
current['authors'] = line.strip('* ')
else:
current['summary'] += ' ' + line.strip()
if current: papers.append(current)
return papers[:20]
def create_paper_audio(papers, query):
combined = []
for paper in papers:
try:
text = f"{paper['title']} by {paper['authors']}. {paper['summary']}"
file_format = st.session_state['audio_format']
audio_file = speak_text(text, file_format=file_format)
paper['audio'] = audio_file
st.write(f"### {FILE_EMOJIS.get(file_format, '')} {os.path.basename(audio_file)}")
st.audio(audio_file)
combined.append(paper['title'])
except Exception as e:
st.warning(f"Error generating audio for {paper['title']}: {str(e)}")
if combined:
summary = f"Found papers about: {'; '.join(combined)}. Query was: {query}"
summary_audio = speak_text(summary)
if summary_audio:
st.write("### π’ Summary")
st.audio(summary_audio)
def display_papers(papers):
st.write("## Research Papers")
for i, paper in enumerate(papers[:20], 1):
with st.expander(f"{i}. π {paper['title']}", expanded=True):
st.markdown(f"**{paper['date']} | {paper['title']} | β¬οΈ**")
st.markdown(f"*{paper['authors']}*")
st.markdown(paper[previous code] ... st.markdown(paper['summary'])
if paper.get('audio'):
st.write("π Paper Audio")
st.audio(paper['audio'])
def process_with_gpt(text):
if not text: return
st.session_state.messages.append({"role": "user", "content": text})
with st.chat_message("user"):
st.markdown(text)
with st.chat_message("assistant"):
response = openai_client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=st.session_state.messages,
stream=False
)
answer = response.choices[0].message.content
st.write(f"GPT-4: {answer}")
create_file(text, answer, "md")
st.session_state.messages.append({"role": "assistant", "content": answer})
return answer
def process_with_claude(text):
if not text: return
with st.chat_message("user"):
st.markdown(text)
with st.chat_message("assistant"):
response = claude_client.messages.create(
model="claude-3-sonnet-20240229",
max_tokens=4000,
messages=[{"role": "user", "content": text}]
)
answer = response.content[0].text
st.write(f"Claude-3: {answer}")
create_file(text, answer, "md")
st.session_state.chat_history.append({"user": text, "claude": answer})
return answer
# Main App
def main():
st.sidebar.title("π² Research Assistant")
# Voice settings
st.sidebar.markdown("### π€ Voice Config")
voice = st.sidebar.selectbox("Voice:", EDGE_TTS_VOICES,
index=EDGE_TTS_VOICES.index(st.session_state['tts_voice']))
fmt = st.sidebar.radio("Format:", ["MP3", "WAV"], index=0)
if voice != st.session_state['tts_voice']:
st.session_state['tts_voice'] = voice
st.rerun()
if fmt.lower() != st.session_state['audio_format']:
st.session_state['audio_format'] = fmt.lower()
st.rerun()
mode = st.radio("Mode:", ["π€ Voice", "π ArXiv", "π Editor"], horizontal=True)
if mode == "π ArXiv":
query = st.text_input("π Search:")
if query:
perform_arxiv_search(query)
elif mode == "π€ Voice":
text = st.text_area("Message:", height=100).strip()
if st.button("Send"):
process_with_gpt(text)
st.subheader("History")
tab1, tab2 = st.tabs(["Claude", "GPT-4"])
with tab1:
for msg in st.session_state.chat_history:
st.write("You:", msg["user"])
st.write("Claude:", msg["claude"])
with tab2:
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
elif mode == "π Editor":
if st.session_state.current_file:
st.subheader(f"Editing: {st.session_state.current_file}")
new_content = st.text_area("Content:", st.session_state.file_content, height=300)
if st.button("Save"):
with open(st.session_state.current_file, 'w') as f:
f.write(new_content)
st.success("Saved!")
st.session_state.should_rerun = True
# File management
groups = load_files_for_sidebar()
display_file_manager_sidebar(groups)
if st.session_state.should_rerun:
st.session_state.should_rerun = False
st.rerun()
sidebar_md = """# π Research
## AGI Levels
L0 β No AI
L1 π± ChatGPT [2303.08774](https://arxiv.org/abs/2303.08774) | [PDF](https://arxiv.org/pdf/2303.08774.pdf)
L2 πͺ Watson [2201.11903](https://arxiv.org/abs/2201.11903) | [PDF](https://arxiv.org/pdf/2201.11903.pdf)
L3 π― DALLΒ·E [2204.06125](https://arxiv.org/abs/2204.06125) | [PDF](https://arxiv.org/pdf/2204.06125.pdf)
L4 π AlphaGo [1712.01815](https://arxiv.org/abs/1712.01815) | [PDF](https://arxiv.org/pdf/1712.01815.pdf)
L5 π AlphaFold [2203.15556](https://arxiv.org/abs/2203.15556) | [PDF](https://arxiv.org/pdf/2203.15556.pdf)
## 𧬠AlphaFold2
[2203.15556](https://arxiv.org/abs/2203.15556) | [PDF](https://arxiv.org/pdf/2203.15556.pdf)
1. 𧬠Input β 2. π Search β 3. π§© MSA
4. π Templates β 5. π Evoformer β 6. π§± Structure
7. π― 3D Predict β 8. β»οΈ Recycle"""
st.sidebar.markdown(sidebar_md)
if __name__ == "__main__":
main()