NAVADA Claude commited on
Commit
c6ce1a6
·
1 Parent(s): b25dcfb

Fix IPython import error preventing Chainlit startup

Browse files

- Remove unused IPython.display import from app.py
- Add auth_manager.py for authentication functionality
- Resolves ModuleNotFoundError that was blocking application startup
- Application now starts successfully on Hugging Face Spaces

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +0 -1
  2. auth_manager.py +310 -0
app.py CHANGED
@@ -48,7 +48,6 @@ from dataclasses import dataclass, field # For structured data classes
48
  from openai import OpenAI # OpenAI API client for GPT model interactions
49
  from dotenv import load_dotenv # Load environment variables from .env file
50
  import uuid # For generating unique thread/session IDs
51
- from IPython.display import display # IPython display utilities (not actively used)
52
  # Optional ML imports - handle gracefully if not available (for Vercel size limits)
53
  try:
54
  from sklearn.model_selection import train_test_split # Split data for ML training
 
48
  from openai import OpenAI # OpenAI API client for GPT model interactions
49
  from dotenv import load_dotenv # Load environment variables from .env file
50
  import uuid # For generating unique thread/session IDs
 
51
  # Optional ML imports - handle gracefully if not available (for Vercel size limits)
52
  try:
53
  from sklearn.model_selection import train_test_split # Split data for ML training
