File size: 12,305 Bytes
45ea80d
2b9aa0c
45ea80d
2b9aa0c
45ea80d
861d237
2b9aa0c
 
45ea80d
861d237
352a295
2b9aa0c
3c808a4
2b9aa0c
861d237
71a1c43
2b9aa0c
7ae304b
3c808a4
7ae304b
 
3c808a4
71a1c43
45ea80d
3c808a4
861d237
2b9aa0c
45ea80d
2b9aa0c
743ac85
45ea80d
3c808a4
7ae304b
45ea80d
 
7ae304b
3c808a4
45ea80d
 
71a1c43
2b9aa0c
45ea80d
 
 
 
 
 
3c808a4
 
 
 
45ea80d
 
 
 
 
 
 
 
 
 
 
 
 
3c808a4
45ea80d
 
 
 
 
 
 
 
 
 
3c808a4
45ea80d
 
 
 
 
 
 
 
3c808a4
45ea80d
 
3c808a4
45ea80d
 
 
 
 
3c808a4
45ea80d
 
2b9aa0c
45ea80d
 
 
2b9aa0c
 
3c808a4
45ea80d
2b9aa0c
 
45ea80d
861d237
45ea80d
3c808a4
 
 
45ea80d
3c808a4
 
 
 
45ea80d
 
 
3c808a4
 
45ea80d
3c808a4
45ea80d
 
 
 
3c808a4
 
45ea80d
3c808a4
45ea80d
3c808a4
45ea80d
 
 
3c808a4
 
 
 
 
 
 
 
 
45ea80d
 
3c808a4
861d237
 
45ea80d
3c808a4
 
 
 
 
 
 
45ea80d
3c808a4
 
45ea80d
 
 
3c808a4
45ea80d
3c808a4
 
71a1c43
45ea80d
3c808a4
 
45ea80d
 
3c808a4
45ea80d
7ae304b
45ea80d
3c808a4
45ea80d
3c808a4
45ea80d
 
 
3c808a4
 
45ea80d
 
1aff0c6
45ea80d
 
3c808a4
 
 
45ea80d
 
3c808a4
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# /home/user/app/pages/2_Consult.py
import streamlit as st
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from datetime import datetime
from typing import List, Optional, Dict, Any
from sqlmodel import select

from config.settings import settings
from agent import get_agent_executor # This now returns the OpenAI-based agent executor
from models import ChatMessage, ChatSession
from models.db import get_session_context
from services.logger import app_logger
from services.metrics import log_consultation_start

# --- Authentication Check ---
if not st.session_state.get("authenticated_user_id"):
    st.warning("Please log in to access the consultation page.")
    try:
        st.switch_page("app.py")
    except st.errors.StreamlitAPIException:
        st.info("Please navigate to the main login page.")
    st.stop()

authenticated_user_id = st.session_state.get("authenticated_user_id")
authenticated_username = st.session_state.get("authenticated_username", "User")
app_logger.info(f"User '{authenticated_username}' (ID: {authenticated_user_id}) accessed Consult page.")

# --- Initialize Agent ---
try:
    agent_executor = get_agent_executor()
    app_logger.info("OpenAI-based agent executor initialized successfully for Consult page.")
except ValueError as e:
    st.error(f"AI Agent Initialization Error: {e}")
    app_logger.critical(f"Fatal: AI Agent could not be initialized in Consult page: {e}", exc_info=True)
    st.info("Please ensure the OPENAI_API_KEY is correctly configured in the application settings (Hugging Face Secrets).")
    st.stop()
except Exception as e:
    st.error(f"An unexpected error occurred while initializing the AI Agent: {e}")
    app_logger.critical(f"Fatal: Unexpected AI Agent initialization error: {e}", exc_info=True)
    st.stop()

# --- Session State for Consult Page ---
if 'current_consult_patient_context_dict' not in st.session_state:
    st.session_state.current_consult_patient_context_dict = {}
if 'consult_context_submitted' not in st.session_state:
    st.session_state.consult_context_submitted = False

