Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -113,44 +113,50 @@ if user_input:
|
|
| 113 |
messages_response = client.beta.threads.messages.list(thread_id=thread_id)
|
| 114 |
latest_response = sorted(messages_response.data, key=lambda x: x.created_at)[-1]
|
| 115 |
assistant_message = latest_response.content[0].text.value
|
| 116 |
-
save_message("assistant", assistant_message)
|
| 117 |
-
|
| 118 |
-
# π Voice controls (after displaying the message)
|
| 119 |
-
st.components.v1.html(f"""
|
| 120 |
-
<div id="voice-controls" style="margin: 10px 0;">
|
| 121 |
-
<button id="speak-btn" style="margin-right:10px;">π Speak</button>
|
| 122 |
-
<button id="mute-btn">π Mute</button>
|
| 123 |
-
</div>
|
| 124 |
-
<script>
|
| 125 |
-
var utterance;
|
| 126 |
-
var isSpeaking = false;
|
| 127 |
-
var synth = window.speechSynthesis;
|
| 128 |
-
var text = `{assistant_message.replace("`", "\\`")}`;
|
| 129 |
-
var voices = [];
|
| 130 |
-
function getFemaleVoice() {{
|
| 131 |
-
voices = synth.getVoices();
|
| 132 |
-
let female = voices.find(v => v.name.includes("Female") || (v.name.includes("en") && v.gender !== "male"));
|
| 133 |
-
return female || voices.find(v => v.lang.startsWith('en') && v.name.toLowerCase().includes('female')) || voices[0];
|
| 134 |
-
}}
|
| 135 |
-
|
| 136 |
-
document.getElementById('speak-btn').onclick = function() {{
|
| 137 |
-
if (isSpeaking) return;
|
| 138 |
-
utterance = new SpeechSynthesisUtterance(text);
|
| 139 |
-
utterance.voice = getFemaleVoice();
|
| 140 |
-
utterance.rate = 1;
|
| 141 |
-
utterance.pitch = 1.1;
|
| 142 |
-
synth.speak(utterance);
|
| 143 |
-
isSpeaking = true;
|
| 144 |
-
utterance.onend = function() {{ isSpeaking = false; }};
|
| 145 |
-
}};
|
| 146 |
-
|
| 147 |
-
document.getElementById('mute-btn').onclick = function() {{
|
| 148 |
-
synth.cancel();
|
| 149 |
-
isSpeaking = false;
|
| 150 |
-
}};
|
| 151 |
-
</script>
|
| 152 |
-
""", height=60)
|
| 153 |
-
|
| 154 |
-
time.sleep(0.5)
|
| 155 |
-
st.rerun()
|
| 156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
messages_response = client.beta.threads.messages.list(thread_id=thread_id)
|
| 114 |
latest_response = sorted(messages_response.data, key=lambda x: x.created_at)[-1]
|
| 115 |
assistant_message = latest_response.content[0].text.value
|
| 116 |
+
save_message("assistant", assistant_message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
+
# Prepare the message for JavaScript safely
|
| 119 |
+
safe_message = assistant_message.replace("\\", "\\\\").replace("`", "\\`").replace("\n", "\\n").replace("'", "\\'")
|
| 120 |
+
|
| 121 |
+
# π Voice controls (auto-speak enabled)
|
| 122 |
+
st.components.v1.html(f"""
|
| 123 |
+
<div id="voice-controls" style="margin: 10px 0;">
|
| 124 |
+
<button id="speak-btn" style="margin-right:10px;">π Speak</button>
|
| 125 |
+
<button id="mute-btn">π Mute</button>
|
| 126 |
+
</div>
|
| 127 |
+
<script>
|
| 128 |
+
var utterance;
|
| 129 |
+
var isSpeaking = false;
|
| 130 |
+
var synth = window.speechSynthesis;
|
| 131 |
+
var text = `{safe_message}`;
|
| 132 |
+
var voices = [];
|
| 133 |
+
function getFemaleVoice() {{
|
| 134 |
+
voices = synth.getVoices();
|
| 135 |
+
let female = voices.find(v => (v.name && v.name.toLowerCase().includes("female")) || (v.lang && v.lang.startsWith("en") && v.gender !== "male"));
|
| 136 |
+
// Fallback: any en female, then any en, then first
|
| 137 |
+
return female || voices.find(v => v.lang && v.lang.startsWith('en') && v.name && v.name.toLowerCase().includes('female')) || voices.find(v => v.lang && v.lang.startsWith('en')) || voices[0];
|
| 138 |
+
}}
|
| 139 |
+
function speakText() {{
|
| 140 |
+
if (isSpeaking) return;
|
| 141 |
+
utterance = new SpeechSynthesisUtterance(text);
|
| 142 |
+
utterance.voice = getFemaleVoice();
|
| 143 |
+
utterance.rate = 1;
|
| 144 |
+
utterance.pitch = 1.1;
|
| 145 |
+
synth.speak(utterance);
|
| 146 |
+
isSpeaking = true;
|
| 147 |
+
utterance.onend = function() {{ isSpeaking = false; }};
|
| 148 |
+
}}
|
| 149 |
+
document.getElementById('speak-btn').onclick = function() {{
|
| 150 |
+
speakText();
|
| 151 |
+
}};
|
| 152 |
+
document.getElementById('mute-btn').onclick = function() {{
|
| 153 |
+
synth.cancel();
|
| 154 |
+
isSpeaking = false;
|
| 155 |
+
}};
|
| 156 |
+
// Auto-speak on load
|
| 157 |
+
setTimeout(speakText, 500);
|
| 158 |
+
</script>
|
| 159 |
+
""", height=80)
|
| 160 |
+
|
| 161 |
+
time.sleep(0.5)
|
| 162 |
+
st.rerun()
|