mgbam commited on
Commit
71a1c43
Β·
verified Β·
1 Parent(s): 5315036

Update pages/2_Consult.py

Browse files
Files changed (1) hide show
  1. pages/2_Consult.py +109 -73
pages/2_Consult.py CHANGED
@@ -1,46 +1,75 @@
 
1
  import streamlit as st
2
  from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
3
  from datetime import datetime
 
4
 
5
  from config.settings import settings
6
  from agent import get_agent_executor
7
- from models import ChatMessage, ChatSession, User # Assuming User is in session_state
8
- from models.db import get_session_context
9
  from services.logger import app_logger
10
- from services.metrics import log_consultation_start
11
 
12
- st.set_page_config(page_title=f"Consult - {settings.APP_TITLE}", layout="wide")
 
13
 
14
- if not st.session_state.get("authenticated_user"):
 
15
  st.warning("Please log in to access the consultation page.")
16
- st.switch_page("app.py") # Redirect to login
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  # --- Initialize Agent ---
19
  try:
20
  agent_executor = get_agent_executor()
21
- except ValueError as e: # Handles missing API key
22
  st.error(f"Could not initialize AI Agent: {e}")
 
 
 
 
 
23
  st.stop()
24
 
25
 
26
  # --- Helper Functions ---
27
- def load_chat_history(session_id: int) -> list:
28
- """Loads chat history from DB for the current session"""
 
29
  messages = []
 
30
  with get_session_context() as db:
 
 
31
  db_messages = db.query(ChatMessage).filter(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp).all()
32
  for msg in db_messages:
33
  if msg.role == "user":
34
  messages.append(HumanMessage(content=msg.content))
35
  elif msg.role == "assistant":
36
  messages.append(AIMessage(content=msg.content))
37
- # Add tool message handling if you store them as distinct roles in DB
38
- # elif msg.role == "tool":
39
- # messages.append(ToolMessage(content=msg.content, tool_call_id=msg.tool_call_id))
 
40
  return messages
41
 
42
- def save_chat_message(session_id: int, role: str, content: str, tool_call_id: Optional[str]=None, tool_name: Optional[str]=None):
43
  """Saves a chat message to the database."""
 
44
  with get_session_context() as db:
