Update app.py
Browse files
app.py
CHANGED
@@ -1,16 +1,12 @@
|
|
1 |
import streamlit as st
|
2 |
-
from pathlib import Path
|
3 |
-
|
4 |
-
from sqlalchemy.orm.exc import DetachedInstanceError as SQLAlchemyDetachedInstanceError
|
5 |
-
|
6 |
|
7 |
from config.settings import settings
|
8 |
from models import create_db_and_tables, get_session_context, User, ChatMessage, ChatSession
|
9 |
-
from models.user import UserCreate
|
10 |
from services.auth import create_user_in_db, authenticate_user
|
11 |
from services.logger import app_logger
|
12 |
-
# Agent will be initialized and used in specific pages like Consult.py
|
13 |
-
# from agent import get_agent_executor # Import in pages where needed
|
14 |
|
15 |
# --- Page Configuration ---
|
16 |
st.set_page_config(
|
@@ -21,7 +17,7 @@ st.set_page_config(
|
|
21 |
)
|
22 |
|
23 |
# --- Database Initialization ---
|
24 |
-
@st.cache_resource
|
25 |
def init_db():
|
26 |
app_logger.info("Initializing database and tables...")
|
27 |
create_db_and_tables()
|
@@ -29,15 +25,16 @@ def init_db():
|
|
29 |
|
30 |
init_db()
|
31 |
|
32 |
-
# --- Session State Initialization ---
|
33 |
-
if '
|
34 |
-
st.session_state.
|
|
|
|
|
35 |
if 'current_chat_session_id' not in st.session_state:
|
36 |
st.session_state.current_chat_session_id = None
|
37 |
if 'chat_messages' not in st.session_state:
|
38 |
st.session_state.chat_messages = []
|
39 |
|
40 |
-
|
41 |
# --- Authentication Logic ---
|
42 |
def display_login_form():
|
43 |
with st.form("login_form"):
|
@@ -47,67 +44,57 @@ def display_login_form():
|
|
47 |
submit_button = st.form_submit_button("Login")
|
48 |
|
49 |
if submit_button:
|
50 |
-
|
|
|
|
|
|
|
51 |
|
52 |
-
if
|
53 |
-
|
54 |
-
|
55 |
-
app_logger.info(f"User {username_input} logged in successfully.")
|
56 |
|
57 |
try:
|
58 |
with get_session_context() as db_session:
|
59 |
-
#
|
60 |
-
#
|
61 |
-
#
|
62 |
-
#
|
63 |
-
# a User object with its 'id' (and other essential attributes) already loaded/refreshed.
|
64 |
-
app_logger.info(f"Attempting to fetch live user details for '{username_input}' in new session.")
|
65 |
live_user = db_session.query(User).filter(User.username == username_input).first()
|
66 |
|
67 |
if not live_user:
|
68 |
-
|
69 |
-
|
70 |
-
#
|
71 |
-
st.
|
72 |
-
|
73 |
-
st.
|
74 |
-
st.rerun() # Rerun to show login page again
|
75 |
return
|
76 |
|
77 |
-
# Store
|
78 |
-
|
79 |
-
|
|
|
|
|
80 |
|
81 |
-
# Now
|
82 |
new_chat_session = ChatSession(user_id=live_user.id, title=f"Session for {live_user.username}")
|
83 |
db_session.add(new_chat_session)
|
84 |
-
db_session.commit()
|
85 |
-
|
|
|
|
|
86 |
st.session_state.current_chat_session_id = new_chat_session.id
|
87 |
-
st.session_state.chat_messages = []
|
88 |
app_logger.info(f"New chat session (ID: {new_chat_session.id}) created for user {live_user.username}.")
|
|
|
89 |
st.rerun() # Rerun to reflect login state and navigate
|
90 |
|
91 |
-
except SQLAlchemyDetachedInstanceError as detached_error:
|
92 |
-
# This specific catch might be less likely if the above workaround (re-fetching live_user) is effective.
|
93 |
-
# However, it's good for diagnostics if the issue persists due to other detached instances.
|
94 |
-
app_logger.error(
|
95 |
-
f"DetachedInstanceError occurred during session setup for user '{username_input}'. "
|
96 |
-
f"This typically means an object from 'authenticate_user' or 'create_user_in_db' "
|
97 |
-
f"had expired attributes. Error: {detached_error}",
|
98 |
-
exc_info=True
|
99 |
-
)
|
100 |
-
st.error(
|
101 |
-
"Could not start a new session due to an object state issue. "
|
102 |
-
"Please ensure user services return objects with loaded attributes."
|
103 |
-
)
|
104 |
-
st.session_state.authenticated_user = None
|
105 |
except Exception as e:
|
106 |
-
app_logger.error(f"Error
|
107 |
-
st.error(f"Could not
|
108 |
-
#
|
109 |
-
|
110 |
-
|
111 |
else:
|
112 |
st.error("Invalid username or password.")
|
113 |
app_logger.warning(f"Failed login attempt for username: {username_input}")
|
@@ -132,18 +119,16 @@ def display_signup_form():
|
|
132 |
password=new_password,
|
133 |
email=new_email if new_email else None
|
134 |
)
|
135 |
-
|
136 |
-
# (e.g., by calling session.refresh(user) before its session closes if a commit occurred).
|
137 |
-
user = create_user_in_db(user_data)
|
138 |
if user:
|
139 |
-
st.success(f"Account created for {new_username}. Please log in.")
|
140 |
app_logger.info(f"Account created for {new_username}.")
|
141 |
else:
|
142 |
st.error("Username might already be taken or another error occurred during signup.")
|
143 |
app_logger.warning(f"Signup failed for username: {new_username}")
|
144 |
|
145 |
-
# --- Main App Logic ---
|
146 |
-
if not st.session_state.
|
147 |
st.title(f"Welcome to {settings.APP_TITLE}")
|
148 |
st.markdown("Your AI-powered partner for advanced healthcare insights.")
|
149 |
|
@@ -153,14 +138,11 @@ if not st.session_state.authenticated_user:
|
|
153 |
with signup_tab:
|
154 |
display_signup_form()
|
155 |
else:
|
|
|
156 |
with st.sidebar:
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
st.markdown(f"### Welcome, {st.session_state.authenticated_user.username}!")
|
161 |
-
except AttributeError as e:
|
162 |
-
st.markdown(f"### Welcome!")
|
163 |
-
app_logger.error(f"Could not access username for authenticated_user in sidebar: {e}. User object: {st.session_state.authenticated_user}", exc_info=True)
|
164 |
|
165 |
logo_path_str = getattr(settings, "LOGO_PATH", None)
|
166 |
if logo_path_str:
|
@@ -178,14 +160,11 @@ else:
|
|
178 |
st.markdown("---")
|
179 |
|
180 |
if st.button("Logout"):
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
app_logger.info(f"User {username_on_logout} logging out.")
|
187 |
-
|
188 |
-
st.session_state.authenticated_user = None
|
189 |
st.session_state.current_chat_session_id = None
|
190 |
st.session_state.chat_messages = []
|
191 |
st.success("You have been logged out.")
|
|
|
1 |
import streamlit as st
|
2 |
+
from pathlib import Path
|
3 |
+
from sqlalchemy.orm.exc import DetachedInstanceError as SQLAlchemyDetachedInstanceError # For specific error catching if needed
|
|
|
|
|
4 |
|
5 |
from config.settings import settings
|
6 |
from models import create_db_and_tables, get_session_context, User, ChatMessage, ChatSession
|
7 |
+
from models.user import UserCreate
|
8 |
from services.auth import create_user_in_db, authenticate_user
|
9 |
from services.logger import app_logger
|
|
|
|
|
10 |
|
11 |
# --- Page Configuration ---
|
12 |
st.set_page_config(
|
|
|
17 |
)
|
18 |
|
19 |
# --- Database Initialization ---
|
20 |
+
@st.cache_resource
|
21 |
def init_db():
|
22 |
app_logger.info("Initializing database and tables...")
|
23 |
create_db_and_tables()
|
|
|
25 |
|
26 |
init_db()
|
27 |
|
28 |
+
# --- Session State Initialization (Using new keys) ---
|
29 |
+
if 'authenticated_user_id' not in st.session_state:
|
30 |
+
st.session_state.authenticated_user_id = None
|
31 |
+
if 'authenticated_username' not in st.session_state:
|
32 |
+
st.session_state.authenticated_username = None
|
33 |
if 'current_chat_session_id' not in st.session_state:
|
34 |
st.session_state.current_chat_session_id = None
|
35 |
if 'chat_messages' not in st.session_state:
|
36 |
st.session_state.chat_messages = []
|
37 |
|
|
|
38 |
# --- Authentication Logic ---
|
39 |
def display_login_form():
|
40 |
with st.form("login_form"):
|
|
|
44 |
submit_button = st.form_submit_button("Login")
|
45 |
|
46 |
if submit_button:
|
47 |
+
# authenticate_user should ideally return a User object with at least ID loaded,
|
48 |
+
# or be robust enough that its session handling doesn't cause immediate detachment issues
|
49 |
+
# if we were to access its attributes directly (though we now avoid that for long-term storage).
|
50 |
+
user_object_from_auth = authenticate_user(username_input, password_input)
|
51 |
|
52 |
+
if user_object_from_auth:
|
53 |
+
st.success(f"Welcome back, {username_input}!") # Use form input for immediate message
|
54 |
+
app_logger.info(f"User {username_input} authenticated successfully.")
|
|
|
55 |
|
56 |
try:
|
57 |
with get_session_context() as db_session:
|
58 |
+
# Fetch the "live" user to get definite ID and current username,
|
59 |
+
# and to ensure it's attached to *this* session for creating the chat session.
|
60 |
+
# The primary fix for 'authenticate_user' is still for it to return a usable object,
|
61 |
+
# but re-fetching here ensures data for st.session_state is from a controlled point.
|
|
|
|
|
62 |
live_user = db_session.query(User).filter(User.username == username_input).first()
|
63 |
|
64 |
if not live_user:
|
65 |
+
st.error("Authentication inconsistency. User details not found after login. Please contact support.")
|
66 |
+
app_logger.error(f"CRITICAL: User '{username_input}' authenticated but then not found in DB by username.")
|
67 |
+
# Clear any potentially partially set auth state from previous attempts
|
68 |
+
st.session_state.authenticated_user_id = None
|
69 |
+
st.session_state.authenticated_username = None
|
70 |
+
st.rerun()
|
|
|
71 |
return
|
72 |
|
73 |
+
# IMPORTANT: Store primitive data in st.session_state
|
74 |
+
# Do this *before* the commit for new_chat_session, as commit might expire 'live_user' attributes.
|
75 |
+
st.session_state.authenticated_user_id = live_user.id
|
76 |
+
st.session_state.authenticated_username = live_user.username # Username from DB
|
77 |
+
app_logger.info(f"Stored user ID {live_user.id} and username '{live_user.username}' in session state.")
|
78 |
|
79 |
+
# Now create the chat session using the live_user's ID
|
80 |
new_chat_session = ChatSession(user_id=live_user.id, title=f"Session for {live_user.username}")
|
81 |
db_session.add(new_chat_session)
|
82 |
+
db_session.commit() # This commit is for new_chat_session
|
83 |
+
# After commit, live_user attributes might be expired if expire_on_commit=True.
|
84 |
+
# But we've already stored the primitives we need.
|
85 |
+
db_session.refresh(new_chat_session) # Get ID for new_chat_session
|
86 |
st.session_state.current_chat_session_id = new_chat_session.id
|
87 |
+
st.session_state.chat_messages = []
|
88 |
app_logger.info(f"New chat session (ID: {new_chat_session.id}) created for user {live_user.username}.")
|
89 |
+
|
90 |
st.rerun() # Rerun to reflect login state and navigate
|
91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
except Exception as e:
|
93 |
+
app_logger.error(f"Error during post-login session setup for user {username_input}: {e}", exc_info=True)
|
94 |
+
st.error(f"Could not complete login process: {e}")
|
95 |
+
# Clear auth state on error
|
96 |
+
st.session_state.authenticated_user_id = None
|
97 |
+
st.session_state.authenticated_username = None
|
98 |
else:
|
99 |
st.error("Invalid username or password.")
|
100 |
app_logger.warning(f"Failed login attempt for username: {username_input}")
|
|
|
119 |
password=new_password,
|
120 |
email=new_email if new_email else None
|
121 |
)
|
122 |
+
user = create_user_in_db(user_data) # Should handle its own session and return User or None
|
|
|
|
|
123 |
if user:
|
124 |
+
st.success(f"Account created for {new_username}. Please log in.") # Use form input
|
125 |
app_logger.info(f"Account created for {new_username}.")
|
126 |
else:
|
127 |
st.error("Username might already be taken or another error occurred during signup.")
|
128 |
app_logger.warning(f"Signup failed for username: {new_username}")
|
129 |
|
130 |
+
# --- Main App Logic (Checks for authenticated_user_id) ---
|
131 |
+
if not st.session_state.get("authenticated_user_id"): # Check if user_id is set
|
132 |
st.title(f"Welcome to {settings.APP_TITLE}")
|
133 |
st.markdown("Your AI-powered partner for advanced healthcare insights.")
|
134 |
|
|
|
138 |
with signup_tab:
|
139 |
display_signup_form()
|
140 |
else:
|
141 |
+
# User is authenticated (authenticated_user_id is present)
|
142 |
with st.sidebar:
|
143 |
+
# Use the stored primitive username
|
144 |
+
username_for_display = st.session_state.get("authenticated_username", "User")
|
145 |
+
st.markdown(f"### Welcome, {username_for_display}!")
|
|
|
|
|
|
|
|
|
146 |
|
147 |
logo_path_str = getattr(settings, "LOGO_PATH", None)
|
148 |
if logo_path_str:
|
|
|
160 |
st.markdown("---")
|
161 |
|
162 |
if st.button("Logout"):
|
163 |
+
logged_out_username = st.session_state.get("authenticated_username", "UnknownUser")
|
164 |
+
app_logger.info(f"User {logged_out_username} logging out.")
|
165 |
+
# Clear authentication state
|
166 |
+
st.session_state.authenticated_user_id = None
|
167 |
+
st.session_state.authenticated_username = None
|
|
|
|
|
|
|
168 |
st.session_state.current_chat_session_id = None
|
169 |
st.session_state.chat_messages = []
|
170 |
st.success("You have been logged out.")
|