Spaces:
Sleeping
Sleeping
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()
|