45
  chat_message = ChatMessage(
46
  session_id=session_id,
@@ -52,78 +81,85 @@ def save_chat_message(session_id: int, role: str, content: str, tool_call_id: Op
52
  )
53
  db.add(chat_message)
54
  db.commit()
 
55
 
56
  # --- Page Logic ---
57
  st.title("AI Consultation Room")
58
- st.markdown("Interact with the Quantum Health Navigator AI.")
59
 
60
- current_user: User = st.session_state.authenticated_user
61
  chat_session_id = st.session_state.get("current_chat_session_id")
62
 
63
  if not chat_session_id:
64
- st.error("No active chat session. Please re-login or contact support.")
 
65
  st.stop()
66
 
67
- # Load initial chat history for the agent (from Langchain Message objects)
68
- # For the agent, we need history in LangChain message format
69
- if "agent_chat_history" not in st.session_state:
70
- st.session_state.agent_chat_history = load_chat_history(chat_session_id)
71
- if not st.session_state.agent_chat_history: # If no history, maybe add a system greeting
72
- log_consultation_start()
73
- # You could add an initial AIMessage here if desired
74
- # initial_ai_message = AIMessage(content="Hello! How can I assist you today?")
75
- # st.session_state.agent_chat_history.append(initial_ai_message)
76
- # save_chat_message(chat_session_id, "assistant", initial_ai_message.content)
77
-
78
-
79
- # Display chat messages from DB (for UI)
80
- with get_session_context() as db:
81
- ui_messages = db.query(ChatMessage).filter(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp).all()
82
- for msg in ui_messages:
83
- with st.chat_message(msg.role):
84
- st.markdown(msg.content)
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  # Chat input
87
- if prompt := st.chat_input("Ask the AI... (e.g., 'What is hypertension?' or 'Optimize treatment for patient X with diabetes')"):
88
- # Add user message to UI and save to DB
89
- with st.chat_message("user"):
90
  st.markdown(prompt)
91
- save_chat_message(chat_session_id, "user", prompt)
92
-
93
  # Add to agent's history (LangChain format)
94
- st.session_state.agent_chat_history.append(HumanMessage(content=prompt))
95
 
96
  # Get AI response
97
- with st.spinner("AI is thinking..."):
98
- try:
99
- response = agent_executor.invoke({
100
- "input": prompt,
101
- "chat_history": st.session_state.agent_chat_history
102
- })
103
- ai_response_content = response['output']
104
-
105
- # Display AI response in UI and save to DB
106
- with st.chat_message("assistant"):
107
- st.markdown(ai_response_content)
108
- save_chat_message(chat_session_id, "assistant", ai_response_content)
109
-
110
- # Add AI response to agent's history
111
- st.session_state.agent_chat_history.append(AIMessage(content=ai_response_content))
112
-
113
- # Note: The agent executor might make tool calls. The create_openai_functions_agent
114
- # and AgentExecutor handle the tool invocation and adding ToolMessages to history internally
115
- # before producing the final 'output'. If you need to log individual tool calls/results
116
- # to your DB, you might need a more custom agent loop or callbacks.
117
-
118
- except Exception as e:
119
- app_logger.error(f"Error during agent invocation: {e}")
120
- st.error(f"An error occurred: {e}")
121
- # Save error message as AI response?
122
- error_message = f"Sorry, I encountered an error: {str(e)[:200]}" # Truncate for DB
123
- with st.chat_message("assistant"): # Or a custom error role
124
- st.markdown(error_message)
125
- save_chat_message(chat_session_id, "assistant", error_message) # Or "error" role
126
- st.session_state.agent_chat_history.append(AIMessage(content=error_message))
127
-
128
- # Rerun to show the latest messages immediately (though Streamlit usually does this)
129
- # st.rerun() # Usually not needed with st.chat_input and context managers
 
1
+ # /home/user/app/pages/2_Consult.py
2
  import streamlit as st
3
  from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
4
  from datetime import datetime
5
+ from typing import List, Optional # Corrected import for List and Optional
6
 
7
  from config.settings import settings
8
  from agent import get_agent_executor
9
+ from models import ChatMessage, ChatSession, User # User might not be needed directly if ID is used
10
+ from models.db import get_session_context # Or from models import get_session_context
11
  from services.logger import app_logger
12
+ from services.metrics import log_consultation_start # Assuming this function exists
13
 
14
+ # Page config typically in app.py
15
+ # st.set_page_config(page_title=f"Consult - {settings.APP_TITLE}", layout="wide")
16
 
17
+ # --- Authentication Check ---
18
+ if not st.session_state.get("authenticated_user_id"):
19
  st.warning("Please log in to access the consultation page.")
20
+ try:
21
+ st.switch_page("app.py")
22
+ except st.errors.StreamlitAPIException as e:
23
+ if "st.switch_page can only be called when running in MPA mode" in str(e):
24
+ app_logger.warning("Consult: Running in single-page mode or st.switch_page issue. Stopping script.")
25
+ st.info("Please navigate to the main login page.")
26
+ else:
27
+ app_logger.error(f"Consult: Error during st.switch_page: {e}")
28
+ st.error("Redirection error. Please go to the login page manually.")
29
+ st.stop()
30
+
31
+ # Get authenticated user's ID and username
32
+ authenticated_user_id = st.session_state.get("authenticated_user_id")
33
+ authenticated_username = st.session_state.get("authenticated_username", "User")
34
+ app_logger.info(f"User {authenticated_username} (ID: {authenticated_user_id}) accessed Consult page.")
35
 
36
  # --- Initialize Agent ---
37
  try:
38
  agent_executor = get_agent_executor()
39
+ except ValueError as e: # Handles missing API key or other init issues
40
  st.error(f"Could not initialize AI Agent: {e}")
41
+ app_logger.critical(f"AI Agent initialization failed: {e}", exc_info=True)
42
+ st.stop()
43
+ except Exception as e:
44
+ st.error(f"An unexpected error occurred while initializing the AI Agent: {e}")
45
+ app_logger.critical(f"Unexpected AI Agent initialization error: {e}", exc_info=True)
46
  st.stop()
47
 
48
 
49
  # --- Helper Functions ---
50
+ @st.cache_data(ttl=60) # Short cache for chat history to avoid constant DB hits on reruns
51
+ def load_chat_history_for_agent(session_id: int) -> List: # Type hint for return
52
+ """Loads chat history from DB for the current session, formatted for LangChain agent."""
53
  messages = []
54
+ app_logger.debug(f"Loading agent chat history for session_id: {session_id}")
55
  with get_session_context() as db:
56
+ # If using SQLModel: from sqlmodel import select
57
+ # db_messages = db.exec(select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)).all()
58
  db_messages = db.query(ChatMessage).filter(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp).all()
59
  for msg in db_messages:
60
  if msg.role == "user":
61
  messages.append(HumanMessage(content=msg.content))
62
  elif msg.role == "assistant":
63
  messages.append(AIMessage(content=msg.content))
64
+ elif msg.role == "tool" and hasattr(msg, 'tool_call_id') and msg.tool_call_id: # Ensure tool_call_id exists
65
+ messages.append(ToolMessage(content=msg.content, tool_call_id=str(msg.tool_call_id))) # Cast to str just in case
66
+ # Add other roles if necessary
67
+ app_logger.debug(f"Loaded {len(messages)} messages for agent history for session {session_id}.")
68
  return messages
69
 
70
+ def save_chat_message_to_db(session_id: int, role: str, content: str, tool_call_id: Optional[str]=None, tool_name: Optional[str]=None):
71
  """Saves a chat message to the database."""
72
+ app_logger.debug(f"Saving message to DB for session {session_id}: Role={role}, Content snippet='{content[:50]}...'")
73
  with get_session_context() as db:
74
  chat_message = ChatMessage(
75
  session_id=session_id,
 
81
  )
82
  db.add(chat_message)
83
  db.commit()
84
+ app_logger.info(f"Message saved to DB for session {session_id}. Role: {role}.")
85
 
86
  # --- Page Logic ---
87
  st.title("AI Consultation Room")
88
+ st.markdown(f"Interacting as: **{authenticated_username}**")
89
 
 
90
  chat_session_id = st.session_state.get("current_chat_session_id")
91
 
92
  if not chat_session_id:
93
+ st.error("No active chat session ID found in session state. This might happen if you logged in before this feature was fully active. Please try logging out and logging back in.")
94
+ app_logger.error(f"User {authenticated_username} (ID: {authenticated_user_id}) on Consult page with no current_chat_session_id.")
95
  st.stop()
96
 
97
+ # Initialize agent's chat history if not already present for this session_id
98
+ # We use a more specific key for agent_chat_history to handle session changes
99
+ agent_history_key = f"agent_chat_history_{chat_session_id}"
100
+ if agent_history_key not in st.session_state:
101
+ st.session_state[agent_history_key] = load_chat_history_for_agent(chat_session_id)
102
+ if not st.session_state[agent_history_key]: # If no history, maybe add a system greeting
103
+ try:
104
+ log_consultation_start(user_id=authenticated_user_id, session_id=chat_session_id)
105
+ except Exception as e:
106
+ app_logger.warning(f"Failed to log consultation start: {e}")
107
+ initial_ai_message_content = "Hello! I am your AI Health Navigator. How can I assist you today?"
108
+ st.session_state[agent_history_key].append(AIMessage(content=initial_ai_message_content))
109
+ save_chat_message_to_db(chat_session_id, "assistant", initial_ai_message_content)
110
+ app_logger.info(f"Initialized new consultation for session {chat_session_id} with a greeting.")
111
+
112
+
113
+ # Display chat messages for UI (always fetch fresh from DB for UI consistency)
114
+ # This ensures UI reflects what's actually in the DB.
115
+ with st.container(): # Use a container for the chat display area
116
+ with get_session_context() as db:
117
+ # If using SQLModel: from sqlmodel import select
118
+ # ui_messages = db.exec(select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)).all()
119
+ ui_messages = db.query(ChatMessage).filter(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp).all()
120
+ for msg in ui_messages:
121
+ avatar = "πŸ§‘β€βš•οΈ" if msg.role == "assistant" else "πŸ‘€"
122
+ if msg.role == "tool": avatar = "πŸ› οΈ"
123
+
124
+ with st.chat_message(msg.role, avatar=avatar):
125
+ st.markdown(msg.content)
126
 
127
  # Chat input
128
+ if prompt := st.chat_input("Ask the AI... (e.g., 'What is hypertension?' or 'Suggest diagnostic tests for chest pain')"):
129
+ # Add user message to UI immediately (optimistic update)
130
+ with st.chat_message("user", avatar="πŸ‘€"):
131
  st.markdown(prompt)
132
+ # Save user message to DB
133
+ save_chat_message_to_db(chat_session_id, "user", prompt)
134
  # Add to agent's history (LangChain format)
135
+ st.session_state[agent_history_key].append(HumanMessage(content=prompt))
136
 
137
  # Get AI response
138
+ with st.chat_message("assistant", avatar="πŸ§‘β€βš•οΈ"): # Prepare AI's chat message bubble
139
+ with st.spinner("AI is thinking..."):
140
+ try:
141
+ response = agent_executor.invoke({
142
+ "input": prompt,
143
+ "chat_history": st.session_state[agent_history_key] # Pass the current agent history
144
+ })
145
+ ai_response_content = response.get('output', "No output from AI.")
146
+ if not isinstance(ai_response_content, str): # Ensure it's a string
147
+ ai_response_content = str(ai_response_content)
148
+
149
+ st.markdown(ai_response_content) # Display AI response in UI
150
+ save_chat_message_to_db(chat_session_id, "assistant", ai_response_content) # Save to DB
151
+ st.session_state[agent_history_key].append(AIMessage(content=ai_response_content)) # Add to agent's history
152
+
153
+ except Exception as e:
154
+ app_logger.error(f"Error during agent invocation for session {chat_session_id}: {e}", exc_info=True)
155
+ error_message_user = f"Sorry, I encountered an error and could not process your request. Please try again or rephrase. (Error: {type(e).__name__})"
156
+ st.error(error_message_user) # Display error in the AI's bubble
157
+ # Save a generic error message to DB for the assistant's turn
158
+ save_chat_message_to_db(chat_session_id, "assistant", f"Error processing request: {type(e).__name__}")
159
+ # Add error representation to agent history so it's aware
160
+ st.session_state[agent_history_key].append(AIMessage(content=f"Observed internal error: {type(e).__name__}"))
161
+
162
+ # A full st.rerun() can be a bit disruptive if not needed.
163
+ # Streamlit's chat_input and context managers usually handle updates well.
164
+ # If messages aren't appearing correctly, a targeted rerun might be useful.
165
+ # st.rerun()