Update pages/2_Consult.py
Browse files- 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 #
|
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 |
-
|
|
|
13 |
|
14 |
-
|
|
|
15 |
st.warning("Please log in to access the consultation page.")
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
28 |
-
|
|
|
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 |
-
|
38 |
-
|
39 |
-
#
|
|
|
40 |
return messages
|
41 |
|
42 |
-
def
|
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("
|
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
|
|
|
65 |
st.stop()
|
66 |
|
67 |
-
#
|
68 |
-
#
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
with
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
# Chat input
|
87 |
-
if prompt := st.chat_input("Ask the AI... (e.g., 'What is hypertension?' or '
|
88 |
-
# Add user message to UI
|
89 |
-
with st.chat_message("user"):
|
90 |
st.markdown(prompt)
|
91 |
-
|
92 |
-
|
93 |
# Add to agent's history (LangChain format)
|
94 |
-
st.session_state.
|
95 |
|
96 |
# Get AI response
|
97 |
-
with st.
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
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()
|
|
|
|
|
|
|
|
|
|