Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -8,7 +8,6 @@ import edge_tts
|
|
| 8 |
import streamlit.components.v1 as components
|
| 9 |
|
| 10 |
# -------------------- Configuration --------------------
|
| 11 |
-
# Exactly 11 user names and 11 voices (as an example)
|
| 12 |
USER_NAMES = [
|
| 13 |
"Aria", "Guy", "Sonia", "Tony", "Jenny", "Davis", "Libby", "Clara", "Liam", "Natasha", "William"
|
| 14 |
]
|
|
@@ -20,24 +19,18 @@ ENGLISH_VOICES = [
|
|
| 20 |
]
|
| 21 |
|
| 22 |
USER_VOICES = dict(zip(USER_NAMES, ENGLISH_VOICES))
|
| 23 |
-
|
| 24 |
SAVED_INPUTS_DIR = "saved_inputs"
|
| 25 |
os.makedirs(SAVED_INPUTS_DIR, exist_ok=True)
|
| 26 |
|
| 27 |
-
# Session state
|
| 28 |
if 'user_name' not in st.session_state:
|
| 29 |
st.session_state['user_name'] = USER_NAMES[0]
|
| 30 |
-
|
| 31 |
if 'old_val' not in st.session_state:
|
| 32 |
st.session_state['old_val'] = None
|
| 33 |
-
|
| 34 |
if 'should_rerun' not in st.session_state:
|
| 35 |
st.session_state['should_rerun'] = False
|
| 36 |
-
|
| 37 |
if 'viewing_prefix' not in st.session_state:
|
| 38 |
st.session_state['viewing_prefix'] = None
|
| 39 |
|
| 40 |
-
# -------------------- Utility Functions --------------------
|
| 41 |
def clean_for_speech(text: str) -> str:
|
| 42 |
text = text.replace("\n", " ")
|
| 43 |
text = text.replace("</s>", " ")
|
|
@@ -118,16 +111,26 @@ def arxiv_search(query, max_results=3):
|
|
| 118 |
for entry in entries:
|
| 119 |
title = entry.find('a:title', ns).text.strip()
|
| 120 |
summary = entry.find('a:summary', ns).text.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
summary_short = summary[:300] + "..."
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
return results
|
| 124 |
return []
|
| 125 |
|
| 126 |
def summarize_arxiv_results(results):
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
return "\n\n".join(lines)
|
| 131 |
|
| 132 |
def concatenate_mp3(files, output_file):
|
| 133 |
with open(output_file, 'wb') as outfile:
|
|
@@ -135,21 +138,6 @@ def concatenate_mp3(files, output_file):
|
|
| 135 |
with open(f, 'rb') as infile:
|
| 136 |
outfile.write(infile.read())
|
| 137 |
|
| 138 |
-
def load_groups():
|
| 139 |
-
files = list_saved_inputs()
|
| 140 |
-
groups = defaultdict(list)
|
| 141 |
-
for fpath in files:
|
| 142 |
-
fname = os.path.basename(fpath)
|
| 143 |
-
prefix = fname[:10]
|
| 144 |
-
groups[prefix].append(fpath)
|
| 145 |
-
for prefix in groups:
|
| 146 |
-
groups[prefix].sort(key=lambda x: os.path.getmtime(x), reverse=True)
|
| 147 |
-
sorted_prefixes = sorted(groups.keys(),
|
| 148 |
-
key=lambda pre: max(os.path.getmtime(x) for x in groups[pre]),
|
| 149 |
-
reverse=True)
|
| 150 |
-
return groups, sorted_prefixes
|
| 151 |
-
|
| 152 |
-
# -------------------- Main Application --------------------
|
| 153 |
st.title("ποΈ Voice Chat & ArXiv Search")
|
| 154 |
|
| 155 |
with st.sidebar:
|
|
@@ -169,55 +157,45 @@ with st.sidebar:
|
|
| 169 |
st.success("All history cleared!")
|
| 170 |
st.experimental_rerun()
|
| 171 |
|
| 172 |
-
# Voice input component (replace path with your component)
|
| 173 |
mycomponent = components.declare_component("mycomponent", path="mycomponent")
|
| 174 |
voice_val = mycomponent(my_input_value="Start speaking...")
|
| 175 |
|
| 176 |
-
tabs = st.tabs(["π€ Voice Chat", "
|
| 177 |
|
| 178 |
# ------------------ Voice Chat Tab -------------------------
|
| 179 |
with tabs[0]:
|
| 180 |
st.subheader("π€ Voice Chat")
|
| 181 |
if voice_val:
|
| 182 |
voice_text = voice_val.strip()
|
| 183 |
-
edited_input = st.text_area("βοΈ Edit Voice Input:", value=voice_text, height=100)
|
| 184 |
-
autorun = st.checkbox("β‘ Auto-Run", value=True)
|
| 185 |
input_changed = (voice_text != st.session_state.get('old_val'))
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
-
|
| 188 |
-
st.
|
| 189 |
-
|
| 190 |
-
saved_path = save_input_as_md(st.session_state['user_name'], edited_input, prefix="input")
|
| 191 |
-
st.success("Saved input!")
|
| 192 |
-
|
| 193 |
-
if st.button("π Save Input Manually"):
|
| 194 |
-
saved_path = save_input_as_md(st.session_state['user_name'], edited_input, prefix="input")
|
| 195 |
-
st.success("Saved input!")
|
| 196 |
-
|
| 197 |
-
st.write("Use the sidebar to select user and the voice input component above to record messages.")
|
| 198 |
-
|
| 199 |
-
# ------------------ ArXiv Search Tab -------------------------
|
| 200 |
-
with tabs[1]:
|
| 201 |
-
st.subheader("π ArXiv Search")
|
| 202 |
-
query = st.text_input("Enter Query:")
|
| 203 |
-
if query and st.button("π Search ArXiv"):
|
| 204 |
-
with st.spinner("Searching..."):
|
| 205 |
-
results = arxiv_search(query)
|
| 206 |
-
if results:
|
| 207 |
summary = summarize_arxiv_results(results)
|
| 208 |
# Save as response
|
| 209 |
save_input_as_md(st.session_state['user_name'], summary, prefix="arxiv")
|
| 210 |
st.write(summary)
|
| 211 |
-
|
|
|
|
| 212 |
voice = USER_VOICES.get(st.session_state['user_name'], "en-US-AriaNeural")
|
| 213 |
audio_file = speak_with_edge_tts(summary, voice=voice)
|
| 214 |
if audio_file:
|
| 215 |
play_and_download_audio(audio_file)
|
| 216 |
-
|
| 217 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
|
| 219 |
# ------------------ History Tab -------------------------
|
| 220 |
-
with tabs[
|
| 221 |
st.subheader("πΎ History")
|
| 222 |
files = list_saved_inputs()
|
| 223 |
conversation = []
|
|
@@ -225,7 +203,6 @@ with tabs[2]:
|
|
| 225 |
user, ts, content = parse_md_file(fpath)
|
| 226 |
conversation.append((user, ts, content, fpath))
|
| 227 |
|
| 228 |
-
# Show conversation and read aloud each line
|
| 229 |
for i, (user, ts, content, fpath) in enumerate(reversed(conversation), start=1):
|
| 230 |
with st.expander(f"{ts} - {user}", expanded=False):
|
| 231 |
st.write(content)
|
|
@@ -235,7 +212,6 @@ with tabs[2]:
|
|
| 235 |
if audio_file:
|
| 236 |
play_and_download_audio(audio_file)
|
| 237 |
|
| 238 |
-
# Read entire conversation
|
| 239 |
if st.button("π Read Entire Conversation"):
|
| 240 |
conversation_chrono = list(reversed(conversation))
|
| 241 |
mp3_files = []
|
|
@@ -254,9 +230,9 @@ with tabs[2]:
|
|
| 254 |
play_and_download_audio(combined_file)
|
| 255 |
|
| 256 |
# ------------------ Settings Tab -------------------------
|
| 257 |
-
with tabs[
|
| 258 |
st.subheader("βοΈ Settings")
|
| 259 |
-
st.write("
|
| 260 |
|
| 261 |
if st.session_state.should_rerun:
|
| 262 |
st.session_state.should_rerun = False
|
|
|
|
| 8 |
import streamlit.components.v1 as components
|
| 9 |
|
| 10 |
# -------------------- Configuration --------------------
|
|
|
|
| 11 |
USER_NAMES = [
|
| 12 |
"Aria", "Guy", "Sonia", "Tony", "Jenny", "Davis", "Libby", "Clara", "Liam", "Natasha", "William"
|
| 13 |
]
|
|
|
|
| 19 |
]
|
| 20 |
|
| 21 |
USER_VOICES = dict(zip(USER_NAMES, ENGLISH_VOICES))
|
|
|
|
| 22 |
SAVED_INPUTS_DIR = "saved_inputs"
|
| 23 |
os.makedirs(SAVED_INPUTS_DIR, exist_ok=True)
|
| 24 |
|
|
|
|
| 25 |
if 'user_name' not in st.session_state:
|
| 26 |
st.session_state['user_name'] = USER_NAMES[0]
|
|
|
|
| 27 |
if 'old_val' not in st.session_state:
|
| 28 |
st.session_state['old_val'] = None
|
|
|
|
| 29 |
if 'should_rerun' not in st.session_state:
|
| 30 |
st.session_state['should_rerun'] = False
|
|
|
|
| 31 |
if 'viewing_prefix' not in st.session_state:
|
| 32 |
st.session_state['viewing_prefix'] = None
|
| 33 |
|
|
|
|
| 34 |
def clean_for_speech(text: str) -> str:
|
| 35 |
text = text.replace("\n", " ")
|
| 36 |
text = text.replace("</s>", " ")
|
|
|
|
| 111 |
for entry in entries:
|
| 112 |
title = entry.find('a:title', ns).text.strip()
|
| 113 |
summary = entry.find('a:summary', ns).text.strip()
|
| 114 |
+
# Extract links (PDF) if available
|
| 115 |
+
links = entry.findall('a:link', ns)
|
| 116 |
+
pdf_link = None
|
| 117 |
+
for link in links:
|
| 118 |
+
if link.get('type') == 'application/pdf':
|
| 119 |
+
pdf_link = link.get('href')
|
| 120 |
summary_short = summary[:300] + "..."
|
| 121 |
+
# Include PDF link and title
|
| 122 |
+
if pdf_link:
|
| 123 |
+
formatted = f"Title: {title}\nPDF: {pdf_link}\nSummary: {summary_short}"
|
| 124 |
+
else:
|
| 125 |
+
formatted = f"Title: {title}\n(No PDF link)\nSummary: {summary_short}"
|
| 126 |
+
results.append(formatted)
|
| 127 |
return results
|
| 128 |
return []
|
| 129 |
|
| 130 |
def summarize_arxiv_results(results):
|
| 131 |
+
if not results:
|
| 132 |
+
return "No results found."
|
| 133 |
+
return "\n\n".join(results)
|
|
|
|
| 134 |
|
| 135 |
def concatenate_mp3(files, output_file):
|
| 136 |
with open(output_file, 'wb') as outfile:
|
|
|
|
| 138 |
with open(f, 'rb') as infile:
|
| 139 |
outfile.write(infile.read())
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
st.title("ποΈ Voice Chat & ArXiv Search")
|
| 142 |
|
| 143 |
with st.sidebar:
|
|
|
|
| 157 |
st.success("All history cleared!")
|
| 158 |
st.experimental_rerun()
|
| 159 |
|
|
|
|
| 160 |
mycomponent = components.declare_component("mycomponent", path="mycomponent")
|
| 161 |
voice_val = mycomponent(my_input_value="Start speaking...")
|
| 162 |
|
| 163 |
+
tabs = st.tabs(["π€ Voice Chat", "πΎ History", "βοΈ Settings"])
|
| 164 |
|
| 165 |
# ------------------ Voice Chat Tab -------------------------
|
| 166 |
with tabs[0]:
|
| 167 |
st.subheader("π€ Voice Chat")
|
| 168 |
if voice_val:
|
| 169 |
voice_text = voice_val.strip()
|
|
|
|
|
|
|
| 170 |
input_changed = (voice_text != st.session_state.get('old_val'))
|
| 171 |
+
if input_changed and voice_text:
|
| 172 |
+
# 1. Save user input
|
| 173 |
+
save_input_as_md(st.session_state['user_name'], voice_text, prefix="input")
|
| 174 |
|
| 175 |
+
# 2. Perform ArXiv search automatically
|
| 176 |
+
with st.spinner("Searching ArXiv..."):
|
| 177 |
+
results = arxiv_search(voice_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
summary = summarize_arxiv_results(results)
|
| 179 |
# Save as response
|
| 180 |
save_input_as_md(st.session_state['user_name'], summary, prefix="arxiv")
|
| 181 |
st.write(summary)
|
| 182 |
+
|
| 183 |
+
# 3. Convert summary to audio and auto-play
|
| 184 |
voice = USER_VOICES.get(st.session_state['user_name'], "en-US-AriaNeural")
|
| 185 |
audio_file = speak_with_edge_tts(summary, voice=voice)
|
| 186 |
if audio_file:
|
| 187 |
play_and_download_audio(audio_file)
|
| 188 |
+
|
| 189 |
+
# 4. Update old_val to avoid repeated searches for same input
|
| 190 |
+
st.session_state['old_val'] = voice_text
|
| 191 |
+
|
| 192 |
+
# 5. Clear displayed text and re-run so next utterance starts fresh
|
| 193 |
+
st.experimental_rerun()
|
| 194 |
+
|
| 195 |
+
st.write("Speak a query to automatically run an ArXiv search and read results aloud.")
|
| 196 |
|
| 197 |
# ------------------ History Tab -------------------------
|
| 198 |
+
with tabs[1]:
|
| 199 |
st.subheader("πΎ History")
|
| 200 |
files = list_saved_inputs()
|
| 201 |
conversation = []
|
|
|
|
| 203 |
user, ts, content = parse_md_file(fpath)
|
| 204 |
conversation.append((user, ts, content, fpath))
|
| 205 |
|
|
|
|
| 206 |
for i, (user, ts, content, fpath) in enumerate(reversed(conversation), start=1):
|
| 207 |
with st.expander(f"{ts} - {user}", expanded=False):
|
| 208 |
st.write(content)
|
|
|
|
| 212 |
if audio_file:
|
| 213 |
play_and_download_audio(audio_file)
|
| 214 |
|
|
|
|
| 215 |
if st.button("π Read Entire Conversation"):
|
| 216 |
conversation_chrono = list(reversed(conversation))
|
| 217 |
mp3_files = []
|
|
|
|
| 230 |
play_and_download_audio(combined_file)
|
| 231 |
|
| 232 |
# ------------------ Settings Tab -------------------------
|
| 233 |
+
with tabs[2]:
|
| 234 |
st.subheader("βοΈ Settings")
|
| 235 |
+
st.write("Currently no additional settings. Use the sidebar to pick a user.")
|
| 236 |
|
| 237 |
if st.session_state.should_rerun:
|
| 238 |
st.session_state.should_rerun = False
|