Update app.py
Browse files
app.py
CHANGED
@@ -89,11 +89,11 @@ HISTORY_FILE = os.path.join(HISTORY_DIR, "chat_history.md")
|
|
89 |
# Unicode digits
|
90 |
UNICODE_DIGITS = {i: f"{i}\uFE0F⃣" for i in range(10)}
|
91 |
|
92 |
-
# Unicode fonts
|
93 |
UNICODE_FONTS = [
|
94 |
("Normal", lambda x: x),
|
95 |
("Bold", lambda x: "".join(chr(ord(c) + 0x1D400 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D41A - 0x61) if 'a' <= c <= 'z' else c for c in x)),
|
96 |
-
# Add other font styles
|
97 |
]
|
98 |
|
99 |
# Global state
|
@@ -390,7 +390,6 @@ def clean_text_for_filename(text):
|
|
390 |
return '_'.join(text.split())[:200]
|
391 |
|
392 |
def parse_arxiv_refs(ref_text):
|
393 |
-
# Simplified parsing for brevity
|
394 |
return [{"title": line.strip(), "url": "", "authors": "", "summary": "", "full_audio": None, "download_base64": ""} for line in ref_text.split('\n') if line.strip()]
|
395 |
|
396 |
async def create_paper_audio_files(papers, input_question):
|
@@ -403,139 +402,6 @@ async def create_paper_audio_files(papers, input_question):
|
|
403 |
b64 = base64.b64encode(f.read()).decode()
|
404 |
paper['download_base64'] = f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(audio_file)}">🎵 Download</a>'
|
405 |
|
406 |
-
# ASR Component HTML (Fixed Audio Chat)
|
407 |
-
ASR_HTML = """
|
408 |
-
<html>
|
409 |
-
<head>
|
410 |
-
<title>Continuous Speech Demo</title>
|
411 |
-
<style>
|
412 |
-
body { font-family: sans-serif; padding: 20px; max-width: 800px; margin: 0 auto; }
|
413 |
-
button { padding: 10px 20px; margin: 10px 5px; font-size: 16px; }
|
414 |
-
#status { margin: 10px 0; padding: 10px; background: #e8f5e9; border-radius: 4px; }
|
415 |
-
#output { white-space: pre-wrap; padding: 15px; background: #f5f5f5; border-radius: 4px; margin: 10px 0; min-height: 100px; max-height: 400px; overflow-y: auto; }
|
416 |
-
</style>
|
417 |
-
</head>
|
418 |
-
<body>
|
419 |
-
<div>
|
420 |
-
<button id="start">Start Listening</button>
|
421 |
-
<button id="stop" disabled>Stop Listening</button>
|
422 |
-
<button id="clear">Clear Text</button>
|
423 |
-
</div>
|
424 |
-
<div id="status">Ready</div>
|
425 |
-
<div id="output"></div>
|
426 |
-
<script>
|
427 |
-
if (!('webkitSpeechRecognition' in window)) {
|
428 |
-
alert('Speech recognition not supported');
|
429 |
-
} else {
|
430 |
-
const recognition = new webkitSpeechRecognition();
|
431 |
-
const startButton = document.getElementById('start');
|
432 |
-
const stopButton = document.getElementById('stop');
|
433 |
-
const clearButton = document.getElementById('clear');
|
434 |
-
const status = document.getElementById('status');
|
435 |
-
const output = document.getElementById('output');
|
436 |
-
let fullTranscript = '';
|
437 |
-
let lastUpdateTime = Date.now();
|
438 |
-
|
439 |
-
recognition.continuous = true;
|
440 |
-
recognition.interimResults = true;
|
441 |
-
|
442 |
-
const startRecognition = () => {
|
443 |
-
try {
|
444 |
-
recognition.start();
|
445 |
-
status.textContent = 'Listening...';
|
446 |
-
startButton.disabled = true;
|
447 |
-
stopButton.disabled = false;
|
448 |
-
} catch (e) {
|
449 |
-
console.error(e);
|
450 |
-
status.textContent = 'Error: ' + e.message;
|
451 |
-
}
|
452 |
-
};
|
453 |
-
|
454 |
-
window.addEventListener('load', () => setTimeout(startRecognition, 1000));
|
455 |
-
|
456 |
-
startButton.onclick = startRecognition;
|
457 |
-
|
458 |
-
stopButton.onclick = () => {
|
459 |
-
recognition.stop();
|
460 |
-
status.textContent = 'Stopped';
|
461 |
-
startButton.disabled = false;
|
462 |
-
stopButton.disabled = true;
|
463 |
-
};
|
464 |
-
|
465 |
-
clearButton.onclick = () => {
|
466 |
-
fullTranscript = '';
|
467 |
-
output.textContent = '';
|
468 |
-
sendDataToPython({value: '', dataType: "json"});
|
469 |
-
};
|
470 |
-
|
471 |
-
recognition.onresult = (event) => {
|
472 |
-
let interimTranscript = '';
|
473 |
-
let finalTranscript = '';
|
474 |
-
|
475 |
-
for (let i = event.resultIndex; i < event.results.length; i++) {
|
476 |
-
const transcript = event.results[i][0].transcript;
|
477 |
-
if (event.results[i].isFinal) {
|
478 |
-
finalTranscript += transcript + '\\n';
|
479 |
-
} else {
|
480 |
-
interimTranscript += transcript;
|
481 |
-
}
|
482 |
-
}
|
483 |
-
|
484 |
-
if (finalTranscript || (Date.now() - lastUpdateTime > 5000)) {
|
485 |
-
if (finalTranscript) fullTranscript += finalTranscript;
|
486 |
-
lastUpdateTime = Date.now();
|
487 |
-
output.textContent = fullTranscript + (interimTranscript ? '... ' + interimTranscript : '');
|
488 |
-
output.scrollTop = output.scrollHeight;
|
489 |
-
sendDataToPython({value: fullTranscript, dataType: "json"});
|
490 |
-
}
|
491 |
-
};
|
492 |
-
|
493 |
-
recognition.onend = () => {
|
494 |
-
if (!stopButton.disabled) {
|
495 |
-
try {
|
496 |
-
recognition.start();
|
497 |
-
console.log('Restarted recognition');
|
498 |
-
} catch (e) {
|
499 |
-
console.error('Failed to restart:', e);
|
500 |
-
status.textContent = 'Error restarting: ' + e.message;
|
501 |
-
startButton.disabled = false;
|
502 |
-
stopButton.disabled = true;
|
503 |
-
}
|
504 |
-
}
|
505 |
-
};
|
506 |
-
|
507 |
-
recognition.onerror = (event) => {
|
508 |
-
console.error('Recognition error:', event.error);
|
509 |
-
status.textContent = 'Error: ' + event.error;
|
510 |
-
if (event.error === 'not-allowed' || event.error === 'service-not-allowed') {
|
511 |
-
startButton.disabled = false;
|
512 |
-
stopButton.disabled = true;
|
513 |
-
}
|
514 |
-
};
|
515 |
-
}
|
516 |
-
|
517 |
-
function sendDataToPython(data) {
|
518 |
-
window.parent.postMessage({
|
519 |
-
isStreamlitMessage: true,
|
520 |
-
type: "streamlit:setComponentValue",
|
521 |
-
...data
|
522 |
-
}, "*");
|
523 |
-
}
|
524 |
-
|
525 |
-
window.addEventListener('load', () => {
|
526 |
-
window.setTimeout(() => {
|
527 |
-
window.parent.postMessage({
|
528 |
-
isStreamlitMessage: true,
|
529 |
-
type: "streamlit:setFrameHeight",
|
530 |
-
height: document.documentElement.clientHeight
|
531 |
-
}, "*");
|
532 |
-
}, 0);
|
533 |
-
});
|
534 |
-
</script>
|
535 |
-
</body>
|
536 |
-
</html>
|
537 |
-
"""
|
538 |
-
|
539 |
# Main execution
|
540 |
def main():
|
541 |
NODE_NAME, port = get_node_name()
|
@@ -578,10 +444,12 @@ def main():
|
|
578 |
await save_chat_entry(st.session_state.username, message, is_markdown=True)
|
579 |
st.rerun()
|
580 |
|
|
|
581 |
st.subheader("🎤 Continuous Speech Input")
|
582 |
-
|
583 |
-
|
584 |
-
|
|
|
585 |
if transcript and transcript != st.session_state.last_transcript:
|
586 |
await save_chat_entry(st.session_state.username, transcript, is_markdown=True)
|
587 |
st.session_state.last_transcript = transcript
|
|
|
89 |
# Unicode digits
|
90 |
UNICODE_DIGITS = {i: f"{i}\uFE0F⃣" for i in range(10)}
|
91 |
|
92 |
+
# Unicode fonts (simplified for brevity)
|
93 |
UNICODE_FONTS = [
|
94 |
("Normal", lambda x: x),
|
95 |
("Bold", lambda x: "".join(chr(ord(c) + 0x1D400 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D41A - 0x61) if 'a' <= c <= 'z' else c for c in x)),
|
96 |
+
# Add other font styles as needed...
|
97 |
]
|
98 |
|
99 |
# Global state
|
|
|
390 |
return '_'.join(text.split())[:200]
|
391 |
|
392 |
def parse_arxiv_refs(ref_text):
|
|
|
393 |
return [{"title": line.strip(), "url": "", "authors": "", "summary": "", "full_audio": None, "download_base64": ""} for line in ref_text.split('\n') if line.strip()]
|
394 |
|
395 |
async def create_paper_audio_files(papers, input_question):
|
|
|
402 |
b64 = base64.b64encode(f.read()).decode()
|
403 |
paper['download_base64'] = f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(audio_file)}">🎵 Download</a>'
|
404 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
# Main execution
|
406 |
def main():
|
407 |
NODE_NAME, port = get_node_name()
|
|
|
444 |
await save_chat_entry(st.session_state.username, message, is_markdown=True)
|
445 |
st.rerun()
|
446 |
|
447 |
+
# Speech intake using the custom component
|
448 |
st.subheader("🎤 Continuous Speech Input")
|
449 |
+
from mycomponent import speech_component # Import the component
|
450 |
+
transcript_data = speech_component(default_value=st.session_state.get('last_transcript', ''))
|
451 |
+
if transcript_data and 'value' in transcript_data:
|
452 |
+
transcript = transcript_data['value'].strip()
|
453 |
if transcript and transcript != st.session_state.last_transcript:
|
454 |
await save_chat_entry(st.session_state.username, transcript, is_markdown=True)
|
455 |
st.session_state.last_transcript = transcript
|