Dhruv-Ty commited on
Commit
0a90f16
·
verified ·
1 Parent(s): e40754a

Update src/database.py

Browse files
Files changed (1) hide show
  1. src/database.py +174 -84
src/database.py CHANGED
@@ -4,6 +4,7 @@ Database module for handling conversation persistence using Supabase.
4
 
5
  import os
6
  import json
 
7
  from datetime import datetime
8
  import requests
9
  from dotenv import load_dotenv
@@ -11,6 +12,11 @@ from dotenv import load_dotenv
11
  # Load environment variables
12
  load_dotenv()
13
 
 
 
 
 
 
14
  class SupabaseClient:
15
  def __init__(self):
16
  """Initialize Supabase client with credentials from environment variables."""
@@ -18,8 +24,11 @@ class SupabaseClient:
18
  self.supabase_key = os.getenv('SUPABASE_KEY')
19
 
20
  if not self.supabase_url or not self.supabase_key:
 
21
  raise ValueError("Supabase credentials not found in environment variables. "
22
  "Please set SUPABASE_URL and SUPABASE_KEY.")
 
 
23
 
24
  def _make_request(self, method, endpoint, data=None, params=None):
25
  """Make a request to Supabase API."""
@@ -31,40 +40,65 @@ class SupabaseClient:
31
  "Prefer": "return=representation"
32
  }
33
 
34
- if method == "GET":
35
- response = requests.get(url, headers=headers, params=params)
36
- elif method == "POST":
37
- response = requests.post(url, headers=headers, json=data)
38
- elif method == "PUT":
39
- response = requests.put(url, headers=headers, json=data)
40
- elif method == "DELETE":
41
- response = requests.delete(url, headers=headers, params=params)
42
- else:
43
- raise ValueError(f"Unsupported method: {method}")
44
-
45
- return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  def create_conversation(self, consultation_id):
48
  """Create a new conversation record in the database."""
 
 
49
  data = {
50
  "consultation_id": consultation_id,
51
  "created_at": datetime.now().isoformat(),
52
  "is_active": True
53
  }
54
 
55
- response = self._make_request(
56
- "POST",
57
- "/rest/v1/conversations",
58
- data=data
59
- )
60
-
61
- if response.status_code not in (200, 201):
62
- raise Exception(f"Failed to create conversation: {response.text}")
63
-
64
- return response.json()
 
 
 
 
 
 
 
65
 
66
  def save_message(self, consultation_id, message):
67
  """Save a single message to the database."""
 
 
68
  # Normalize message format for database storage
