Update app.py
Browse files
app.py
CHANGED
@@ -127,7 +127,7 @@ def init_session_state():
|
|
127 |
'autosend': True, 'autosearch': True, 'last_message': "", 'last_query': "",
|
128 |
'mp3_files': {}, 'timer_start': time.time(), 'quote_index': 0,
|
129 |
'quote_source': "famous", 'last_sent_transcript': "", 'old_val': None,
|
130 |
-
'last_refresh': time.time(), 'paper_metadata': {}, 'paste_image_base64':
|
131 |
}
|
132 |
for k, v in defaults.items():
|
133 |
if k not in st.session_state:
|
@@ -282,7 +282,6 @@ async def perform_claude_search(query, username, image=None):
|
|
282 |
client = anthropic.Anthropic(api_key=anthropic_key)
|
283 |
message_content = [{"type": "text", "text": query}]
|
284 |
if image:
|
285 |
-
# Convert PIL Image to base64
|
286 |
buffered = io.BytesIO()
|
287 |
image.save(buffered, format="PNG")
|
288 |
img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
|
@@ -565,49 +564,43 @@ def create_zip_of_files(md_files, mp3_files, png_files, mp4_files, query):
|
|
565 |
|
566 |
# Custom Paste Image Component
|
567 |
def paste_image_component():
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
img_bytes = base64.b64decode(base64_str)
|
606 |
-
img = Image.open(io.BytesIO(img_bytes))
|
607 |
-
return img
|
608 |
-
except Exception as e:
|
609 |
-
st.warning(f"Error decoding pasted image: {e}")
|
610 |
-
return None
|
611 |
return None
|
612 |
|
613 |
# ๐ฎ Main Interface
|
@@ -641,10 +634,10 @@ def main():
|
|
641 |
st.rerun()
|
642 |
|
643 |
tab_main = st.radio("Action:", ["๐ค Chat & Voice", "๐ ArXiv", "๐ PDF to Audio"], horizontal=True, key="tab_main")
|
644 |
-
|
645 |
-
|
646 |
-
st.checkbox("Autosend Chat", value=
|
647 |
-
st.checkbox("Autosearch ArXiv", value=
|
648 |
|
649 |
# ๐ค Chat & Voice
|
650 |
if tab_main == "๐ค Chat & Voice":
|
@@ -659,8 +652,8 @@ def main():
|
|
659 |
|
660 |
# Custom Paste Image Button
|
661 |
pasted_image = paste_image_component()
|
662 |
-
if pasted_image is not None and st.session_state['paste_image_base64'] != pasted_image.tobytes():
|
663 |
-
st.session_state['paste_image_base64'] = pasted_image.tobytes()
|
664 |
voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
|
665 |
st.image(pasted_image, caption="Pasted Image")
|
666 |
filename = asyncio.run(save_pasted_image(pasted_image, st.session_state.username))
|
@@ -679,7 +672,7 @@ def main():
|
|
679 |
if audio_file_arxiv:
|
680 |
play_and_download_audio(audio_file_arxiv)
|
681 |
st.session_state.pasted_image_data = None
|
682 |
-
st.session_state['paste_image_base64'] =
|
683 |
st.session_state.timer_start = time.time()
|
684 |
save_username(st.session_state.username)
|
685 |
st.rerun()
|
@@ -731,7 +724,7 @@ def main():
|
|
731 |
if q and q != st.session_state.last_query:
|
732 |
st.session_state.last_query = q
|
733 |
if st.session_state.autosearch or st.button("๐ Run", key="arxiv_run"):
|
734 |
-
result, papers = asyncio.run(perform_ai_lookup(q, useArxiv=
|
735 |
st.markdown(f"### Query: {q}")
|
736 |
for i, p in enumerate(papers, 1):
|
737 |
expander_label = f"{p['title']} | [arXiv Link]({p['url']})"
|
@@ -874,7 +867,7 @@ def main():
|
|
874 |
else:
|
875 |
timer_placeholder.markdown(f"<p class='timer'>โณ Next refresh in: {remaining_time} seconds</p>", unsafe_allow_html=True)
|
876 |
|
877 |
-
# Start WebSocket server in a
|
878 |
if not st.session_state.server_running and not st.session_state.server_task:
|
879 |
st.session_state.server_task = threading.Thread(target=start_websocket_server, daemon=True)
|
880 |
st.session_state.server_task.start()
|
|
|
127 |
'autosend': True, 'autosearch': True, 'last_message': "", 'last_query': "",
|
128 |
'mp3_files': {}, 'timer_start': time.time(), 'quote_index': 0,
|
129 |
'quote_source': "famous", 'last_sent_transcript': "", 'old_val': None,
|
130 |
+
'last_refresh': time.time(), 'paper_metadata': {}, 'paste_image_base64': ""
|
131 |
}
|
132 |
for k, v in defaults.items():
|
133 |
if k not in st.session_state:
|
|
|
282 |
client = anthropic.Anthropic(api_key=anthropic_key)
|
283 |
message_content = [{"type": "text", "text": query}]
|
284 |
if image:
|
|
|
285 |
buffered = io.BytesIO()
|
286 |
image.save(buffered, format="PNG")
|
287 |
img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
|
|
|
564 |
|
565 |
# Custom Paste Image Component
|
566 |
def paste_image_component():
|
567 |
+
with st.form(key="paste_form"):
|
568 |
+
# Hidden text input to receive clipboard data
|
569 |
+
paste_input = st.text_input("Paste Base64 Image Here (hidden)", value="", key="paste_input", label_visibility="collapsed")
|
570 |
+
# JavaScript to paste clipboard content into the input
|
571 |
+
st.markdown("""
|
572 |
+
<script>
|
573 |
+
function pasteClipboard() {
|
574 |
+
navigator.clipboard.readText().then(text => {
|
575 |
+
document.getElementById('paste_input').value = text;
|
576 |
+
document.getElementById('paste_form').requestSubmit();
|
577 |
+
}).catch(err => {
|
578 |
+
console.error('Failed to read clipboard: ', err);
|
579 |
+
document.getElementById('paste_input').value = 'ERROR: ' + err.message;
|
580 |
+
document.getElementById('paste_form').requestSubmit();
|
581 |
+
});
|
582 |
+
}
|
583 |
+
</script>
|
584 |
+
""", unsafe_allow_html=True)
|
585 |
+
# Button to trigger paste
|
586 |
+
paste_button = st.form_submit_button("Paste Image ๐", on_click=lambda: st.markdown("<script>pasteClipboard();</script>", unsafe_allow_html=True))
|
587 |
+
|
588 |
+
if paste_button and paste_input:
|
589 |
+
if paste_input.startswith('ERROR:'):
|
590 |
+
st.warning(f"Paste failed: {paste_input}")
|
591 |
+
return None
|
592 |
+
if paste_input.startswith('data:image'):
|
593 |
+
try:
|
594 |
+
base64_str = paste_input.split(',')[1]
|
595 |
+
img_bytes = base64.b64decode(base64_str)
|
596 |
+
img = Image.open(io.BytesIO(img_bytes))
|
597 |
+
return img
|
598 |
+
except Exception as e:
|
599 |
+
st.warning(f"Error decoding pasted image: {e}")
|
600 |
+
return None
|
601 |
+
else:
|
602 |
+
st.warning("Clipboard does not contain a valid image (expected base64 data:image)")
|
603 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
604 |
return None
|
605 |
|
606 |
# ๐ฎ Main Interface
|
|
|
634 |
st.rerun()
|
635 |
|
636 |
tab_main = st.radio("Action:", ["๐ค Chat & Voice", "๐ ArXiv", "๐ PDF to Audio"], horizontal=True, key="tab_main")
|
637 |
+
st.checkbox("Search ArXiv", value=st.session_state['use_arxiv'], key="use_arxiv")
|
638 |
+
st.checkbox("ArXiv Audio", value=st.session_state['use_arxiv_audio'], key="use_arxiv_audio")
|
639 |
+
st.checkbox("Autosend Chat", value=st.session_state['autosend'], key="autosend")
|
640 |
+
st.checkbox("Autosearch ArXiv", value=st.session_state['autosearch'], key="autosearch")
|
641 |
|
642 |
# ๐ค Chat & Voice
|
643 |
if tab_main == "๐ค Chat & Voice":
|
|
|
652 |
|
653 |
# Custom Paste Image Button
|
654 |
pasted_image = paste_image_component()
|
655 |
+
if pasted_image is not None and st.session_state['paste_image_base64'] != base64.b64encode(pasted_image.tobytes()).decode('utf-8'):
|
656 |
+
st.session_state['paste_image_base64'] = base64.b64encode(pasted_image.tobytes()).decode('utf-8')
|
657 |
voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
|
658 |
st.image(pasted_image, caption="Pasted Image")
|
659 |
filename = asyncio.run(save_pasted_image(pasted_image, st.session_state.username))
|
|
|
672 |
if audio_file_arxiv:
|
673 |
play_and_download_audio(audio_file_arxiv)
|
674 |
st.session_state.pasted_image_data = None
|
675 |
+
st.session_state['paste_image_base64'] = ""
|
676 |
st.session_state.timer_start = time.time()
|
677 |
save_username(st.session_state.username)
|
678 |
st.rerun()
|
|
|
724 |
if q and q != st.session_state.last_query:
|
725 |
st.session_state.last_query = q
|
726 |
if st.session_state.autosearch or st.button("๐ Run", key="arxiv_run"):
|
727 |
+
result, papers = asyncio.run(perform_ai_lookup(q, useArxiv=st.session_state['use_arxiv'], useArxivAudio=st.session_state['use_arxiv_audio']))
|
728 |
st.markdown(f"### Query: {q}")
|
729 |
for i, p in enumerate(papers, 1):
|
730 |
expander_label = f"{p['title']} | [arXiv Link]({p['url']})"
|
|
|
867 |
else:
|
868 |
timer_placeholder.markdown(f"<p class='timer'>โณ Next refresh in: {remaining_time} seconds</p>", unsafe_allow_html=True)
|
869 |
|
870 |
+
# Start WebSocket server in a thread
|
871 |
if not st.session_state.server_running and not st.session_state.server_task:
|
872 |
st.session_state.server_task = threading.Thread(target=start_websocket_server, daemon=True)
|
873 |
st.session_state.server_task.start()
|