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
|