mgbam commited on
Commit
7ae304b
Β·
verified Β·
1 Parent(s): a6d04e1

Update pages/2_Consult.py

Browse files
Files changed (1) hide show
  1. pages/2_Consult.py +110 -76
pages/2_Consult.py CHANGED
@@ -1,23 +1,24 @@
1
  # /home/user/app/pages/2_Consult.py
2
  import streamlit as st
3
- from langchain_core.messages import HumanMessage, AIMessage, SystemMessage # <--- CORRECTED IMPORT
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
13
  from services.metrics import log_consultation_start
14
 
15
-
16
  # --- Authentication Check ---
17
  if not st.session_state.get("authenticated_user_id"):
18
  st.warning("Please log in to access the consultation page.")
19
- try: st.switch_page("app.py")
20
- except st.errors.StreamlitAPIException: st.info("Please navigate to the main login page.")
 
 
21
  st.stop()
22
 
23
  authenticated_user_id = st.session_state.get("authenticated_user_id")
@@ -26,36 +27,44 @@ app_logger.info(f"User '{authenticated_username}' (ID: {authenticated_user_id})
26
 
27
  # --- Initialize Agent ---
28
  try:
29
- agent_executor = get_agent_executor()
 
 
 
 
 
 
30
  except Exception as e:
31
- st.error(f"Fatal Error: Could not initialize AI Agent: {e}. Please check API keys and configurations.")
32
- app_logger.critical(f"AI Agent initialization failed: {e}", exc_info=True)
33
  st.stop()
34
 
 
35
  # --- Session State for Consult Page ---
36
  if 'current_consult_patient_context' not in st.session_state:
37
- st.session_state.current_consult_patient_context = {} # Stores structured context for current consult
38
  if 'consult_context_submitted' not in st.session_state:
39
  st.session_state.consult_context_submitted = False
40
 
41
  # --- Helper Functions ---
42
- @st.cache_data(ttl=30, show_spinner=False) # Short cache for agent history
43
- def load_chat_history_for_agent(session_id: int) -> List:
44
  messages = []
45
- # ... (load_chat_history_for_agent from previous full rewrite of 2_Consult.py, using SQLModel select) ...
46
- # This function should convert DB ChatMessages to LangChain HumanMessage/AIMessage
47
  app_logger.debug(f"Loading agent chat history for session_id: {session_id}")
48
  with get_session_context() as db:
49
  statement = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
50
  db_messages = db.exec(statement).all()
51
  for msg in db_messages:
52
- if msg.role == "user": messages.append(HumanMessage(content=msg.content))
53
- elif msg.role == "assistant": messages.append(AIMessage(content=msg.content))
 
 
 
 
 
54
  return messages
55
 
56
-
57
  def save_chat_message_to_db(session_id: int, role: str, content: str, tool_call_id: Optional[str]=None, tool_name: Optional[str]=None):
58
- # ... (save_chat_message_to_db from previous full rewrite of 2_Consult.py) ...
59
  app_logger.debug(f"Saving message to DB for session {session_id}: Role={role}")
60
  with get_session_context() as db:
61
  chat_message = ChatMessage(
@@ -65,8 +74,7 @@ def save_chat_message_to_db(session_id: int, role: str, content: str, tool_call_
65
  db.add(chat_message) # Commit handled by context manager
66
  app_logger.info(f"Message saved to DB for session {session_id}. Role: {role}.")
67
 
68
-
69
- def update_chat_session_with_context(session_id: int, context_summary: str):
70
  with get_session_context() as db:
71
  session_to_update = db.get(ChatSession, session_id)
72
  if session_to_update:
@@ -74,86 +82,95 @@ def update_chat_session_with_context(session_id: int, context_summary: str):
74
  db.add(session_to_update) # Stage for commit
75
  app_logger.info(f"Updated ChatSession {session_id} with patient context summary.")
76
  else:
77
- app_logger.error(f"Could not find ChatSession {session_id} to update with context.")
78
 
79
  # --- Page Logic ---
80
  st.title("AI Consultation Room")
81
  st.markdown(f"Interacting as: **{authenticated_username}**")
82
- st.info(settings.MAIN_DISCLAIMER_SHORT + " Do not enter real PHI.")
83
 
84
  chat_session_id = st.session_state.get("current_chat_session_id")
85
  if not chat_session_id:
86
- st.error("No active chat session. This may occur if you logged out and back in. A new session was created. If issues persist, please re-login fully or contact support.")
87
- app_logger.error(f"User '{authenticated_username}' on Consult page with no current_chat_session_id.")
88
- # Attempt to create a new one if truly missing, or guide to re-login
89
- # For now, stopping is safer if app.py is supposed to always create one.
90
  st.stop()
91
 
92
  # --- Patient Context Input Form ---
93
  if not st.session_state.consult_context_submitted:
94
- st.subheader("Optional: Provide Patient Context (Simulated Data Only)")
95
- with st.form(key="patient_context_form"):
96
  st.markdown("**Reminder: Use only anonymized, simulated data for this demonstration.**")
97
- age = st.number_input("Patient Age (Simulated)", min_value=0, max_value=120, step=1)
98
- gender = st.selectbox("Patient Gender (Simulated)", ["Not Specified", "Male", "Female", "Other"])
99
- chief_complaint = st.text_area("Chief Complaint / Reason for Consult (Simulated)", height=100)
100
- key_history = st.text_area("Key Medical History (Simulated - e.g., diabetes, hypertension)", height=100)
101
- current_meds = st.text_area("Current Medications (Simulated - e.g., metformin, lisinopril)", height=100)
 
102
  submit_context_button = st.form_submit_button("Start Consult with this Context")
103
 
104
  if submit_context_button:
105
- context = {
106
- "age": age if age > 0 else "Not Specified",
107
- "gender": gender,
108
- "chief_complaint": chief_complaint.strip() if chief_complaint.strip() else "Not Specified",
109
- "key_medical_history": key_history.strip() if key_history.strip() else "Not Specified",
110
- "current_medications": current_meds.strip() if current_meds.strip() else "Not Specified",
111
  }
112
- st.session_state.current_consult_patient_context = context
113
- st.session_state.consult_context_submitted = True
114
-
115
- # Create a summary for the agent and DB
116
- context_summary_parts = [f"{k.replace('_', ' ').title()}: {v}" for k, v in context.items() if v != "Not Specified" and v != ""]
117
- context_summary_for_agent = "Patient Context: " + "; ".join(context_summary_parts) if context_summary_parts else "No specific patient context provided."
 
 
 
 
 
118
 
119
- # Save context summary to ChatSession model
120
- update_chat_session_with_context(chat_session_id, context_summary_for_agent)
121
-
122
- # Prepend context to agent's chat history as a system message or initial user message
123
- # For this example, let's add it as a system message to guide the AI
124
  agent_history_key = f"agent_chat_history_{chat_session_id}"
125
  if agent_history_key not in st.session_state: st.session_state[agent_history_key] = []
126
- st.session_state[agent_history_key].insert(0, SystemMessage(content=context_summary_for_agent))
127
- # Also save this "system" context message to DB for record keeping if desired
128
- save_chat_message_to_db(chat_session_id, "system", context_summary_for_agent)
129
 
130
- app_logger.info(f"Patient context submitted for session {chat_session_id}: {context_summary_for_agent}")
131
- st.rerun() # Rerun to hide form and show chat
132
- st.stop() # Don't proceed to chat until context is submitted or skipped
 
 
133
 
134
- # --- Chat Interface (Shown after context is submitted/skipped) ---
 
 
 
 
 
 
135
  agent_history_key = f"agent_chat_history_{chat_session_id}"
 
136
  if agent_history_key not in st.session_state:
137
  st.session_state[agent_history_key] = load_chat_history_for_agent(chat_session_id)
138
- if not st.session_state[agent_history_key]: # If history is empty (even after context attempt)
139
  try: log_consultation_start(user_id=authenticated_user_id, session_id=chat_session_id)
140
- except Exception as e: app_logger.warning(f"Failed to log consultation start: {e}")
 
141
  initial_ai_message_content = "Hello! I am your AI Health Navigator. How can I assist you today?"
 
 
 
142
  st.session_state[agent_history_key].append(AIMessage(content=initial_ai_message_content))
143
  save_chat_message_to_db(chat_session_id, "assistant", initial_ai_message_content)
 
144
 
145
- # Display chat messages from DB for UI
146
  with st.container():
147
  with get_session_context() as db:
148
  stmt = select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)
149
  ui_messages = db.exec(stmt).all()
150
  for msg in ui_messages:
151
- if msg.role == "system": # Don't show system context messages directly in chat UI
152
- continue
153
  avatar = "πŸ§‘β€βš•οΈ" if msg.role == "assistant" else "πŸ‘€"
154
- if msg.role == "tool": avatar = "πŸ› οΈ"
155
  with st.chat_message(msg.role, avatar=avatar):
156
- st.markdown(msg.content) # Add source/confidence here if msg object supports it
157
 
158
  if prompt := st.chat_input("Ask the AI..."):
159
  with st.chat_message("user", avatar="πŸ‘€"): st.markdown(prompt)
@@ -163,24 +180,41 @@ if prompt := st.chat_input("Ask the AI..."):
163
  with st.chat_message("assistant", avatar="πŸ§‘β€βš•οΈ"):
164
  with st.spinner("AI is thinking..."):
165
  try:
166
- # Pass patient context if your agent is designed to use it explicitly
167
- # current_context = st.session_state.get('current_consult_patient_context', {})
168
- # context_str_for_invoke = "; ".join([f"{k}: {v}" for k,v in current_context.items() if v and v!="Not Specified"])
169
-
170
- response = agent_executor.invoke({
 
 
 
 
171
  "input": prompt,
172
  "chat_history": st.session_state[agent_history_key],
173
- # "patient_context": context_str_for_invoke # If agent expects this
174
- })
175
- ai_response_content = response.get('output', "I could not generate a response.")
 
 
 
 
176
  if not isinstance(ai_response_content, str): ai_response_content = str(ai_response_content)
177
 
178
- st.markdown(ai_response_content) # Display sources/confidence here if available in ai_response_content
 
179
  save_chat_message_to_db(chat_session_id, "assistant", ai_response_content)
180
  st.session_state[agent_history_key].append(AIMessage(content=ai_response_content))
 
181
  except Exception as e:
182
  app_logger.error(f"Error during agent invocation for session {chat_session_id}: {e}", exc_info=True)
183
- error_msg_user = f"Sorry, an error occurred: {type(e).__name__}. Please try again."
184
- st.error(error_msg_user)
185
- save_chat_message_to_db(chat_session_id, "assistant", f"Internal error: {type(e).__name__}")
186
- st.session_state[agent_history_key].append(AIMessage(content=f"Internal error: {type(e).__name__}"))
 
 
 
 
 
 
 
 
1
  # /home/user/app/pages/2_Consult.py
2
  import streamlit as st
3
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage # Ensure SystemMessage is imported
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 # This now returns the Gemini-based agent
10
  from models import ChatMessage, ChatSession
11
  from models.db import get_session_context
12
  from services.logger import app_logger
13
  from services.metrics import log_consultation_start
14
 
 
15
  # --- Authentication Check ---
16
  if not st.session_state.get("authenticated_user_id"):
17
  st.warning("Please log in to access the consultation page.")
18
+ try:
19
+ st.switch_page("app.py")
20
+ except st.errors.StreamlitAPIException:
21
+ st.info("Please navigate to the main login page.")
22
  st.stop()
23
 
24
  authenticated_user_id = st.session_state.get("authenticated_user_id")
 
27
 
28
  # --- Initialize Agent ---
29
  try:
30
+ agent_executor = get_agent_executor() # Gets the Gemini agent executor
31
+ app_logger.info("Gemini-based agent executor initialized for Consult page.")
32
+ except ValueError as e: # Catch specific error from get_agent_executor if API key is missing
33
+ st.error(f"AI Agent Initialization Error: {e}")
34
+ app_logger.critical(f"Fatal: AI Agent could not be initialized in Consult page: {e}", exc_info=True)
35
+ st.info("Please ensure the necessary API keys (e.g., Google API Key for Gemini) are configured in the application settings.")
36
+ st.stop()
37
  except Exception as e:
38
+ st.error(f"An unexpected error occurred while initializing the AI Agent: {e}")
39
+ app_logger.critical(f"Fatal: Unexpected AI Agent initialization error: {e}", exc_info=True)
40
  st.stop()
41
 
42
+
43
  # --- Session State for Consult Page ---
44
  if 'current_consult_patient_context' not in st.session_state:
45
+ st.session_state.current_consult_patient_context = {}
46
  if 'consult_context_submitted' not in st.session_state:
47
  st.session_state.consult_context_submitted = False
48
 
49
  # --- Helper Functions ---
50
+ @st.cache_data(ttl=30, show_spinner=False)
51
+ def load_chat_history_for_agent(session_id: int) -> List: # List of LangChain messages
52
  messages = []
 
 
53
  app_logger.debug(f"Loading agent chat history for session_id: {session_id}")
54
  with get_session_context() as db:
55
  statement = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
56
  db_messages = db.exec(statement).all()
57
  for msg in db_messages:
58
+ if msg.role == "user":
59
+ messages.append(HumanMessage(content=msg.content))
60
+ elif msg.role == "assistant":
61
+ messages.append(AIMessage(content=msg.content))
62
+ elif msg.role == "system": # Include system messages in agent history if they were saved
63
+ messages.append(SystemMessage(content=msg.content))
64
+ app_logger.debug(f"Loaded {len(messages)} messages for agent history for session {session_id}.")
65
  return messages
66
 
 
67
  def save_chat_message_to_db(session_id: int, role: str, content: str, tool_call_id: Optional[str]=None, tool_name: Optional[str]=None):
 
68
  app_logger.debug(f"Saving message to DB for session {session_id}: Role={role}")
69
  with get_session_context() as db:
70
  chat_message = ChatMessage(
 
74
  db.add(chat_message) # Commit handled by context manager
75
  app_logger.info(f"Message saved to DB for session {session_id}. Role: {role}.")
76
 
77
+ def update_chat_session_with_context_summary(session_id: int, context_summary: str):
 
78
  with get_session_context() as db:
79
  session_to_update = db.get(ChatSession, session_id)
80
  if session_to_update:
 
82
  db.add(session_to_update) # Stage for commit
83
  app_logger.info(f"Updated ChatSession {session_id} with patient context summary.")
84
  else:
85
+ app_logger.error(f"Could not find ChatSession {session_id} to update with context summary.")
86
 
87
  # --- Page Logic ---
88
  st.title("AI Consultation Room")
89
  st.markdown(f"Interacting as: **{authenticated_username}**")
90
+ st.info(f"{settings.MAIN_DISCLAIMER_SHORT} Remember to use only anonymized, simulated data.")
91
 
92
  chat_session_id = st.session_state.get("current_chat_session_id")
93
  if not chat_session_id:
94
+ st.error("No active chat session ID. This can occur if a session wasn't properly created on login. Please try logging out and then logging back in. If the problem persists, contact support.")
95
+ app_logger.error(f"User '{authenticated_username}' (ID: {authenticated_user_id}) on Consult page with NO current_chat_session_id.")
 
 
96
  st.stop()
97
 
98
  # --- Patient Context Input Form ---
99
  if not st.session_state.consult_context_submitted:
100
+ st.subheader("Step 1: Provide Patient Context (Optional, Simulated Data Only)")
101
+ with st.form(key="patient_context_form_consult"):
102
  st.markdown("**Reminder: Use only anonymized, simulated data for this demonstration.**")
103
+ age = st.number_input("Patient Age (Simulated)", min_value=0, max_value=120, step=1, value=None) # Default to None
104
+ gender_options = ["Not Specified", "Male", "Female", "Other"]
105
+ gender = st.selectbox("Patient Gender (Simulated)", gender_options, index=0)
106
+ chief_complaint = st.text_area("Chief Complaint / Reason for Consult (Simulated)", height=100, placeholder="e.g., Persistent cough for 2 weeks")
107
+ key_history = st.text_area("Key Medical History (Simulated)", height=100, placeholder="e.g., Type 2 Diabetes, Hypertension, Asthma")
108
+ current_meds = st.text_area("Current Medications (Simulated)", height=100, placeholder="e.g., Metformin 500mg BID, Lisinopril 10mg OD")
109
  submit_context_button = st.form_submit_button("Start Consult with this Context")
110
 
111
  if submit_context_button:
112
+ context_dict = {
113
+ "age": age if age is not None and age > 0 else None, # Store None if not specified
114
+ "gender": gender if gender != "Not Specified" else None,
115
+ "chief_complaint": chief_complaint.strip() or None,
116
+ "key_medical_history": key_history.strip() or None,
117
+ "current_medications": current_meds.strip() or None,
118
  }
119
+ # Filter out None values for the summary string
120
+ valid_context_parts = {k: v for k, v in context_dict.items() if v is not None}
121
+ st.session_state.current_consult_patient_context = valid_context_parts # Store the filtered dict
122
+
123
+ if valid_context_parts:
124
+ context_summary_str_parts = [f"{k.replace('_', ' ').title()}: {v}" for k, v in valid_context_parts.items()]
125
+ context_summary_for_db_and_agent = "; ".join(context_summary_str_parts)
126
+ else:
127
+ context_summary_for_db_and_agent = "No specific patient context provided for this session."
128
+
129
+ update_chat_session_with_context_summary(chat_session_id, context_summary_for_db_and_agent)
130
 
 
 
 
 
 
131
  agent_history_key = f"agent_chat_history_{chat_session_id}"
132
  if agent_history_key not in st.session_state: st.session_state[agent_history_key] = []
 
 
 
133
 
134
+ # Don't add patient context as a SystemMessage if it's passed as a variable to invoke
135
+ # The agent's main system prompt will now include a placeholder for it.
136
+ # However, we save it to DB for record keeping.
137
+ if valid_context_parts: # Save a system message indicating context was provided
138
+ save_chat_message_to_db(chat_session_id, "system", f"Initial Patient Context Provided: {context_summary_for_db_and_agent}")
139
 
140
+ st.session_state.consult_context_submitted = True
141
+ app_logger.info(f"Patient context submitted for session {chat_session_id}: {context_summary_for_db_and_agent}")
142
+ st.rerun()
143
+ st.stop()
144
+
145
+ # --- Chat Interface (Shown after context is submitted or if skipped by some other logic not yet present) ---
146
+ st.subheader("Step 2: Interact with AI Health Navigator")
147
  agent_history_key = f"agent_chat_history_{chat_session_id}"
148
+
149
  if agent_history_key not in st.session_state:
150
  st.session_state[agent_history_key] = load_chat_history_for_agent(chat_session_id)
151
+ if not st.session_state[agent_history_key]: # If history is empty
152
  try: log_consultation_start(user_id=authenticated_user_id, session_id=chat_session_id)
153
+ except Exception as e: app_logger.warning(f"Failed to log consultation start metric: {e}")
154
+
155
  initial_ai_message_content = "Hello! I am your AI Health Navigator. How can I assist you today?"
156
+ if st.session_state.get('current_consult_patient_context'):
157
+ initial_ai_message_content += " I have noted the patient context you provided."
158
+
159
  st.session_state[agent_history_key].append(AIMessage(content=initial_ai_message_content))
160
  save_chat_message_to_db(chat_session_id, "assistant", initial_ai_message_content)
161
+ app_logger.info(f"Initialized new consultation (session {chat_session_id}) with a greeting.")
162
 
163
+ # Display chat messages for UI
164
  with st.container():
165
  with get_session_context() as db:
166
  stmt = select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)
167
  ui_messages = db.exec(stmt).all()
168
  for msg in ui_messages:
169
+ if msg.role == "system": continue # Don't display system context messages directly
 
170
  avatar = "πŸ§‘β€βš•οΈ" if msg.role == "assistant" else "πŸ‘€"
171
+ if msg.role == "tool": avatar = "πŸ› οΈ" # Assuming you might log tool calls this way
172
  with st.chat_message(msg.role, avatar=avatar):
173
+ st.markdown(msg.content) # Potentially enhance to show sources/confidence if agent provides
174
 
175
  if prompt := st.chat_input("Ask the AI..."):
176
  with st.chat_message("user", avatar="πŸ‘€"): st.markdown(prompt)
 
180
  with st.chat_message("assistant", avatar="πŸ§‘β€βš•οΈ"):
181
  with st.spinner("AI is thinking..."):
182
  try:
183
+ # Prepare patient context string for the agent, if any was provided
184
+ patient_context_dict = st.session_state.get('current_consult_patient_context', {})
185
+ if patient_context_dict:
186
+ context_parts_for_invoke = [f"{k.replace('_', ' ').title()}: {v}" for k, v in patient_context_dict.items()]
187
+ patient_context_str_for_invoke = "; ".join(context_parts_for_invoke)
188
+ else:
189
+ patient_context_str_for_invoke = "No specific patient context was provided for this interaction."
190
+
191
+ invoke_payload = {
192
  "input": prompt,
193
  "chat_history": st.session_state[agent_history_key],
194
+ "patient_context": patient_context_str_for_invoke # Pass to agent
195
+ }
196
+ app_logger.debug(f"Invoking agent with payload: {invoke_payload}")
197
+
198
+ response = agent_executor.invoke(invoke_payload)
199
+
200
+ ai_response_content = response.get('output', "I could not generate a valid response.")
201
  if not isinstance(ai_response_content, str): ai_response_content = str(ai_response_content)
202
 
203
+ app_logger.info(f"Agent response for session {chat_session_id}: '{ai_response_content[:100]}...'")
204
+ st.markdown(ai_response_content) # Display AI response
205
  save_chat_message_to_db(chat_session_id, "assistant", ai_response_content)
206
  st.session_state[agent_history_key].append(AIMessage(content=ai_response_content))
207
+
208
  except Exception as e:
209
  app_logger.error(f"Error during agent invocation for session {chat_session_id}: {e}", exc_info=True)
210
+ # The user-facing error was: "Sorry, an error occurred: ValidationError. Please try again."
211
+ # Let's try to be a bit more specific if we can, or keep it generic but log details.
212
+ error_type_name = type(e).__name__ # e.g., "ValidationError", "APIError"
213
+ user_friendly_error_message = f"Sorry, an error occurred ({error_type_name}). Please try rephrasing your query or contact support if the issue persists."
214
+ st.error(user_friendly_error_message)
215
+
216
+ # Save a representation of the error to DB for the assistant's turn
217
+ db_error_message = f"System encountered an error: {error_type_name} while processing user query. Details logged."
218
+ save_chat_message_to_db(chat_session_id, "assistant", db_error_message)
219
+ # Add error representation to agent history so it's aware for next turn (optional)
220
+ st.session_state[agent_history_key].append(AIMessage(content=f"Note to self: Encountered an error ({error_type_name}) on the previous turn."))