Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,6 +2,9 @@ import streamlit as st
|
|
2 |
import os
|
3 |
import time
|
4 |
import re
|
|
|
|
|
|
|
5 |
from openai import OpenAI
|
6 |
|
7 |
# ------------------ App Configuration ------------------
|
@@ -13,7 +16,6 @@ st.caption("Chat with an AI Assistant on your medical/pathology documents")
|
|
13 |
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
|
14 |
ASSISTANT_ID = os.environ.get("ASSISTANT_ID")
|
15 |
|
16 |
-
# ------------------ Error Handling for Missing Secrets ------------------
|
17 |
if not OPENAI_API_KEY or not ASSISTANT_ID:
|
18 |
st.error("Missing secrets. Please ensure both OPENAI_API_KEY and ASSISTANT_ID are set in your Hugging Face Space secrets.")
|
19 |
st.stop()
|
@@ -41,17 +43,27 @@ if st.sidebar.button("π Clear Chat"):
|
|
41 |
|
42 |
show_image = st.sidebar.checkbox("π Show Document Image", value=True)
|
43 |
|
44 |
-
# ------------------
|
45 |
-
|
|
|
46 |
|
47 |
-
# ------------------
|
48 |
-
|
|
|
|
|
|
|
|
|
49 |
if show_image and st.session_state.image_url:
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
52 |
|
53 |
-
# ------------------ Chat
|
54 |
-
with
|
|
|
55 |
for message in st.session_state.messages:
|
56 |
role, content = message["role"], message["content"]
|
57 |
st.chat_message(role).write(content)
|
@@ -61,14 +73,13 @@ with col2:
|
|
61 |
st.chat_message("user").write(prompt)
|
62 |
|
63 |
try:
|
64 |
-
# Initialize thread if needed
|
65 |
if st.session_state.thread_id is None:
|
66 |
thread = client.beta.threads.create()
|
67 |
st.session_state.thread_id = thread.id
|
68 |
|
69 |
thread_id = st.session_state.thread_id
|
70 |
|
71 |
-
# Send
|
72 |
client.beta.threads.messages.create(
|
73 |
thread_id=thread_id,
|
74 |
role="user",
|
@@ -81,7 +92,7 @@ with col2:
|
|
81 |
assistant_id=ASSISTANT_ID
|
82 |
)
|
83 |
|
84 |
-
#
|
85 |
with st.spinner("Assistant is thinking..."):
|
86 |
while True:
|
87 |
run_status = client.beta.threads.runs.retrieve(
|
@@ -92,7 +103,7 @@ with col2:
|
|
92 |
break
|
93 |
time.sleep(1)
|
94 |
|
95 |
-
# Get assistant
|
96 |
messages = client.beta.threads.messages.list(thread_id=thread_id)
|
97 |
assistant_message = None
|
98 |
for message in reversed(messages.data):
|
@@ -103,7 +114,7 @@ with col2:
|
|
103 |
st.chat_message("assistant").write(assistant_message)
|
104 |
st.session_state.messages.append({"role": "assistant", "content": assistant_message})
|
105 |
|
106 |
-
#
|
107 |
image_match = re.search(
|
108 |
r'https://raw\.githubusercontent\.com/AndrewLORTech/surgical-pathology-manual/main/[\w\-/]*\.png',
|
109 |
assistant_message
|
@@ -111,7 +122,28 @@ with col2:
|
|
111 |
if image_match:
|
112 |
st.session_state.image_url = image_match.group(0)
|
113 |
st.session_state.image_updated = True
|
114 |
-
st.rerun()
|
115 |
|
116 |
except Exception as e:
|
117 |
st.error(f"β Error: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import os
|
3 |
import time
|
4 |
import re
|
5 |
+
import json
|
6 |
+
import requests
|
7 |
+
from PIL import Image
|
8 |
from openai import OpenAI
|
9 |
|
10 |
# ------------------ App Configuration ------------------
|
|
|
16 |
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
|
17 |
ASSISTANT_ID = os.environ.get("ASSISTANT_ID")
|
18 |
|
|
|
19 |
if not OPENAI_API_KEY or not ASSISTANT_ID:
|
20 |
st.error("Missing secrets. Please ensure both OPENAI_API_KEY and ASSISTANT_ID are set in your Hugging Face Space secrets.")
|
21 |
st.stop()
|
|
|
43 |
|
44 |
show_image = st.sidebar.checkbox("π Show Document Image", value=True)
|
45 |
|
46 |
+
# ------------------ Load Structured Summary/FAQ ------------------
|
47 |
+
with open("51940670-Manual-of-Surgical-Pathology-Third-Edition_1_structured_output.json", "r") as f:
|
48 |
+
structured_data = json.load(f)
|
49 |
|
50 |
+
# ------------------ Three-Column Layout ------------------
|
51 |
+
left, center, right = st.columns([1, 2, 1]) # adjust as needed
|
52 |
+
|
53 |
+
# ------------------ Left Column: Document Image ------------------
|
54 |
+
with left:
|
55 |
+
st.subheader("π Document Image")
|
56 |
if show_image and st.session_state.image_url:
|
57 |
+
try:
|
58 |
+
image = Image.open(requests.get(st.session_state.image_url, stream=True).raw)
|
59 |
+
st.image(image, caption="π Extracted Page", use_column_width=True)
|
60 |
+
st.session_state.image_updated = False
|
61 |
+
except Exception as e:
|
62 |
+
st.warning("β οΈ Could not load image.")
|
63 |
|
64 |
+
# ------------------ Center Column: Chat UI ------------------
|
65 |
+
with center:
|
66 |
+
st.subheader("π¬ Document AI Assistant")
|
67 |
for message in st.session_state.messages:
|
68 |
role, content = message["role"], message["content"]
|
69 |
st.chat_message(role).write(content)
|
|
|
73 |
st.chat_message("user").write(prompt)
|
74 |
|
75 |
try:
|
|
|
76 |
if st.session_state.thread_id is None:
|
77 |
thread = client.beta.threads.create()
|
78 |
st.session_state.thread_id = thread.id
|
79 |
|
80 |
thread_id = st.session_state.thread_id
|
81 |
|
82 |
+
# Send user prompt
|
83 |
client.beta.threads.messages.create(
|
84 |
thread_id=thread_id,
|
85 |
role="user",
|
|
|
92 |
assistant_id=ASSISTANT_ID
|
93 |
)
|
94 |
|
95 |
+
# Poll until done
|
96 |
with st.spinner("Assistant is thinking..."):
|
97 |
while True:
|
98 |
run_status = client.beta.threads.runs.retrieve(
|
|
|
103 |
break
|
104 |
time.sleep(1)
|
105 |
|
106 |
+
# Get assistant message
|
107 |
messages = client.beta.threads.messages.list(thread_id=thread_id)
|
108 |
assistant_message = None
|
109 |
for message in reversed(messages.data):
|
|
|
114 |
st.chat_message("assistant").write(assistant_message)
|
115 |
st.session_state.messages.append({"role": "assistant", "content": assistant_message})
|
116 |
|
117 |
+
# Detect GitHub image in response
|
118 |
image_match = re.search(
|
119 |
r'https://raw\.githubusercontent\.com/AndrewLORTech/surgical-pathology-manual/main/[\w\-/]*\.png',
|
120 |
assistant_message
|
|
|
122 |
if image_match:
|
123 |
st.session_state.image_url = image_match.group(0)
|
124 |
st.session_state.image_updated = True
|
125 |
+
st.rerun()
|
126 |
|
127 |
except Exception as e:
|
128 |
st.error(f"β Error: {str(e)}")
|
129 |
+
|
130 |
+
# ------------------ Right Column: Summary and FAQ ------------------
|
131 |
+
with right:
|
132 |
+
st.subheader("π Summary")
|
133 |
+
|
134 |
+
if st.session_state.image_url:
|
135 |
+
match = re.search(r'page_(\d+)', st.session_state.image_url)
|
136 |
+
page_number = int(match.group(1)) if match else None
|
137 |
+
else:
|
138 |
+
page_number = 151 # default
|
139 |
+
|
140 |
+
summary_text = structured_data.get(str(page_number), {}).get("summary", "No summary available.")
|
141 |
+
st.markdown(summary_text)
|
142 |
+
|
143 |
+
st.subheader("β Auto-Generated FAQ")
|
144 |
+
faq_list = structured_data.get(str(page_number), {}).get("faqs", [])
|
145 |
+
if faq_list:
|
146 |
+
for faq in faq_list:
|
147 |
+
st.markdown(f"**Q:** {faq.get('question', '')}\n\n**A:** {faq.get('answer', '')}")
|
148 |
+
else:
|
149 |
+
st.info("No FAQs available for this page.")
|