mgbam commited on
Commit
52c7695
Β·
verified Β·
1 Parent(s): 991c5ac

Update pages/2_Consult.py

Browse files
Files changed (1) hide show
  1. pages/2_Consult.py +77 -96
pages/2_Consult.py CHANGED
@@ -1,6 +1,6 @@
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
@@ -29,192 +29,173 @@ app_logger.info(f"User '{authenticated_username}' (ID: {authenticated_user_id})
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(
71
  session_id=session_id, role=role, content=content, timestamp=datetime.utcnow(),
72
  tool_call_id=tool_call_id, tool_name=tool_name
73
  )
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:
81
  session_to_update.patient_context_summary = context_summary
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)
177
- save_chat_message_to_db(chat_session_id, "user", prompt)
178
- st.session_state[agent_history_key].append(HumanMessage(content=prompt))
 
179
 
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."))
 
1
  # /home/user/app/pages/2_Consult.py
2
  import streamlit as st
3
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage # Ensure all are imported
4
  from datetime import datetime
5
  from typing import List, Optional, Dict, Any
6
  from sqlmodel import select
 
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:
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 API keys (e.g., Google API Key for Gemini) are configured.")
36
  st.stop()
37
+ except Exception as e: # Catch any other unexpected error during agent init
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
  # --- Session State for Consult Page ---
43
+ if 'current_consult_patient_context_dict' not in st.session_state: # Renamed for clarity
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
  # --- Helper Functions ---
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 session_id: {session_id}")
53
  with get_session_context() as db:
54
  statement = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
55
  db_messages = db.exec(statement).all()
56
  for msg in db_messages:
57
+ if msg.role == "user": messages.append(HumanMessage(content=msg.content))
58
+ elif msg.role == "assistant": messages.append(AIMessage(content=msg.content))
59
+ elif msg.role == "system": messages.append(SystemMessage(content=msg.content))
60
+ app_logger.debug(f"Loaded {len(messages)} LangChain messages for agent history (session {session_id}).")
 
 
 
61
  return messages
62
 
63
  def save_chat_message_to_db(session_id: int, role: str, content: str, tool_call_id: Optional[str]=None, tool_name: Optional[str]=None):
64
  app_logger.debug(f"Saving message to DB for session {session_id}: Role={role}")
65
  with get_session_context() as db:
66
+ chat_message_obj = ChatMessage(
67
  session_id=session_id, role=role, content=content, timestamp=datetime.utcnow(),
68
  tool_call_id=tool_call_id, tool_name=tool_name
69
  )
70
+ db.add(chat_message_obj)
71
  app_logger.info(f"Message saved to DB for session {session_id}. Role: {role}.")
72
 
73
+ def update_chat_session_with_context_summary_in_db(session_id: int, context_summary: str):
74
  with get_session_context() as db:
75
  session_to_update = db.get(ChatSession, session_id)
76
  if session_to_update:
77
  session_to_update.patient_context_summary = context_summary
78
+ db.add(session_to_update)
79
+ app_logger.info(f"Updated ChatSession {session_id} with patient context summary in DB.")
80
  else:
81
+ app_logger.error(f"Could not find ChatSession {session_id} to update context summary.")
82
 
83
  # --- Page Logic ---
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 for this demonstration.")
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 ID found. Please try logging out and back in.")
91
+ app_logger.error(f"User '{authenticated_username}' on Consult page with NO current_chat_session_id.")
92
  st.stop()
93
 
94
  # --- Patient Context Input Form ---
95
  if not st.session_state.consult_context_submitted:
96
  st.subheader("Step 1: Provide Patient Context (Optional, Simulated Data Only)")
97
+ with st.form(key="patient_context_form_consult_page"):
98
  st.markdown("**Reminder: Use only anonymized, simulated data for this demonstration.**")
99
+ age_in = st.number_input("Patient Age (Simulated)", min_value=0, max_value=120, step=1, value=None)
100
+ gender_in = st.selectbox("Patient Gender (Simulated)", ["Not Specified", "Male", "Female", "Other"], index=0)
101
+ cc_in = st.text_area("Chief Complaint / Reason for Consult (Simulated)", height=100, placeholder="e.g., Persistent cough")
102
+ hist_in = st.text_area("Key Medical History (Simulated)", height=100, placeholder="e.g., Type 2 Diabetes")
103
+ meds_in = st.text_area("Current Medications (Simulated)", height=100, placeholder="e.g., Metformin")
104
+ submit_context_btn = st.form_submit_button("Start Consult with this Context")
105
+
106
+ if submit_context_btn:
107
+ raw_context = {
108
+ "age": age_in, "gender": gender_in, "chief_complaint": cc_in,
109
+ "key_medical_history": hist_in, "current_medications": meds_in,
110
+ }
111
+ # Filter out None/empty/"Not Specified" values for cleaner context dictionary
112
+ filtered_context_dict = {
113
+ k.replace('_', ' ').title(): v for k, v in raw_context.items()
114
+ if v is not None and str(v).strip() and str(v) != "Not Specified" and (isinstance(v, int) and v > 0 or isinstance(v, str)) # ensure age is >0
115
  }
