Update pages/2_Consult.py
Browse files- pages/2_Consult.py +92 -100
pages/2_Consult.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import streamlit as st
|
2 |
from langchain.schema import HumanMessage, AIMessage, SystemMessage
|
3 |
from datetime import datetime
|
4 |
-
from typing import List, Union, Optional
|
5 |
from sqlmodel import select
|
6 |
|
7 |
from config.settings import settings
|
@@ -20,56 +20,51 @@ if not st.session_state.get("authenticated_user_id"):
|
|
20 |
st.info("Please navigate to the main login page.")
|
21 |
st.stop()
|
22 |
|
|
|
23 |
authenticated_user_id = st.session_state["authenticated_user_id"]
|
24 |
authenticated_username = st.session_state.get("authenticated_username", "User")
|
25 |
app_logger.info(f"User '{authenticated_username}' (ID: {authenticated_user_id}) accessed Consult page.")
|
26 |
|
27 |
-
# --- Initialize Agent ---
|
28 |
try:
|
29 |
agent_executor = get_agent_executor()
|
30 |
app_logger.info("Gemini-based agent executor initialized for Consult page.")
|
31 |
except ValueError as e:
|
32 |
st.error(f"AI Agent Initialization Error: {e}")
|
33 |
-
app_logger.critical(f"Fatal: AI Agent
|
34 |
-
st.info("
|
35 |
st.stop()
|
36 |
except Exception as e:
|
37 |
st.error(f"Unexpected error initializing AI Agent: {e}")
|
38 |
-
app_logger.critical(f"Fatal: Unexpected AI Agent
|
39 |
st.stop()
|
40 |
|
41 |
# --- Session State Setup ---
|
42 |
-
|
43 |
-
|
44 |
-
if 'consult_context_submitted' not in st.session_state:
|
45 |
-
st.session_state.consult_context_submitted = False
|
46 |
|
47 |
-
# --- Caching helper to load history ---
|
48 |
@st.cache_data(ttl=30, show_spinner=False)
|
49 |
-
def
|
50 |
-
|
51 |
-
|
|
|
|
|
52 |
with get_session_context() as db:
|
53 |
-
|
54 |
-
for msg in db.exec(
|
55 |
if msg.role == "user":
|
56 |
-
|
57 |
elif msg.role == "assistant":
|
58 |
-
|
59 |
elif msg.role == "system":
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
content: str,
|
69 |
-
tool_call_id: Optional[str] = None,
|
70 |
-
tool_name: Optional[str] = None,
|
71 |
-
):
|
72 |
-
app_logger.debug(f"Saving message to DB for session {session_id}: Role={role}")
|
73 |
with get_session_context() as db:
|
74 |
obj = ChatMessage(
|
75 |
session_id=session_id,
|
@@ -80,80 +75,79 @@ def save_chat_message_to_db(
|
|
80 |
tool_name=tool_name,
|
81 |
)
|
82 |
db.add(obj)
|
83 |
-
app_logger.info(f"
|
84 |
|
85 |
|
86 |
-
def
|
|
|
|
|
|
|
87 |
with get_session_context() as db:
|
88 |
session = db.get(ChatSession, session_id)
|
89 |
if session:
|
90 |
session.patient_context_summary = summary
|
91 |
db.add(session)
|
92 |
-
app_logger.info(f"Updated
|
93 |
else:
|
94 |
-
app_logger.error(f"ChatSession {session_id} not found
|
95 |
|
96 |
-
# --- Page
|
97 |
st.title("AI Consultation Room")
|
98 |
-
st.markdown(f"
|
99 |
-
st.info(f"{settings.MAIN_DISCLAIMER_SHORT}
|
100 |
|
101 |
-
# ---
|
102 |
chat_session_id = st.session_state.get("current_chat_session_id")
|
103 |
if not chat_session_id:
|
104 |
-
st.error("No active chat session
|
105 |
-
app_logger.error("
|
106 |
st.stop()
|
107 |
|
108 |
-
# --- Patient Context
|
109 |
if not st.session_state.consult_context_submitted:
|
110 |
st.subheader("Step 1: Provide Patient Context (Optional)")
|
111 |
-
with st.form("
|
112 |
-
age = st.number_input("Patient Age (
|
113 |
-
gender = st.selectbox("Patient Gender (
|
114 |
-
complaint = st.text_area("Chief Complaint", height=80
|
115 |
-
history = st.text_area("Key Medical History", height=80
|
116 |
-
meds = st.text_area("Current Medications", height=80
|
117 |
submit = st.form_submit_button("Start Consult")
|
118 |
|
119 |
if submit:
|
120 |
-
raw = {"
|
121 |
-
filtered = {k
|
122 |
-
st.session_state.
|
123 |
-
|
124 |
-
summary = (
|
125 |
-
|
126 |
-
|
127 |
-
"No specific patient context provided."
|
128 |
-
)
|
129 |
-
update_chat_session_with_context_summary_in_db(chat_session_id, summary)
|
130 |
-
save_chat_message_to_db(chat_session_id, "system", f"Context: {summary}")
|
131 |
st.session_state.consult_context_submitted = True
|
132 |
-
app_logger.info(f"
|
133 |
-
st.
|
134 |
st.stop()
|
135 |
|
136 |
-
# --- Chat
|
137 |
-
st.subheader("Step 2: Interact with AI
|
138 |
-
history_key = f"
|
139 |
if history_key not in st.session_state:
|
140 |
-
st.session_state[history_key] =
|
141 |
if not st.session_state[history_key]:
|
142 |
try:
|
143 |
log_consultation_start(authenticated_user_id, chat_session_id)
|
144 |
-
except
|
145 |
-
|
146 |
-
greeting = "Hello! I
|
147 |
-
if st.session_state.
|
148 |
-
greeting += "
|
149 |
st.session_state[history_key].append(AIMessage(content=greeting))
|
150 |
-
|
151 |
|
152 |
-
# Display
|
153 |
with st.container(height=400):
|
154 |
with get_session_context() as db:
|
155 |
stmt = select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)
|
156 |
-
for msg in db.exec(stmt)
|
157 |
if msg.role == "system":
|
158 |
continue
|
159 |
avatar = "π§ββοΈ" if msg.role == "assistant" else "π€"
|
@@ -162,40 +156,38 @@ with st.container(height=400):
|
|
162 |
with st.chat_message(msg.role, avatar=avatar):
|
163 |
st.markdown(msg.content)
|
164 |
|
165 |
-
# Capture user input
|
166 |
-
if
|
167 |
with st.chat_message("user", avatar="π€"):
|
168 |
-
st.markdown(
|
169 |
-
|
170 |
-
st.session_state[history_key].append(HumanMessage(content=
|
171 |
|
172 |
with st.chat_message("assistant", avatar="π§ββοΈ"):
|
173 |
with st.spinner("AI is thinking..."):
|
174 |
try:
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
pc_str = "No specific patient context provided."
|
180 |
|
181 |
payload = {
|
182 |
-
"input":
|
183 |
"chat_history": st.session_state[history_key],
|
184 |
-
"patient_context":
|
185 |
}
|
186 |
-
app_logger.debug(f"Invoking agent
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
st.error(
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
st.rerun()
|
|
|
1 |
import streamlit as st
|
2 |
from langchain.schema import HumanMessage, AIMessage, SystemMessage
|
3 |
from datetime import datetime
|
4 |
+
from typing import List, Union, Optional
|
5 |
from sqlmodel import select
|
6 |
|
7 |
from config.settings import settings
|
|
|
20 |
st.info("Please navigate to the main login page.")
|
21 |
st.stop()
|
22 |
|
23 |
+
# Retrieve authenticated user info
|
24 |
authenticated_user_id = st.session_state["authenticated_user_id"]
|
25 |
authenticated_username = st.session_state.get("authenticated_username", "User")
|
26 |
app_logger.info(f"User '{authenticated_username}' (ID: {authenticated_user_id}) accessed Consult page.")
|
27 |
|
28 |
+
# --- Initialize AI Agent ---
|
29 |
try:
|
30 |
agent_executor = get_agent_executor()
|
31 |
app_logger.info("Gemini-based agent executor initialized for Consult page.")
|
32 |
except ValueError as e:
|
33 |
st.error(f"AI Agent Initialization Error: {e}")
|
34 |
+
app_logger.critical(f"Fatal: AI Agent init failed: {e}", exc_info=True)
|
35 |
+
st.info("Ensure API keys (e.g., Gemini) are configured in settings or environment.")
|
36 |
st.stop()
|
37 |
except Exception as e:
|
38 |
st.error(f"Unexpected error initializing AI Agent: {e}")
|
39 |
+
app_logger.critical(f"Fatal: Unexpected AI Agent init error: {e}", exc_info=True)
|
40 |
st.stop()
|
41 |
|
42 |
# --- Session State Setup ---
|
43 |
+
st.session_state.setdefault('current_consult_patient_context', {})
|
44 |
+
st.session_state.setdefault('consult_context_submitted', False)
|
|
|
|
|
45 |
|
|
|
46 |
@st.cache_data(ttl=30, show_spinner=False)
|
47 |
+
def load_chat_history(session_id: int) -> List[Union[HumanMessage, AIMessage, SystemMessage]]:
|
48 |
+
"""
|
49 |
+
Load chat history from the database and convert to LangChain messages.
|
50 |
+
"""
|
51 |
+
history: List[Union[HumanMessage, AIMessage, SystemMessage]] = []
|
52 |
with get_session_context() as db:
|
53 |
+
stmt = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
|
54 |
+
for msg in db.exec(stmt).all():
|
55 |
if msg.role == "user":
|
56 |
+
history.append(HumanMessage(content=msg.content))
|
57 |
elif msg.role == "assistant":
|
58 |
+
history.append(AIMessage(content=msg.content))
|
59 |
elif msg.role == "system":
|
60 |
+
history.append(SystemMessage(content=msg.content))
|
61 |
+
return history
|
62 |
+
|
63 |
+
|
64 |
+
def save_message(session_id: int, role: str, content: str, tool_call_id: Optional[str] = None, tool_name: Optional[str] = None):
|
65 |
+
"""
|
66 |
+
Persist a chat message to the database.
|
67 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
68 |
with get_session_context() as db:
|
69 |
obj = ChatMessage(
|
70 |
session_id=session_id,
|
|
|
75 |
tool_name=tool_name,
|
76 |
)
|
77 |
db.add(obj)
|
78 |
+
app_logger.info(f"Saved {role} message to DB for session {session_id}.")
|
79 |
|
80 |
|
81 |
+
def update_session_context(session_id: int, summary: str):
|
82 |
+
"""
|
83 |
+
Update the ChatSession record with a summary of patient context.
|
84 |
+
"""
|
85 |
with get_session_context() as db:
|
86 |
session = db.get(ChatSession, session_id)
|
87 |
if session:
|
88 |
session.patient_context_summary = summary
|
89 |
db.add(session)
|
90 |
+
app_logger.info(f"Updated session {session_id} with context summary.")
|
91 |
else:
|
92 |
+
app_logger.error(f"ChatSession {session_id} not found.")
|
93 |
|
94 |
+
# --- Page Header ---
|
95 |
st.title("AI Consultation Room")
|
96 |
+
st.markdown(f"**User:** {authenticated_username}")
|
97 |
+
st.info(f"{settings.MAIN_DISCLAIMER_SHORT} Use only anonymized, simulated data.")
|
98 |
|
99 |
+
# --- Ensure Chat Session Exists ---
|
100 |
chat_session_id = st.session_state.get("current_chat_session_id")
|
101 |
if not chat_session_id:
|
102 |
+
st.error("No active chat session. Please log out and log back in.")
|
103 |
+
app_logger.error("Missing current_chat_session_id in session_state.")
|
104 |
st.stop()
|
105 |
|
106 |
+
# --- Step 1: Patient Context Input ---
|
107 |
if not st.session_state.consult_context_submitted:
|
108 |
st.subheader("Step 1: Provide Patient Context (Optional)")
|
109 |
+
with st.form("context_form"):
|
110 |
+
age = st.number_input("Patient Age (Sim)", min_value=0, max_value=120, step=1)
|
111 |
+
gender = st.selectbox("Patient Gender (Sim)", ["Not Specified", "Male", "Female", "Other"], index=0)
|
112 |
+
complaint = st.text_area("Chief Complaint (Sim)", height=80)
|
113 |
+
history = st.text_area("Key Medical History (Sim)", height=80)
|
114 |
+
meds = st.text_area("Current Medications (Sim)", height=80)
|
115 |
submit = st.form_submit_button("Start Consult")
|
116 |
|
117 |
if submit:
|
118 |
+
raw = {"Age": age, "Gender": gender, "Complaint": complaint.strip(), "History": history.strip(), "Meds": meds.strip()}
|
119 |
+
filtered = {k: v for k, v in raw.items() if v and not (isinstance(v, str) and v == "Not Specified")}
|
120 |
+
st.session_state.current_consult_patient_context = filtered
|
121 |
+
|
122 |
+
summary = "; ".join(f"{k}: {v}" for k, v in filtered.items()) if filtered else "No patient context provided."
|
123 |
+
update_session_context(chat_session_id, summary)
|
124 |
+
save_message(chat_session_id, "system", f"Context: {summary}")
|
|
|
|
|
|
|
|
|
125 |
st.session_state.consult_context_submitted = True
|
126 |
+
app_logger.info(f"Context for session {chat_session_id}: {summary}")
|
127 |
+
st.experimental_rerun()
|
128 |
st.stop()
|
129 |
|
130 |
+
# --- Step 2: Chat Interface ---
|
131 |
+
st.subheader("Step 2: Interact with the AI")
|
132 |
+
history_key = f"history_{chat_session_id}"
|
133 |
if history_key not in st.session_state:
|
134 |
+
st.session_state[history_key] = load_chat_history(chat_session_id)
|
135 |
if not st.session_state[history_key]:
|
136 |
try:
|
137 |
log_consultation_start(authenticated_user_id, chat_session_id)
|
138 |
+
except:
|
139 |
+
pass
|
140 |
+
greeting = "Hello! I'm your AI Health Navigator. How can I assist today?"
|
141 |
+
if st.session_state.current_consult_patient_context:
|
142 |
+
greeting += " (Patient context noted.)"
|
143 |
st.session_state[history_key].append(AIMessage(content=greeting))
|
144 |
+
save_message(chat_session_id, "assistant", greeting)
|
145 |
|
146 |
+
# Display chat messages
|
147 |
with st.container(height=400):
|
148 |
with get_session_context() as db:
|
149 |
stmt = select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)
|
150 |
+
for msg in db.exec(stmt):
|
151 |
if msg.role == "system":
|
152 |
continue
|
153 |
avatar = "π§ββοΈ" if msg.role == "assistant" else "π€"
|
|
|
156 |
with st.chat_message(msg.role, avatar=avatar):
|
157 |
st.markdown(msg.content)
|
158 |
|
159 |
+
# Capture user input
|
160 |
+
if user_input := st.chat_input("Ask the AI..."):
|
161 |
with st.chat_message("user", avatar="π€"):
|
162 |
+
st.markdown(user_input)
|
163 |
+
save_message(chat_session_id, "user", user_input)
|
164 |
+
st.session_state[history_key].append(HumanMessage(content=user_input))
|
165 |
|
166 |
with st.chat_message("assistant", avatar="π§ββοΈ"):
|
167 |
with st.spinner("AI is thinking..."):
|
168 |
try:
|
169 |
+
# Prepare invocation payload
|
170 |
+
context_str = "; ".join(f"{k}: {v}" for k, v in st.session_state.current_consult_patient_context.items())
|
171 |
+
if not context_str:
|
172 |
+
context_str = "No patient context provided."
|
|
|
173 |
|
174 |
payload = {
|
175 |
+
"input": user_input,
|
176 |
"chat_history": st.session_state[history_key],
|
177 |
+
"patient_context": context_str,
|
178 |
}
|
179 |
+
app_logger.debug(f"Invoking agent: {payload}")
|
180 |
+
result = agent_executor.invoke(payload)
|
181 |
+
output = str(result.get("output", "No response generated."))
|
182 |
+
|
183 |
+
app_logger.info(f"Agent output: {output[:100]}")
|
184 |
+
st.markdown(output)
|
185 |
+
save_message(chat_session_id, "assistant", output)
|
186 |
+
st.session_state[history_key].append(AIMessage(content=output))
|
187 |
+
except Exception as e:
|
188 |
+
app_logger.error(f"Error during agent invocation: {e}", exc_info=True)
|
189 |
+
error_msg = f"Sorry, an error occurred ({type(e).__name__})."
|
190 |
+
st.error(error_msg)
|
191 |
+
save_message(chat_session_id, "assistant", error_msg)
|
192 |
+
st.session_state[history_key].append(AIMessage(content=error_msg))
|
193 |
+
st.experimental_rerun()
|
|