import streamlit as st
import anthropic, openai, base64, cv2, glob, json, math, os, pytz, random, re, requests, time, zipfile
import plotly.graph_objects as go
import streamlit.components.v1 as components
from datetime import datetime
from audio_recorder_streamlit import audio_recorder
from bs4 import BeautifulSoup
from collections import defaultdict, deque
from dotenv import load_dotenv
from gradio_client import Client
from huggingface_hub import InferenceClient
from io import BytesIO
from PIL import Image
from PyPDF2 import PdfReader
from urllib.parse import quote
from xml.etree import ElementTree as ET
from openai import OpenAI
import extra_streamlit_components as stx
from streamlit.runtime.scriptrunner import get_script_run_ctx
import asyncio
import edge_tts
# 1. Core Configuration & Setup
st.set_page_config(
page_title="🚲BikeAI🏆 Research Assistant Pro",
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': "Research Assistant Pro with Voice Search"
}
)
load_dotenv()
# 2. API Setup & Clients
openai_api_key = os.getenv('OPENAI_API_KEY', st.secrets.get('OPENAI_API_KEY', ''))
anthropic_key = os.getenv('ANTHROPIC_API_KEY_3', st.secrets.get('ANTHROPIC_API_KEY', ''))
hf_key = os.getenv('HF_KEY', st.secrets.get('HF_KEY', ''))
openai_client = OpenAI(api_key=openai_api_key)
claude_client = anthropic.Anthropic(api_key=anthropic_key)
# 3. Session State Management
if 'transcript_history' not in st.session_state:
st.session_state['transcript_history'] = []
if 'chat_history' not in st.session_state:
st.session_state['chat_history'] = []
if 'openai_model' not in st.session_state:
st.session_state['openai_model'] = "gpt-4-vision-preview"
if 'messages' not in st.session_state:
st.session_state['messages'] = []
if 'last_voice_input' not in st.session_state:
st.session_state['last_voice_input'] = ""
if 'editing_file' not in st.session_state:
st.session_state['editing_file'] = None
if 'current_audio' not in st.session_state:
st.session_state['current_audio'] = None
if 'autoplay_audio' not in st.session_state:
st.session_state['autoplay_audio'] = True
if 'should_rerun' not in st.session_state:
st.session_state['should_rerun'] = False
if 'old_val' not in st.session_state:
st.session_state['old_val'] = None
# 4. Style Definitions
st.markdown("""
""", unsafe_allow_html=True)
FILE_EMOJIS = {
"md": "📝",
"mp3": "🎵",
"mp4": "🎥",
"png": "🖼️",
"jpg": "📸"
}
# 5. Voice Recognition Component
def create_voice_component():
"""Create auto-searching voice recognition component"""
return components.html(
"""
Starting voice recognition...
""",
height=200
)
# Available English voices
ENGLISH_VOICES = [
"en-US-AriaNeural", # Female, conversational
"en-US-JennyNeural", # Female, customer service
"en-US-GuyNeural", # Male, newscast
"en-US-RogerNeural", # Male, calm
"en-GB-SoniaNeural", # British female
"en-GB-RyanNeural", # British male
"en-AU-NatashaNeural", # Australian female
"en-AU-WilliamNeural", # Australian male
"en-CA-ClaraNeural", # Canadian female
"en-CA-LiamNeural", # Canadian male
"en-IE-EmilyNeural", # Irish female
"en-IE-ConnorNeural", # Irish male
"en-IN-NeerjaNeural", # Indian female
"en-IN-PrabhatNeural", # Indian male
]
def render_search_interface():
"""Render main search interface with auto-search voice component"""
st.header("🔍 Voice Search")
# Voice settings
col1, col2 = st.columns([2, 1])
with col1:
selected_voice = st.selectbox(
"Select Voice",
ENGLISH_VOICES,
index=0,
help="Choose the voice for audio responses"
)
with col2:
auto_search = st.checkbox("Auto-Search on Pause", value=True)
# Voice component
voice_result = create_voice_component()
# Handle voice input
if voice_result and isinstance(voice_result, (str, dict)):
# Extract text and trigger info
if isinstance(voice_result, dict):
current_text = voice_result.get('text', '')
trigger = voice_result.get('trigger')
else:
current_text = voice_result
trigger = None
# Process on pause trigger if enabled
if auto_search and trigger == 'pause' and current_text:
if current_text != st.session_state.get('last_processed_text', ''):
st.session_state.last_processed_text = current_text
# Show the detected text
st.info(f"🎤 Detected: {current_text}")
# Perform search
try:
with st.spinner("Searching and generating audio response..."):
response, audio_file = asyncio.run(
process_voice_search(
current_text,
voice=selected_voice
)
)
if response:
st.markdown(response)
if audio_file:
render_audio_result(audio_file, "Search Results")
# Save to history
st.session_state.transcript_history.append({
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
'query': current_text,
'response': response,
'audio': audio_file
})
except Exception as e:
st.error(f"Error processing search: {str(e)}")
# Manual search option
with st.expander("📝 Manual Search", expanded=False):
query = st.text_input("Search Query:", value=st.session_state.get('last_processed_text', ''))
if st.button("🔍 Search"):
try:
with st.spinner("Searching and generating audio..."):
response, audio_file = asyncio.run(
process_voice_search(
query,
voice=selected_voice
)
)
if response:
st.markdown(response)
if audio_file:
render_audio_result(audio_file)
except Exception as e:
st.error(f"Error processing search: {str(e)}")
# 6. Audio Processing Functions
def get_autoplay_audio_html(audio_path, width="100%"):
"""Create HTML for autoplaying audio with controls"""
try:
with open(audio_path, "rb") as audio_file:
audio_bytes = audio_file.read()
audio_b64 = base64.b64encode(audio_bytes).decode()
return f'''