116
+ st.session_state.current_consult_patient_context_dict = filtered_context_dict
 
 
117
 
118
+ if filtered_context_dict:
119
+ context_summary_str = "; ".join([f"{k}: {v}" for k, v in filtered_context_dict.items()])
 
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 Interface ---
132
  st.subheader("Step 2: Interact with AI Health Navigator")
133
  agent_history_key = f"agent_chat_history_{chat_session_id}"
134
 
135
  if agent_history_key not in st.session_state:
136
  st.session_state[agent_history_key] = load_chat_history_for_agent(chat_session_id)
137
+ if not st.session_state[agent_history_key]: # If history is empty after loading
138
  try: log_consultation_start(user_id=authenticated_user_id, session_id=chat_session_id)
139
+ except Exception as e_metric: app_logger.warning(f"Failed to log consultation start metric: {e_metric}")
140
 
141
+ initial_ai_msg = "Hello! I am your AI Health Navigator. How can I assist you today?"
142
+ if st.session_state.get('current_consult_patient_context_dict'): # Check the renamed key
143
+ initial_ai_msg += " I have noted the patient context you provided."
144
 
145
+ st.session_state[agent_history_key].append(AIMessage(content=initial_ai_msg))
146
+ save_chat_message_to_db(chat_session_id, "assistant", initial_ai_msg)
 
147
 
148
+ # Display chat messages from DB for UI
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
  ui_messages = db.exec(stmt).all()
153
  for msg in ui_messages:
154
+ if msg.role == "system": continue
155
  avatar = "πŸ§‘β€βš•οΈ" if msg.role == "assistant" else "πŸ‘€"
156
+ if msg.role == "tool": avatar = "πŸ› οΈ"
157
  with st.chat_message(msg.role, avatar=avatar):
158
+ st.markdown(msg.content)
159
 
160
+ # Chat input
161
+ if user_prompt := st.chat_input("Ask the AI... (e.g., 'What is hypertension?')"):
162
+ with st.chat_message("user", avatar="πŸ‘€"): st.markdown(user_prompt)
163
+ save_chat_message_to_db(chat_session_id, "user", user_prompt)
164
+ st.session_state[agent_history_key].append(HumanMessage(content=user_prompt))
165
 
166
  with st.chat_message("assistant", avatar="πŸ§‘β€βš•οΈ"):
167
  with st.spinner("AI is thinking..."):
168
  try:
169
+ # Prepare patient context string for the agent
170
+ patient_context_dict = st.session_state.get('current_consult_patient_context_dict', {})
171
  if patient_context_dict:
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
  patient_context_str_for_invoke = "No specific patient context was provided for this interaction."
176
 
177
  invoke_payload = {
178
+ "input": user_prompt,
179
+ "chat_history": st.session_state[agent_history_key], # List of BaseMessage
180
+ "patient_context": patient_context_str_for_invoke
181
  }
182
  app_logger.debug(f"Invoking agent with payload: {invoke_payload}")
 
183
  response = agent_executor.invoke(invoke_payload)
184
 
185
+ ai_response_content = response.get('output', "I could not generate a valid response at this time.")
186
  if not isinstance(ai_response_content, str): ai_response_content = str(ai_response_content)
187
 
188
  app_logger.info(f"Agent response for session {chat_session_id}: '{ai_response_content[:100]}...'")
189
+ st.markdown(ai_response_content)
190
  save_chat_message_to_db(chat_session_id, "assistant", ai_response_content)
191
  st.session_state[agent_history_key].append(AIMessage(content=ai_response_content))
192
 
193
  except Exception as e:
194
  app_logger.error(f"Error during agent invocation for session {chat_session_id}: {e}", exc_info=True)
195
+ error_type_name = type(e).__name__
196
+ user_friendly_error = f"Sorry, an error occurred ({error_type_name}). Please try rephrasing your query or contact support if the issue persists."
197
+ st.error(user_friendly_error)
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