auth_manager.py ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Authentication Manager Module for NAVADA
3
+ Handles user authentication, session management, and conversation storage.
4
+ """
5
+
6
+ import hashlib
7
+ import secrets
8
+ import sqlite3
9
+ import json
10
+ from datetime import datetime, timedelta
11
+ from typing import Dict, Any, Optional, List
12
+ import logging
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class AuthManager:
17
+ """Simple authentication manager with SQLite backend."""
18
+
19
+ def __init__(self, db_path: str = "navada_auth.db"):
20
+ self.db_path = db_path
21
+ self.init_database()
22
+
23
+ def init_database(self):
24
+ """Initialize database tables."""
25
+ try:
26
+ # Use timeout to prevent hanging
27
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
28
+ cursor = conn.cursor()
29
+
30
+ # Set pragmas for better performance
31
+ cursor.execute("PRAGMA journal_mode=WAL")
32
+ cursor.execute("PRAGMA synchronous=NORMAL")
33
+
34
+ # Users table
35
+ cursor.execute('''
36
+ CREATE TABLE IF NOT EXISTS users (
37
+ user_id TEXT PRIMARY KEY,
38
+ username TEXT UNIQUE NOT NULL,
39
+ email TEXT UNIQUE,
40
+ password_hash TEXT NOT NULL,
41
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
42
+ )
43
+ ''')
44
+
45
+ # Sessions table
46
+ cursor.execute('''
47
+ CREATE TABLE IF NOT EXISTS sessions (
48
+ session_token TEXT PRIMARY KEY,
49
+ user_id TEXT NOT NULL,
50
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51
+ expires_at TIMESTAMP,
52
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
53
+ )
54
+ ''')
55
+
56
+ # Conversations table
57
+ cursor.execute('''
58
+ CREATE TABLE IF NOT EXISTS conversations (
59
+ conversation_id TEXT PRIMARY KEY,
60
+ user_id TEXT NOT NULL,
61
+ thread_id TEXT,
62
+ mode TEXT,
63
+ messages TEXT,
64
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
65
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
66
+ )
67
+ ''')
68
+
69
+ # User actions log table
70
+ cursor.execute('''
71
+ CREATE TABLE IF NOT EXISTS user_actions (
72
+ action_id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ user_id TEXT NOT NULL,
74
+ action_type TEXT,
75
+ details TEXT,
76
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
77
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
78
+ )
79
+ ''')
80
+
81
+ conn.commit()
82
+ logger.info("Database tables initialized successfully")
83
+ except sqlite3.OperationalError as e:
84
+ if "database is locked" in str(e):
85
+ logger.error("Database is locked - another process may be using it")
86
+ else:
87
+ logger.error(f"Database operational error: {e}")
88
+ except Exception as e:
89
+ logger.error(f"Database initialization error: {e}")
90
+
91
+ def hash_password(self, password: str) -> str:
92
+ """Hash a password using SHA256."""
93
+ return hashlib.sha256(password.encode()).hexdigest()
94
+
95
+ def generate_token(self) -> str:
96
+ """Generate a secure random token."""
97
+ return secrets.token_urlsafe(32)
98
+
99
+ def register_user(self, username: str, password: str, email: Optional[str] = None) -> Dict[str, Any]:
100
+ """Register a new user."""
101
+ try:
102
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
103
+ cursor = conn.cursor()
104
+
105
+ # Check if user exists
106
+ cursor.execute("SELECT username FROM users WHERE username = ?", (username,))
107
+ if cursor.fetchone():
108
+ return {"success": False, "message": "Username already exists"}
109
+
110
+ # Create user
111
+ user_id = secrets.token_urlsafe(16)
112
+ password_hash = self.hash_password(password)
113
+
114
+ cursor.execute('''
115
+ INSERT INTO users (user_id, username, email, password_hash)
116
+ VALUES (?, ?, ?, ?)
117
+ ''', (user_id, username, email, password_hash))
118
+
119
+ conn.commit()
120
+
121
+ return {
122
+ "success": True,
123
+ "message": "User registered successfully",
124
+ "user_id": user_id
125
+ }
126
+ except Exception as e:
127
+ logger.error(f"Registration error: {e}")
128
+ return {"success": False, "message": str(e)}
129
+
130
+ def authenticate_user(self, username: str, password: str) -> Dict[str, Any]:
131
+ """Authenticate a user and create a session."""
132
+ try:
133
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
134
+ cursor = conn.cursor()
135
+
136
+ # Get user
137
+ password_hash = self.hash_password(password)
138
+ cursor.execute('''
139
+ SELECT user_id, username FROM users
140
+ WHERE username = ? AND password_hash = ?
141
+ ''', (username, password_hash))
142
+
143
+ user = cursor.fetchone()
144
+ if not user:
145
+ return {"success": False, "message": "Invalid credentials"}
146
+
147
+ user_id, username = user
148
+
149
+ # Create session
150
+ session_token = self.generate_token()
151
+ auth_token = self.generate_token()
152
+ expires_at = datetime.now() + timedelta(hours=24)
153
+
154
+ cursor.execute('''
155
+ INSERT INTO sessions (session_token, user_id, expires_at)
156
+ VALUES (?, ?, ?)
157
+ ''', (session_token, user_id, expires_at))
158
+
159
+ conn.commit()
160
+
161
+ return {
162
+ "success": True,
163
+ "message": "Login successful",
164
+ "user_id": user_id,
165
+ "username": username,
166
+ "session_token": session_token,
167
+ "auth_token": auth_token
168
+ }
169
+ except Exception as e:
170
+ logger.error(f"Authentication error: {e}")
171
+ return {"success": False, "message": str(e)}
172
+
173
+ def validate_session(self, session_token: str) -> Dict[str, Any]:
174
+ """Validate a session token."""
175
+ try:
176
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
177
+ cursor = conn.cursor()
178
+
179
+ cursor.execute('''
180
+ SELECT s.user_id, s.expires_at, u.username
181
+ FROM sessions s
182
+ JOIN users u ON s.user_id = u.user_id
183
+ WHERE s.session_token = ?
184
+ ''', (session_token,))
185
+
186
+ session = cursor.fetchone()
187
+ if not session:
188
+ return {"valid": False, "message": "Invalid session"}
189
+
190
+ user_id, expires_at, username = session
191
+
192
+ # Check expiration
193
+ if datetime.fromisoformat(expires_at) < datetime.now():
194
+ return {"valid": False, "message": "Session expired"}
195
+
196
+ return {
197
+ "valid": True,
198
+ "user_id": user_id,
199
+ "username": username
200
+ }
201
+ except Exception as e:
202
+ logger.error(f"Session validation error: {e}")
203
+ return {"valid": False, "message": str(e)}
204
+
205
+ def logout_user(self, session_token: str) -> Dict[str, Any]:
206
+ """Logout user by removing session."""
207
+ try:
208
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
209
+ cursor = conn.cursor()
210
+ cursor.execute("DELETE FROM sessions WHERE session_token = ?", (session_token,))
211
+ conn.commit()
212
+ return {"success": True, "message": "Logged out successfully"}
213
+ except Exception as e:
214
+ logger.error(f"Logout error: {e}")
215
+ return {"success": False, "message": str(e)}
216
+
217
+ def save_full_conversation(self, user_id: str, thread_id: str, mode: str, messages: List[Dict]) -> Dict[str, Any]:
218
+ """Save a conversation to database."""
219
+ try:
220
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
221
+ cursor = conn.cursor()
222
+
223
+ conversation_id = secrets.token_urlsafe(16)
224
+ messages_json = json.dumps(messages)
225
+
226
+ cursor.execute('''
227
+ INSERT INTO conversations (conversation_id, user_id, thread_id, mode, messages)
228
+ VALUES (?, ?, ?, ?, ?)
229
+ ''', (conversation_id, user_id, thread_id, mode, messages_json))
230
+
231
+ conn.commit()
232
+
233
+ return {"success": True, "conversation_id": conversation_id}
234
+ except Exception as e:
235
+ logger.error(f"Save conversation error: {e}")
236
+ return {"success": False, "message": str(e)}
237
+
238
+ def get_user_conversations(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
239
+ """Get user's recent conversations."""
240
+ try:
241
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
242
+ cursor = conn.cursor()
243
+
244
+ cursor.execute('''
245
+ SELECT conversation_id, thread_id, mode, messages, created_at
246
+ FROM conversations
247
+ WHERE user_id = ?
248
+ ORDER BY created_at DESC
249
+ LIMIT ?
250
+ ''', (user_id, limit))
251
+
252
+ conversations = []
253
+ for row in cursor.fetchall():
254
+ conv_id, thread_id, mode, messages_json, created_at = row
255
+ conversations.append({
256
+ "conversation_id": conv_id,
257
+ "thread_id": thread_id,
258
+ "mode": mode,
259
+ "messages": json.loads(messages_json) if messages_json else [],
260
+ "created_at": created_at
261
+ })
262
+
263
+ return conversations
264
+ except Exception as e:
265
+ logger.error(f"Get conversations error: {e}")
266
+ return []
267
+
268
+ def log_user_action(self, user_id: str, action_type: str, details: Optional[Dict] = None) -> Dict[str, Any]:
269
+ """Log a user action."""
270
+ try:
271
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
272
+ cursor = conn.cursor()
273
+
274
+ details_json = json.dumps(details) if details else None
275
+
276
+ cursor.execute('''
277
+ INSERT INTO user_actions (user_id, action_type, details)
278
+ VALUES (?, ?, ?)
279
+ ''', (user_id, action_type, details_json))
280
+
281
+ conn.commit()
282
+
283
+ return {"success": True}
284
+ except Exception as e:
285
+ logger.error(f"Log action error: {e}")
286
+ return {"success": False, "message": str(e)}
287
+
288
+ def save_conversation(self, user_id: str, chainlit_session_id: str, role: str, content: str, persona_mode: str = None, metadata: Optional[Dict] = None) -> Dict[str, Any]:
289
+ """Save individual conversation messages with chainlit session tracking."""
290
+ try:
291
+ # Use log_user_action for individual message logging
292
+ details = {
293
+ "chainlit_session_id": chainlit_session_id,
294
+ "role": role,
295
+ "content": content,
296
+ "persona_mode": persona_mode,
297
+ "metadata": metadata or {}
298
+ }
299
+
300
+ return self.log_user_action(
301
+ user_id=user_id,
302
+ action_type=f"message_{role}",
303
+ details=details
304
+ )
305
+ except Exception as e:
306
+ logger.error(f"Save conversation message error: {e}")
307
+ return {"success": False, "message": str(e)}
308
+
309
+ # Create singleton instance
310
+ auth_manager = AuthManager()