# --- Helper Functions (load_chat_history_for_agent, save_chat_message_to_db, update_chat_session_with_context_summary_in_db) ---
# ... (These functions are identical to the previous full 2_Consult.py provided - no changes needed here for this specific error) ...
@st.cache_data(ttl=30, show_spinner=False, max_entries=10)
def load_chat_history_for_agent(session_id: int) -> List[Any]:
    messages = []
    app_logger.debug(f"Loading agent chat history from DB for session_id: {session_id}")
    try:
        with get_session_context() as db:
            statement = select(ChatMessage).where(ChatMessage.session_id == session_id).order_by(ChatMessage.timestamp)
            db_messages = db.exec(statement).all()
            for msg in db_messages:
                if msg.role == "user": messages.append(HumanMessage(content=msg.content))
                elif msg.role == "assistant": messages.append(AIMessage(content=msg.content))
                elif msg.role == "system": messages.append(SystemMessage(content=msg.content))
        app_logger.debug(f"Loaded {len(messages)} LangChain messages for agent history (session {session_id}).")
    except Exception as e:
        app_logger.error(f"Error loading chat history for session {session_id}: {e}", exc_info=True)
        st.toast(f"Error loading history: {e}", icon="⚠️")
    return messages

def save_chat_message_to_db(session_id: int, role: str, content: str, tool_call_id: Optional[str]=None, tool_name: Optional[str]=None):
    app_logger.debug(f"Saving message to DB for session {session_id}: Role='{role}', Content snippet='{content[:50]}...'")
    try:
        with get_session_context() as db:
            chat_message_obj = ChatMessage(
                session_id=session_id, role=role, content=content, timestamp=datetime.utcnow(),
                tool_call_id=tool_call_id, tool_name=tool_name
            )
            db.add(chat_message_obj)
        app_logger.info(f"Message (Role: {role}) saved to DB for session {session_id}.")
    except Exception as e:
        app_logger.error(f"Error saving chat message to DB for session {session_id}: {e}", exc_info=True)
        st.toast(f"Error saving message: {e}", icon="⚠️")

def update_chat_session_with_context_summary_in_db(session_id: int, context_summary: str):
    try:
        with get_session_context() as db:
            session_to_update = db.get(ChatSession, session_id)
            if session_to_update:
                session_to_update.patient_context_summary = context_summary
                db.add(session_to_update)
                app_logger.info(f"Updated ChatSession {session_id} with patient context summary in DB.")
            else:
                app_logger.error(f"Could not find ChatSession {session_id} in DB to update context summary.")
    except Exception as e:
        app_logger.error(f"Error updating chat session {session_id} context summary: {e}", exc_info=True)
        st.toast(f"Error saving context: {e}", icon="⚠️")

# --- Page Logic ---
st.title("AI Consultation Room")
st.markdown(f"Interacting as: **{authenticated_username}**")
st.warning(f"**Reminder & Disclaimer:** {settings.MAIN_DISCLAIMER_LONG} {settings.SIMULATION_DISCLAIMER}")

chat_session_id = st.session_state.get("current_chat_session_id")
if not chat_session_id:
    st.error("Error: No active chat session ID found. Please try logging out and back in.")
    app_logger.critical(f"User '{authenticated_username}' (ID: {authenticated_user_id}) on Consult page encountered MISSING current_chat_session_id.")
    st.stop()

# --- Patient Context Input Form ---
if not st.session_state.consult_context_submitted:
    st.subheader("Step 1: Provide Patient Context (Optional, Use Simulated Data Only)")
    # ... (Form logic remains the same as previous full version of 2_Consult.py) ...
    with st.form(key="patient_context_form_consult_page_openai_v2"): # Unique key
        st.markdown("**Crucial Reminder: Use only anonymized, simulated data. Do NOT enter real PHI.**")
        age_in = st.number_input("Patient Age (Simulated)", min_value=0, max_value=120, step=1, value=None, help="Leave blank if not applicable.")
        gender_in = st.selectbox("Patient Gender (Simulated)", ["Not Specified", "Male", "Female", "Other"], index=0)
        cc_in = st.text_area("Chief Complaint / Reason for Consult (Simulated)", height=100, placeholder="e.g., Persistent cough")
        hist_in = st.text_area("Key Medical History (Simulated)", height=100, placeholder="e.g., Type 2 Diabetes")
        meds_in = st.text_area("Current Medications (Simulated)", height=100, placeholder="e.g., Metformin")
        submit_context_btn = st.form_submit_button("Start Consult with this Context")

        if submit_context_btn:
            raw_context = {"Age": age_in, "Gender": gender_in, "Chief Complaint": cc_in, "Key Medical History": hist_in, "Current Medications": meds_in}
            filtered_context_dict = { k: v for k, v in raw_context.items() if v is not None and str(v).strip() and (isinstance(v, str) and v.lower() != "not specified") and (isinstance(v, int) and v > 0 or not isinstance(v, int))}
            st.session_state.current_consult_patient_context_dict = filtered_context_dict
            context_summary_str = "; ".join([f"{k}: {v}" for k, v in filtered_context_dict.items()]) if filtered_context_dict else "No specific patient context was provided."
            update_chat_session_with_context_summary_in_db(chat_session_id, context_summary_str)
            save_chat_message_to_db(chat_session_id, "system", f"Initial Patient Context Set: {context_summary_str}")
            st.session_state.consult_context_submitted = True
            app_logger.info(f"Patient context submitted for session {chat_session_id}: {context_summary_str}")
            st.rerun()
    st.stop()