69
  message_data = {
70
  "consultation_id": consultation_id,
@@ -79,93 +113,149 @@ class SupabaseClient:
79
 
80
  if "evidence" in message:
81
  # Convert evidence to string for storage
82
- message_data["evidence"] = json.dumps(message.get("evidence"))
83
-
84
- response = self._make_request(
85
- "POST",
86
- "/rest/v1/messages",
87
- data=message_data
88
- )
89
-
90
- if response.status_code not in (200, 201):
91
- raise Exception(f"Failed to save message: {response.text}")
92
 
93
- return response.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  def get_conversation_history(self, consultation_id):
96
  """Retrieve full conversation history for a given consultation ID."""
 
 
97
  params = {
98
  "consultation_id": f"eq.{consultation_id}",
99
  "order": "timestamp.asc"
100
  }
101
 
102
- response = self._make_request(
103
- "GET",
104
- "/rest/v1/messages",
105
- params=params
106
- )
107
-
108
- if response.status_code != 200:
109
- raise Exception(f"Failed to retrieve conversation history: {response.text}")
110
-
111
- # Convert database format back to application format
112
- messages = response.json()
113
- history = []
114
-
115
- for msg in messages:
116
- message_dict = {
117
- "role": msg["role"],
118
- "content": msg["content"]
119
- }
120
 
121
- if msg.get("explanation"):
122
- message_dict["explanation"] = msg["explanation"]
 
123
 
124
- if msg.get("evidence"):
125
- # Parse evidence JSON string back to object
126
- try:
127
- message_dict["evidence"] = json.loads(msg["evidence"])
128
- except:
129
- message_dict["evidence"] = []
130
 
131
- history.append(message_dict)
132
-
133
- return history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  def delete_conversation(self, consultation_id):
136
  """Delete a completed conversation from the database."""
137
- # First delete all messages
138
- msg_params = {"consultation_id": f"eq.{consultation_id}"}
139
- self._make_request("DELETE", "/rest/v1/messages", params=msg_params)
140
-
141
- # Then delete conversation record
142
- conv_params = {"consultation_id": f"eq.{consultation_id}"}
143
- response = self._make_request("DELETE", "/rest/v1/conversations", params=conv_params)
144
-
145
- if response.status_code != 200:
146
- raise Exception(f"Failed to delete conversation: {response.text}")
147
 
148
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
  def get_active_conversations(self):
151
  """Get list of active conversations."""
 
 
152
  params = {
153
  "is_active": "eq.true",
154
  "order": "created_at.desc"
155
  }
156
 
157
- response = self._make_request(
158
- "GET",
159
- "/rest/v1/conversations",
160
- params=params
161
- )
162
-
163
- if response.status_code != 200:
164
- raise Exception(f"Failed to retrieve active conversations: {response.text}")
165
-
166
- return response.json()
 
 
 
 
 
 
 
 
167
 
168
  # Initialize database client
169
  def get_db_client():
170
  """Get database client instance."""
171
- return SupabaseClient()
 
 
 
 
 
4
 
5
  import os
6
  import json
7
+ import logging
8
  from datetime import datetime
9
  import requests
10
  from dotenv import load_dotenv
 
12
  # Load environment variables
13
  load_dotenv()
14
 
15
+ # Set up logging
16
+ logging.basicConfig(level=logging.INFO,
17
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
18
+ logger = logging.getLogger('database')
19
+
20
  class SupabaseClient:
21
  def __init__(self):
22
  """Initialize Supabase client with credentials from environment variables."""
 
24
  self.supabase_key = os.getenv('SUPABASE_KEY')
25
 
26
  if not self.supabase_url or not self.supabase_key:
27
+ logger.error("Supabase credentials not found in environment variables")
28
  raise ValueError("Supabase credentials not found in environment variables. "
29
  "Please set SUPABASE_URL and SUPABASE_KEY.")
30
+
31
+ logger.info(f"Initialized Supabase client with URL: {self.supabase_url[:20]}...")
32
 
33
  def _make_request(self, method, endpoint, data=None, params=None):
34
  """Make a request to Supabase API."""
 
40
  "Prefer": "return=representation"
41
  }
42
 
43
+ logger.debug(f"Making {method} request to {endpoint}")
44
+
45
+ try:
46
+ if method == "GET":
47
+ response = requests.get(url, headers=headers, params=params)
48
+ elif method == "POST":
49
+ logger.debug(f"POST data: {json.dumps(data)[:500]}...")
50
+ response = requests.post(url, headers=headers, json=data)
51
+ elif method == "PUT":
52
+ response = requests.put(url, headers=headers, json=data)
53
+ elif method == "DELETE":
54
+ response = requests.delete(url, headers=headers, params=params)
55
+ else:
56
+ raise ValueError(f"Unsupported method: {method}")
57
+
58
+ # Check response status and log appropriately
59
+ if response.status_code >= 400:
60
+ logger.error(f"API error: {method} {endpoint} returned {response.status_code}: {response.text}")
61
+ else:
62
+ logger.debug(f"API success: {method} {endpoint} returned {response.status_code}")
63
+
64
+ return response
65
+
66
+ except requests.RequestException as e:
67
+ logger.error(f"Request error: {str(e)}")
68
+ raise
69
 
70
  def create_conversation(self, consultation_id):
71
  """Create a new conversation record in the database."""
72
+ logger.info(f"Creating conversation with ID: {consultation_id}")
73
+
74
  data = {
75
  "consultation_id": consultation_id,
76
  "created_at": datetime.now().isoformat(),
77
  "is_active": True
78
  }
79
 
80
+ try:
81
+ response = self._make_request(
82
+ "POST",
83
+ "/rest/v1/conversations",
84
+ data=data
85
+ )
86
+
87
+ if response.status_code not in (200, 201):
88
+ logger.error(f"Failed to create conversation: {response.text}")
89
+ raise Exception(f"Failed to create conversation: {response.text}")
90
+
91
+ logger.info(f"Successfully created conversation: {consultation_id}")
92
+ return response.json()
93
+
94
+ except Exception as e:
95
+ logger.error(f"Error creating conversation: {str(e)}")
96
+ raise
97
 
98
  def save_message(self, consultation_id, message):
99
  """Save a single message to the database."""
100
+ logger.info(f"Saving message for conversation: {consultation_id}, role: {message.get('role')}")
101
+
102
  # Normalize message format for database storage
103
  message_data = {
104
  "consultation_id": consultation_id,
 
113
 
114
  if "evidence" in message:
115
  # Convert evidence to string for storage
116
+ try:
117
+ message_data["evidence"] = json.dumps(message.get("evidence"))
118
+ except Exception as e:
119
+ logger.error(f"Error serializing evidence: {str(e)}")
120
+ message_data["evidence"] = "[]"
 
 
 
 
 
121
 
122
+ try:
123
+ # First verify that the consultation_id exists
124
+ verify_params = {"consultation_id": f"eq.{consultation_id}"}
125
+ verify_response = self._make_request("GET", "/rest/v1/conversations", params=verify_params)
126
+
127
+ if verify_response.status_code != 200 or len(verify_response.json()) == 0:
128
+ logger.error(f"Conversation {consultation_id} not found, creating it first")
129
+ self.create_conversation(consultation_id)
130
+
131
+ # Now save the message
132
+ response = self._make_request(
133
+ "POST",
134
+ "/rest/v1/messages",
135
+ data=message_data
136
+ )
137
+
138
+ if response.status_code not in (200, 201):
139
+ logger.error(f"Failed to save message: {response.text}")
140
+ raise Exception(f"Failed to save message: {response.text}")
141
+
142
+ logger.info(f"Successfully saved message for conversation: {consultation_id}")
143
+ return response.json()
144
+
145
+ except Exception as e:
146
+ logger.error(f"Error saving message: {str(e)}")
147
+ raise
148
 
149
  def get_conversation_history(self, consultation_id):
150
  """Retrieve full conversation history for a given consultation ID."""
151
+ logger.info(f"Retrieving conversation history for: {consultation_id}")
152
+
153
  params = {
154
  "consultation_id": f"eq.{consultation_id}",
155
  "order": "timestamp.asc"
156
  }
157
 
158
+ try:
159
+ response = self._make_request(
160
+ "GET",
161
+ "/rest/v1/messages",
162
+ params=params
163
+ )
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ if response.status_code != 200:
166
+ logger.error(f"Failed to retrieve conversation history: {response.text}")
167
+ raise Exception(f"Failed to retrieve conversation history: {response.text}")
168
 
169
+ # Convert database format back to application format
170
+ messages = response.json()
171
+ history = []
 
 
 
172
 
173
+ logger.info(f"Retrieved {len(messages)} messages for conversation: {consultation_id}")
174
+
175
+ for msg in messages:
176
+ message_dict = {
177
+ "role": msg["role"],
178
+ "content": msg["content"]
179
+ }
180
+
181
+ if msg.get("explanation"):
182
+ message_dict["explanation"] = msg["explanation"]
183
+
184
+ if msg.get("evidence"):
185
+ # Parse evidence JSON string back to object
186
+ try:
187
+ message_dict["evidence"] = json.loads(msg["evidence"])
188
+ except:
189
+ message_dict["evidence"] = []
190
+
191
+ history.append(message_dict)
192
+
193
+ return history
194
+
195
+ except Exception as e:
196
+ logger.error(f"Error retrieving conversation history: {str(e)}")
197
+ raise
198
 
199
  def delete_conversation(self, consultation_id):
200
  """Delete a completed conversation from the database."""
201
+ logger.info(f"Deleting conversation: {consultation_id}")
 
 
 
 
 
 
 
 
 
202
 
203
+ try:
204
+ # First delete all messages
205
+ msg_params = {"consultation_id": f"eq.{consultation_id}"}
206
+ msg_response = self._make_request("DELETE", "/rest/v1/messages", params=msg_params)
207
+
208
+ if msg_response.status_code != 200:
209
+ logger.warning(f"Failed to delete messages for conversation {consultation_id}: {msg_response.text}")
210
+
211
+ # Then delete conversation record
212
+ conv_params = {"consultation_id": f"eq.{consultation_id}"}
213
+ response = self._make_request("DELETE", "/rest/v1/conversations", params=conv_params)
214
+
215
+ if response.status_code != 200:
216
+ logger.error(f"Failed to delete conversation: {response.text}")
217
+ raise Exception(f"Failed to delete conversation: {response.text}")
218
+
219
+ logger.info(f"Successfully deleted conversation: {consultation_id}")
220
+ return True
221
+
222
+ except Exception as e:
223
+ logger.error(f"Error deleting conversation: {str(e)}")
224
+ raise
225
 
226
  def get_active_conversations(self):
227
  """Get list of active conversations."""
228
+ logger.info("Retrieving active conversations")
229
+
230
  params = {
231
  "is_active": "eq.true",
232
  "order": "created_at.desc"
233
  }
234
 
235
+ try:
236
+ response = self._make_request(
237
+ "GET",
238
+ "/rest/v1/conversations",
239
+ params=params
240
+ )
241
+
242
+ if response.status_code != 200:
243
+ logger.error(f"Failed to retrieve active conversations: {response.text}")
244
+ raise Exception(f"Failed to retrieve active conversations: {response.text}")
245
+
246
+ conversations = response.json()
247
+ logger.info(f"Retrieved {len(conversations)} active conversations")
248
+ return conversations
249
+
250
+ except Exception as e:
251
+ logger.error(f"Error retrieving active conversations: {str(e)}")
252
+ raise
253
 
254
  # Initialize database client
255
  def get_db_client():
256
  """Get database client instance."""
257
+ try:
258
+ return SupabaseClient()
259
+ except Exception as e:
260
+ logger.error(f"Error initializing database client: {str(e)}")
261
+ raise