Update pages/2_Consult.py
Browse files- pages/2_Consult.py +128 -128
pages/2_Consult.py
CHANGED
@@ -1,12 +1,11 @@
|
|
1 |
-
# /home/user/app/pages/2_Consult.py
|
2 |
import streamlit as st
|
3 |
-
from
|
4 |
from datetime import datetime
|
5 |
-
from typing import List, Optional, Dict, Any
|
6 |
from sqlmodel import select
|
7 |
|
8 |
from config.settings import settings
|
9 |
-
from agent import get_agent_executor
|
10 |
from models import ChatMessage, ChatSession
|
11 |
from models.db import get_session_context
|
12 |
from services.logger import app_logger
|
@@ -21,181 +20,182 @@ if not st.session_state.get("authenticated_user_id"):
|
|
21 |
st.info("Please navigate to the main login page.")
|
22 |
st.stop()
|
23 |
|
24 |
-
authenticated_user_id = st.session_state
|
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 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 could not be initialized
|
35 |
st.info("Please ensure API keys (e.g., Google API Key for Gemini) are configured.")
|
36 |
st.stop()
|
37 |
-
except Exception as e:
|
38 |
-
st.error(f"
|
39 |
app_logger.critical(f"Fatal: Unexpected AI Agent initialization error: {e}", exc_info=True)
|
40 |
st.stop()
|
41 |
|
42 |
-
# --- Session State
|
43 |
-
if 'current_consult_patient_context_dict' not in st.session_state:
|
44 |
st.session_state.current_consult_patient_context_dict = {}
|
45 |
if 'consult_context_submitted' not in st.session_state:
|
46 |
st.session_state.consult_context_submitted = False
|
47 |
|
48 |
-
# ---
|
49 |
@st.cache_data(ttl=30, show_spinner=False)
|
50 |
-
def load_chat_history_for_agent(session_id: int) -> List:
|
51 |
-
messages = []
|
52 |
-
app_logger.debug(f"Loading agent chat history for
|
53 |
with get_session_context() as db:
|
54 |
statement = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
elif msg.role == "assistant":
|
59 |
-
|
60 |
-
|
|
|
|
|
61 |
return messages
|
62 |
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
app_logger.debug(f"Saving message to DB for session {session_id}: Role={role}")
|
65 |
with get_session_context() as db:
|
66 |
-
|
67 |
-
session_id=session_id,
|
68 |
-
|
|
|
|
|
|
|
|
|
69 |
)
|
70 |
-
db.add(
|
71 |
app_logger.info(f"Message saved to DB for session {session_id}. Role: {role}.")
|
72 |
|
73 |
-
|
|
|
74 |
with get_session_context() as db:
|
75 |
-
|
76 |
-
if
|
77 |
-
|
78 |
-
db.add(
|
79 |
-
app_logger.info(f"Updated ChatSession {session_id} with
|
80 |
else:
|
81 |
-
app_logger.error(f"
|
82 |
|
83 |
-
# --- Page
|
84 |
st.title("AI Consultation Room")
|
85 |
st.markdown(f"Interacting as: **{authenticated_username}**")
|
86 |
-
st.info(f"{settings.MAIN_DISCLAIMER_SHORT} Remember to use only anonymized, simulated data
|
87 |
|
|
|
88 |
chat_session_id = st.session_state.get("current_chat_session_id")
|
89 |
if not chat_session_id:
|
90 |
-
st.error("No active chat session
|
91 |
-
app_logger.error(
|
92 |
st.stop()
|
93 |
|
94 |
-
# --- Patient Context
|
95 |
if not st.session_state.consult_context_submitted:
|
96 |
-
st.subheader("Step 1: Provide Patient Context (Optional
|
97 |
-
with st.form(
|
98 |
-
st.
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
else:
|
121 |
-
context_summary_str = "No specific patient context was provided for this session."
|
122 |
-
|
123 |
-
update_chat_session_with_context_summary_in_db(chat_session_id, context_summary_str)
|
124 |
-
save_chat_message_to_db(chat_session_id, "system", f"Initial Patient Context Provided: {context_summary_str}")
|
125 |
-
|
126 |
-
st.session_state.consult_context_submitted = True
|
127 |
-
app_logger.info(f"Patient context submitted for session {chat_session_id}: {context_summary_str}")
|
128 |
-
st.rerun()
|
129 |
st.stop()
|
130 |
|
131 |
-
# --- Chat
|
132 |
st.subheader("Step 2: Interact with AI Health Navigator")
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
st.session_state[
|
137 |
-
|
138 |
-
|
139 |
-
except Exception as
|
140 |
-
|
141 |
-
|
142 |
-
if st.session_state.
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
with st.container(height=400): # Fixed height container for chat, makes it scrollable
|
150 |
with get_session_context() as db:
|
151 |
stmt = select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
avatar = "π§ββοΈ" if msg.role == "assistant" else "π€"
|
156 |
-
if msg.role == "tool":
|
|
|
157 |
with st.chat_message(msg.role, avatar=avatar):
|
158 |
st.markdown(msg.content)
|
159 |
|
160 |
-
#
|
161 |
-
if
|
162 |
-
with st.chat_message("user", avatar="π€"):
|
163 |
-
|
164 |
-
|
|
|
165 |
|
166 |
with st.chat_message("assistant", avatar="π§ββοΈ"):
|
167 |
with st.spinner("AI is thinking..."):
|
168 |
try:
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
context_parts_for_invoke = [f"{k}: {v}" for k, v in patient_context_dict.items()]
|
173 |
-
patient_context_str_for_invoke = "; ".join(context_parts_for_invoke)
|
174 |
else:
|
175 |
-
|
176 |
|
177 |
-
|
178 |
-
"input":
|
179 |
-
"chat_history": st.session_state[
|
180 |
-
"patient_context":
|
181 |
}
|
182 |
-
app_logger.debug(f"Invoking agent with payload: {
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
db_error_msg = f"System encountered an error: {error_type_name}. Details logged."
|
199 |
-
save_chat_message_to_db(chat_session_id, "assistant", db_error_msg)
|
200 |
-
st.session_state[agent_history_key].append(AIMessage(content=f"Note: Encountered error ({error_type_name})."))
|
201 |
-
st.rerun() # Rerun to display the new messages immediately
|
|
|
|
|
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, Dict, Any
|
5 |
from sqlmodel import select
|
6 |
|
7 |
from config.settings import settings
|
8 |
+
from agent import get_agent_executor # Returns the configured Gemini-based agent executor
|
9 |
from models import ChatMessage, ChatSession
|
10 |
from models.db import get_session_context
|
11 |
from services.logger import app_logger
|
|
|
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 could not be initialized: {e}", exc_info=True)
|
34 |
st.info("Please ensure API keys (e.g., Google API Key for Gemini) are configured.")
|
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 initialization error: {e}", exc_info=True)
|
39 |
st.stop()
|
40 |
|
41 |
+
# --- Session State Setup ---
|
42 |
+
if 'current_consult_patient_context_dict' not in st.session_state:
|
43 |
st.session_state.current_consult_patient_context_dict = {}
|
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 load_chat_history_for_agent(session_id: int) -> List[Union[HumanMessage, AIMessage, SystemMessage]]:
|
50 |
+
messages: List[Union[HumanMessage, AIMessage, SystemMessage]] = []
|
51 |
+
app_logger.debug(f"Loading agent chat history for session {session_id}")
|
52 |
with get_session_context() as db:
|
53 |
statement = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
|
54 |
+
for msg in db.exec(statement).all():
|
55 |
+
if msg.role == "user":
|
56 |
+
messages.append(HumanMessage(content=msg.content))
|
57 |
+
elif msg.role == "assistant":
|
58 |
+
messages.append(AIMessage(content=msg.content))
|
59 |
+
elif msg.role == "system":
|
60 |
+
messages.append(SystemMessage(content=msg.content))
|
61 |
+
app_logger.debug(f"Loaded {len(messages)} messages for session {session_id}")
|
62 |
return messages
|
63 |
|
64 |
+
# --- Database write helpers ---
|
65 |
+
def save_chat_message_to_db(
|
66 |
+
session_id: int,
|
67 |
+
role: str,
|
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,
|
76 |
+
role=role,
|
77 |
+
content=content,
|
78 |
+
timestamp=datetime.utcnow(),
|
79 |
+
tool_call_id=tool_call_id,
|
80 |
+
tool_name=tool_name,
|
81 |
)
|
82 |
+
db.add(obj)
|
83 |
app_logger.info(f"Message saved to DB for session {session_id}. Role: {role}.")
|
84 |
|
85 |
+
|
86 |
+
def update_chat_session_with_context_summary_in_db(session_id: int, summary: str):
|
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 ChatSession {session_id} with context summary.")
|
93 |
else:
|
94 |
+
app_logger.error(f"ChatSession {session_id} not found for updating context.")
|
95 |
|
96 |
+
# --- Page Title & Disclaimer ---
|
97 |
st.title("AI Consultation Room")
|
98 |
st.markdown(f"Interacting as: **{authenticated_username}**")
|
99 |
+
st.info(f"{settings.MAIN_DISCLAIMER_SHORT} Remember to use only anonymized, simulated data.")
|
100 |
|
101 |
+
# --- Retrieve or Create Chat Session ---
|
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 found. Please logout and login again.")
|
105 |
+
app_logger.error("No current_chat_session_id in session_state.")
|
106 |
st.stop()
|
107 |
|
108 |
+
# --- Patient Context Form ---
|
109 |
if not st.session_state.consult_context_submitted:
|
110 |
+
st.subheader("Step 1: Provide Patient Context (Optional)")
|
111 |
+
with st.form("patient_context_form"):
|
112 |
+
age = st.number_input("Patient Age (Simulated)", min_value=0, max_value=120, step=1)
|
113 |
+
gender = st.selectbox("Patient Gender (Simulated)", ["Not Specified", "Male", "Female", "Other"], index=0)
|
114 |
+
complaint = st.text_area("Chief Complaint", height=80, placeholder="e.g., Persistent cough")
|
115 |
+
history = st.text_area("Key Medical History", height=80, placeholder="e.g., Type 2 Diabetes")
|
116 |
+
meds = st.text_area("Current Medications", height=80, placeholder="e.g., Metformin")
|
117 |
+
submit = st.form_submit_button("Start Consult")
|
118 |
+
|
119 |
+
if submit:
|
120 |
+
raw = {"age": age, "gender": gender, "complaint": complaint, "history": history, "meds": meds}
|
121 |
+
filtered = {k.title(): v for k, v in raw.items() if v and not (isinstance(v, str) and v == "Not Specified")}
|
122 |
+
st.session_state.current_consult_patient_context_dict = filtered
|
123 |
+
|
124 |
+
summary = (
|
125 |
+
"; ".join(f"{k}: {v}" for k, v in filtered.items())
|
126 |
+
if filtered else
|
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"Patient context for session {chat_session_id}: {summary}")
|
133 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
st.stop()
|
135 |
|
136 |
+
# --- Chat Interaction ---
|
137 |
st.subheader("Step 2: Interact with AI Health Navigator")
|
138 |
+
history_key = f"agent_history_{chat_session_id}"
|
139 |
+
if history_key not in st.session_state:
|
140 |
+
st.session_state[history_key] = load_chat_history_for_agent(chat_session_id)
|
141 |
+
if not st.session_state[history_key]:
|
142 |
+
try:
|
143 |
+
log_consultation_start(authenticated_user_id, chat_session_id)
|
144 |
+
except Exception as e:
|
145 |
+
app_logger.warning(f"Metrics log failed: {e}")
|
146 |
+
greeting = "Hello! I am your AI Health Navigator. How can I help today?"
|
147 |
+
if st.session_state.current_consult_patient_context_dict:
|
148 |
+
greeting += " I have noted the patient context provided."
|
149 |
+
st.session_state[history_key].append(AIMessage(content=greeting))
|
150 |
+
save_chat_message_to_db(chat_session_id, "assistant", greeting)
|
151 |
+
|
152 |
+
# Display past messages
|
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).all():
|
157 |
+
if msg.role == "system":
|
158 |
+
continue
|
159 |
avatar = "π§ββοΈ" if msg.role == "assistant" else "π€"
|
160 |
+
if msg.role == "tool":
|
161 |
+
avatar = "π οΈ"
|
162 |
with st.chat_message(msg.role, avatar=avatar):
|
163 |
st.markdown(msg.content)
|
164 |
|
165 |
+
# Capture user input and invoke agent
|
166 |
+
if prompt := st.chat_input("Ask the AI... (e.g., 'What is hypertension?')"):
|
167 |
+
with st.chat_message("user", avatar="π€"):
|
168 |
+
st.markdown(prompt)
|
169 |
+
save_chat_message_to_db(chat_session_id, "user", prompt)
|
170 |
+
st.session_state[history_key].append(HumanMessage(content=prompt))
|
171 |
|
172 |
with st.chat_message("assistant", avatar="π§ββοΈ"):
|
173 |
with st.spinner("AI is thinking..."):
|
174 |
try:
|
175 |
+
pctxt = st.session_state.current_consult_patient_context_dict
|
176 |
+
if pctxt:
|
177 |
+
pc_str = "; ".join(f"{k}: {v}" for k, v in pctxt.items())
|
|
|
|
|
178 |
else:
|
179 |
+
pc_str = "No specific patient context provided."
|
180 |
|
181 |
+
payload = {
|
182 |
+
"input": prompt,
|
183 |
+
"chat_history": st.session_state[history_key],
|
184 |
+
"patient_context": pc_str,
|
185 |
}
|
186 |
+
app_logger.debug(f"Invoking agent with payload: {payload}")
|
187 |
+
resp = agent_executor.invoke(payload)
|
188 |
+
content = resp.get("output", "I could not generate a response.")
|
189 |
+
content = str(content)
|
190 |
+
|
191 |
+
app_logger.info(f"Agent response session {chat_session_id}: {content[:100]}")
|
192 |
+
st.markdown(content)
|
193 |
+
save_chat_message_to_db(chat_session_id, "assistant", content)
|
194 |
+
st.session_state[history_key].append(AIMessage(content=content))
|
195 |
+
except Exception as err:
|
196 |
+
app_logger.error(f"Error during agent invocation for session {chat_session_id}: {err}", exc_info=True)
|
197 |
+
st.error(f"Sorry, an error occurred: {type(err).__name__}. Please try again.")
|
198 |
+
err_msg = f"System encountered an error: {type(err).__name__}."
|
199 |
+
save_chat_message_to_db(chat_session_id, "assistant", err_msg)
|
200 |
+
st.session_state[history_key].append(AIMessage(content=err_msg))
|
201 |
+
st.rerun()
|
|
|
|
|
|
|
|