# --- Chat Interface ---
st.subheader("Step 2: Interact with AI Health Navigator")
agent_history_key = f"agent_chat_history_{chat_session_id}"

if agent_history_key not in st.session_state:
    st.session_state[agent_history_key] = load_chat_history_for_agent(chat_session_id)
    if not st.session_state[agent_history_key]:
        try: log_consultation_start(user_id=authenticated_user_id, session_id=chat_session_id)
        except Exception as e_metric: app_logger.warning(f"Failed log_consultation_start: {e_metric}")
        initial_ai_msg = "Hello! I am your AI Health Navigator. How can I assist you today?"
        if st.session_state.get('current_consult_patient_context_dict'):
            initial_ai_msg += " I have noted the patient context you provided."
        st.session_state[agent_history_key].append(AIMessage(content=initial_ai_msg))
        save_chat_message_to_db(chat_session_id, "assistant", initial_ai_msg)

chat_display_container = st.container(height=450)
with chat_display_container:
    # ... (Chat message display loop - same as previous full version) ...
    with get_session_context() as db:
        stmt = select(ChatMessage).where(ChatMessage.session_id == chat_session_id).order_by(ChatMessage.timestamp)
        ui_messages_from_db = db.exec(stmt).all()
        for msg_db in ui_messages_from_db: # Renamed msg to msg_db to avoid conflict
            if msg_db.role == "system": continue
            avatar_icon = "πŸ§‘β€βš•οΈ" if msg_db.role == "assistant" else "πŸ‘€"
            if msg_db.role == "tool": avatar_icon = "πŸ› οΈ"
            with st.chat_message(msg_db.role, avatar=avatar_icon):
                st.markdown(msg_db.content)

if user_prompt := st.chat_input("Ask the AI... (e.g., 'What is hypertension?')"):
    with chat_display_container:
        with st.chat_message("user", avatar="πŸ‘€"): st.markdown(user_prompt)
    save_chat_message_to_db(chat_session_id, "user", user_prompt)
    st.session_state[agent_history_key].append(HumanMessage(content=user_prompt))

    with chat_display_container:
        with st.chat_message("assistant", avatar="πŸ§‘β€βš•οΈ"):
            thinking_msg_placeholder = st.empty()
            thinking_msg_placeholder.markdown("β–Œ")
            try:
                patient_context_dict = st.session_state.get('current_consult_patient_context_dict', {})
                patient_context_str_for_invoke = "; ".join([f"{k}: {v}" for k,v in patient_context_dict.items()]) if patient_context_dict else "No specific patient context provided."
                
                invoke_payload = {
                    "input": user_prompt,
                    "chat_history": st.session_state[agent_history_key],
                    "patient_context": patient_context_str_for_invoke
                }
                app_logger.debug(f"Invoking OpenAI agent with payload: {invoke_payload}")
                thinking_msg_placeholder.markdown("AI is thinking...")
                response = agent_executor.invoke(invoke_payload)
                ai_response_content = response.get('output', "Could not generate a response.")
                if not isinstance(ai_response_content, str): ai_response_content = str(ai_response_content)
                
                app_logger.info(f"OpenAI Agent response for session {chat_session_id}: '{ai_response_content[:100]}...'")
                thinking_msg_placeholder.empty()
                st.markdown(ai_response_content)
                save_chat_message_to_db(chat_session_id, "assistant", ai_response_content)
                st.session_state[agent_history_key].append(AIMessage(content=ai_response_content))
            except Exception as e:
                app_logger.error(f"Error during OpenAI agent invocation for session {chat_session_id}: {e}", exc_info=True)
                error_type_name = type(e).__name__
                user_friendly_error = f"Sorry, an error occurred ({error_type_name}). Please try rephrasing or contact support."
                thinking_msg_placeholder.empty()
                st.error(user_friendly_error)
                db_error_msg = f"System encountered an error: {error_type_name}. Details logged."
                save_chat_message_to_db(chat_session_id, "assistant", db_error_msg)
                st.session_state[agent_history_key].append(AIMessage(content=f"Note: Error ({error_type_name})."))
    st.rerun()