cryogenic22 commited on
Commit
aa9fdd5
·
verified ·
1 Parent(s): 2179810

Update persistence_manager.py

Browse files
Files changed (1) hide show
  1. persistence_manager.py +235 -86
persistence_manager.py CHANGED
@@ -48,7 +48,7 @@ class APIConfigManager:
48
  class HuggingFacePersistenceManager:
49
  """
50
  Specialized persistence manager for Hugging Face Spaces
51
- Provides multiple layers of data persistence
52
  """
53
  def __init__(self, base_storage_path: str = '/data'):
54
  """
@@ -57,17 +57,56 @@ class HuggingFacePersistenceManager:
57
  Args:
58
  base_storage_path (str): Base path for persistent storage
59
  """
60
- # Ensure base storage path exists
61
- self.base_storage_path = base_storage_path
 
 
62
  self._create_storage_structure()
63
 
64
  # Setup database connection
65
  self.db_path = os.path.join(self.base_storage_path, 'book_projects.db')
66
  self.conn = self._initialize_database()
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  def _create_storage_structure(self):
69
  """
70
  Create necessary directory structure for persistent storage
 
71
  """
72
  # Directories to create
73
  directories = [
@@ -80,9 +119,24 @@ class HuggingFacePersistenceManager:
80
  # Create directories with proper permissions
81
  for dir_path in directories:
82
  try:
 
83
  os.makedirs(dir_path, exist_ok=True)
84
- # Ensure write permissions
85
- os.chmod(dir_path, 0o755)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  except Exception as e:
87
  st.error(f"Could not create directory {dir_path}: {e}")
88
 
@@ -159,35 +213,41 @@ class HuggingFacePersistenceManager:
159
  }
160
 
161
  # Write to JSON file
162
- with open(project_file, 'w') as f:
163
- json.dump(save_data, f, indent=4)
 
 
 
164
 
165
  # Save to SQLite database
166
  if self.conn:
167
- cursor = self.conn.cursor()
168
-
169
- # Insert or update project
170
- cursor.execute('''
171
- INSERT OR REPLACE INTO projects
172
- (project_id, title, genre, total_chapters)
173
- VALUES (?, ?, ?, ?)
174
- ''', (
175
- project_id,
176
- save_data.get('title', 'Untitled'),
177
- save_data.get('genre', 'Unspecified'),
178
- save_data.get('total_chapters', 0)
179
- ))
180
-
181
- # Save chapters if exists
182
- if 'chapters' in save_data:
183
- for chapter_num, chapter_content in save_data['chapters'].items():
184
- cursor.execute('''
185
- INSERT OR REPLACE INTO chapters
186
- (project_id, chapter_number, content)
187
- VALUES (?, ?, ?)
188
- ''', (project_id, chapter_num, chapter_content))
189
-
190
- self.conn.commit()
 
 
 
191
 
192
  return project_id
193
 
@@ -214,31 +274,37 @@ class HuggingFacePersistenceManager:
214
  )
215
 
216
  if os.path.exists(project_file):
217
- with open(project_file, 'r') as f:
218
- return json.load(f)
 
 
 
219
 
220
  # Fallback to database
221
  if self.conn:
222
- cursor = self.conn.cursor()
223
-
224
- # Fetch project details
225
- cursor.execute('SELECT * FROM projects WHERE project_id = ?', (project_id,))
226
- project = cursor.fetchone()
227
-
228
- if project:
229
- # Fetch chapters
230
- cursor.execute('SELECT * FROM chapters WHERE project_id = ?', (project_id,))
231
- chapters = cursor.fetchall()
232
 
233
- return {
234
- 'project_id': project[0],
235
- 'title': project[1],
236
- 'genre': project[2],
237
- 'total_chapters': project[3],
238
- 'chapters': {
239
- chapter[1]: chapter[2] for chapter in chapters
 
 
 
 
 
 
 
 
 
 
240
  }
241
- }
 
242
 
243
  return None
244
 
@@ -275,21 +341,24 @@ class HuggingFacePersistenceManager:
275
 
276
  # Supplement with database entries if available
277
  if self.conn:
278
- cursor = self.conn.cursor()
279
- cursor.execute('SELECT project_id, title, last_modified FROM projects')
280
- db_projects = cursor.fetchall()
281
-
282
- # Add any database entries not already in file list
283
- db_project_ids = {p[0] for p in db_projects}
284
- file_project_ids = {p['project_id'] for p in projects}
285
-
286
- for db_project in db_projects:
287
- if db_project[0] not in file_project_ids:
288
- projects.append({
289
- 'project_id': db_project[0],
290
- 'title': db_project[1],
291
- 'last_modified': db_project[2]
292
- })
 
 
 
293
 
294
  except Exception as e:
295
  st.error(f"Error listing projects: {e}")
@@ -396,7 +465,10 @@ class ResiliентAPIHandler:
396
  "Error occurred"
397
  ]
398
 
399
- return not any(indicator in response for indicator in nonsense_indicators)
 
 
 
400
 
401
  def check_huggingface_storage():
402
  """
@@ -405,31 +477,108 @@ def check_huggingface_storage():
405
  Returns:
406
  Dict with storage verification results
407
  """
408
- # Check /data directory
409
- data_dir = '/data'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
 
411
- return {
412
- 'data_dir_exists': os.path.exists(data_dir),
413
- 'data_dir_writable': os.access(data_dir, os.W_OK),
414
- 'data_dir_readable': os.access(data_dir, os.R_OK),
415
- 'free_space': os.statvfs(data_dir).f_bfree # Free blocks available
416
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
  def main():
419
- # Retrieve API credentials
420
- api_credentials = APIConfigManager.get_api_credentials()
 
 
 
 
 
421
 
422
- # Print out configuration (for debugging)
423
- print("API Credentials Configuration:")
 
 
 
 
424
  for key in api_credentials:
425
  print(f"{key}: {'*' * 8 if api_credentials[key] else 'Not Set'}")
426
-
427
- # Initialize persistence manager
428
- persistence_manager = HuggingFacePersistenceManager()
429
-
430
- # Verify storage capabilities
431
- storage_check = check_huggingface_storage()
432
- print("Hugging Face Storage Check:", json.dumps(storage_check, indent=2))
433
 
434
  if __name__ == "__main__":
435
  main()
 
48
  class HuggingFacePersistenceManager:
49
  """
50
  Specialized persistence manager for Hugging Face Spaces
51
+ Provides multiple layers of data persistence with robust error handling
52
  """
53
  def __init__(self, base_storage_path: str = '/data'):
54
  """
 
57
  Args:
58
  base_storage_path (str): Base path for persistent storage
59
  """
60
+ # Determine storage path with fallback
61
+ self.base_storage_path = self._determine_storage_path(base_storage_path)
62
+
63
+ # Create storage structure with error handling
64
  self._create_storage_structure()
65
 
66
  # Setup database connection
67
  self.db_path = os.path.join(self.base_storage_path, 'book_projects.db')
68
  self.conn = self._initialize_database()
69
 
70
+ def _determine_storage_path(self, preferred_path: str) -> str:
71
+ """
72
+ Determine an appropriate storage path with fallback mechanisms
73
+
74
+ Args:
75
+ preferred_path (str): Preferred storage path
76
+
77
+ Returns:
78
+ str: A writable storage path
79
+ """
80
+ # List of potential fallback paths
81
+ fallback_paths = [
82
+ preferred_path, # Original /data path
83
+ '/tmp/book_writing_agent', # Temporary directory
84
+ os.path.join(os.path.expanduser('~'), '.book_writing_agent'), # Home directory
85
+ ]
86
+
87
+ for path in fallback_paths:
88
+ try:
89
+ # Try to create directory if it doesn't exist
90
+ os.makedirs(path, exist_ok=True)
91
+
92
+ # Check if directory is writable
93
+ test_file = os.path.join(path, '.writetest')
94
+ with open(test_file, 'w') as f:
95
+ f.write('test')
96
+ os.remove(test_file)
97
+
98
+ st.info(f"Using storage path: {path}")
99
+ return path
100
+ except Exception as e:
101
+ st.warning(f"Could not use path {path}: {e}")
102
+
103
+ # If no path works, raise a critical error
104
+ raise RuntimeError("Could not find a writable storage location")
105
+
106
  def _create_storage_structure(self):
107
  """
108
  Create necessary directory structure for persistent storage
109
+ with comprehensive error handling
110
  """
111
  # Directories to create
112
  directories = [
 
119
  # Create directories with proper permissions
120
  for dir_path in directories:
121
  try:
122
+ # Use os.makedirs with exist_ok to prevent race conditions
123
  os.makedirs(dir_path, exist_ok=True)
124
+
125
+ # Attempt to set permissions (if possible)
126
+ try:
127
+ os.chmod(dir_path, 0o755)
128
+ except Exception as perm_error:
129
+ # Log but don't fail if chmod is not possible
130
+ st.warning(f"Could not set permissions for {dir_path}: {perm_error}")
131
+
132
+ # Verify directory is writable
133
+ test_file = os.path.join(dir_path, '.writetest')
134
+ with open(test_file, 'w') as f:
135
+ f.write('test')
136
+ os.remove(test_file)
137
+
138
+ except PermissionError:
139
+ st.error(f"Permission denied when creating directory {dir_path}")
140
  except Exception as e:
141
  st.error(f"Could not create directory {dir_path}: {e}")
142
 
 
213
  }
214
 
215
  # Write to JSON file
216
+ try:
217
+ with open(project_file, 'w') as f:
218
+ json.dump(save_data, f, indent=4)
219
+ except Exception as json_error:
220
+ st.error(f"Error saving JSON file: {json_error}")
221
 
222
  # Save to SQLite database
223
  if self.conn:
224
+ try:
225
+ cursor = self.conn.cursor()
226
+
227
+ # Insert or update project
228
+ cursor.execute('''
229
+ INSERT OR REPLACE INTO projects
230
+ (project_id, title, genre, total_chapters)
231
+ VALUES (?, ?, ?, ?)
232
+ ''', (
233
+ project_id,
234
+ save_data.get('title', 'Untitled'),
235
+ save_data.get('genre', 'Unspecified'),
236
+ save_data.get('total_chapters', 0)
237
+ ))
238
+
239
+ # Save chapters if exists
240
+ if 'chapters' in save_data:
241
+ for chapter_num, chapter_content in save_data['chapters'].items():
242
+ cursor.execute('''
243
+ INSERT OR REPLACE INTO chapters
244
+ (project_id, chapter_number, content)
245
+ VALUES (?, ?, ?)
246
+ ''', (project_id, chapter_num, chapter_content))
247
+
248
+ self.conn.commit()
249
+ except Exception as db_error:
250
+ st.error(f"Error saving to database: {db_error}")
251
 
252
  return project_id
253
 
 
274
  )
275
 
276
  if os.path.exists(project_file):
277
+ try:
278
+ with open(project_file, 'r') as f:
279
+ return json.load(f)
280
+ except Exception as json_error:
281
+ st.error(f"Error reading JSON file: {json_error}")
282
 
283
  # Fallback to database
284
  if self.conn:
285
+ try:
286
+ cursor = self.conn.cursor()
 
 
 
 
 
 
 
 
287
 
288
+ # Fetch project details
289
+ cursor.execute('SELECT * FROM projects WHERE project_id = ?', (project_id,))
290
+ project = cursor.fetchone()
291
+
292
+ if project:
293
+ # Fetch chapters
294
+ cursor.execute('SELECT * FROM chapters WHERE project_id = ?', (project_id,))
295
+ chapters = cursor.fetchall()
296
+
297
+ return {
298
+ 'project_id': project[0],
299
+ 'title': project[1],
300
+ 'genre': project[2],
301
+ 'total_chapters': project[3],
302
+ 'chapters': {
303
+ chapter[1]: chapter[2] for chapter in chapters
304
+ }
305
  }
306
+ except Exception as db_error:
307
+ st.error(f"Error loading from database: {db_error}")
308
 
309
  return None
310
 
 
341
 
342
  # Supplement with database entries if available
343
  if self.conn:
344
+ try:
345
+ cursor = self.conn.cursor()
346
+ cursor.execute('SELECT project_id, title, last_modified FROM projects')
347
+ db_projects = cursor.fetchall()
348
+
349
+ # Add any database entries not already in file list
350
+ db_project_ids = {p[0] for p in db_projects}
351
+ file_project_ids = {p['project_id'] for p in projects}
352
+
353
+ for db_project in db_projects:
354
+ if db_project[0] not in file_project_ids:
355
+ projects.append({
356
+ 'project_id': db_project[0],
357
+ 'title': db_project[1],
358
+ 'last_modified': db_project[2]
359
+ })
360
+ except Exception as db_error:
361
+ st.error(f"Error listing projects from database: {db_error}")
362
 
363
  except Exception as e:
364
  st.error(f"Error listing projects: {e}")
 
465
  "Error occurred"
466
  ]
467
 
468
+ return not any(indicator in response for response lower case in content
469
+ ]
470
+
471
+ return not any(indicator.lower() in response.lower() for indicator in nonsense_indicators)
472
 
473
  def check_huggingface_storage():
474
  """
 
477
  Returns:
478
  Dict with storage verification results
479
  """
480
+ # Check storage paths
481
+ paths_to_check = ['/data', '/tmp', os.path.expanduser('~')]
482
+
483
+ storage_results = {}
484
+
485
+ for path in paths_to_check:
486
+ try:
487
+ # Check if path exists
488
+ path_exists = os.path.exists(path)
489
+
490
+ storage_results[path] = {
491
+ 'exists': path_exists,
492
+ 'writable': os.access(path, os.W_OK) if path_exists else False,
493
+ 'readable': os.access(path, os.R_OK) if path_exists else False
494
+ }
495
+
496
+ # Add free space if path exists
497
+ if path_exists:
498
+ try:
499
+ statvfs = os.statvfs(path)
500
+ storage_results[path]['free_space_bytes'] = statvfs.f_frsize * statvfs.f_bavail
501
+ storage_results[path]['free_space_mb'] = (statvfs.f_frsize * statvfs.f_bavail) / (1024 * 1024)
502
+ except Exception:
503
+ storage_results[path]['free_space_bytes'] = 0
504
+ storage_results[path]['free_space_mb'] = 0
505
+
506
+ except Exception as e:
507
+ storage_results[path] = {
508
+ 'exists': False,
509
+ 'writable': False,
510
+ 'readable': False,
511
+ 'error': str(e)
512
+ }
513
+
514
+ return storage_results
515
+
516
+ def debug_storage_capabilities():
517
+ """
518
+ Comprehensive debug function to check storage capabilities
519
+ Prints detailed information about storage access
520
+ """
521
+ import platform
522
 
523
+ print("Storage Capabilities Debug Report")
524
+ print("=" * 40)
525
+
526
+ # System Information
527
+ print(f"Operating System: {platform.system()}")
528
+ print(f"OS Release: {platform.release()}")
529
+ print(f"Python Version: {platform.python_version()}")
530
+
531
+ print("\nStorage Path Checks:")
532
+ print("-" * 20)
533
+
534
+ # Check various potential storage paths
535
+ paths_to_check = [
536
+ '/data',
537
+ '/tmp',
538
+ os.path.expanduser('~'),
539
+ os.getcwd()
540
+ ]
541
+
542
+ for path in paths_to_check:
543
+ print(f"\nPath: {path}")
544
+ try:
545
+ # Detailed path analysis
546
+ print(f" Exists: {os.path.exists(path)}")
547
+ print(f" Is Directory: {os.path.isdir(path)}")
548
+ print(f" Readable: {os.access(path, os.R_OK)}")
549
+ print(f" Writable: {os.access(path, os.W_OK)}")
550
+ print(f" Executable: {os.access(path, os.X_OK)}")
551
+
552
+ # Try creating a test file
553
+ try:
554
+ test_file = os.path.join(path, '.storage_test')
555
+ with open(test_file, 'w') as f:
556
+ f.write('storage test')
557
+ os.remove(test_file)
558
+ print(" File Creation: SUCCESS")
559
+ except Exception as file_error:
560
+ print(f" File Creation: FAILED - {file_error}")
561
+
562
+ except Exception as e:
563
+ print(f" Error checking path: {e}")
564
 
565
  def main():
566
+ """
567
+ Main function to demonstrate storage management capabilities
568
+ """
569
+ # Demonstrate storage check
570
+ storage_capabilities = check_huggingface_storage()
571
+ print("Storage Capabilities:")
572
+ print(json.dumps(storage_capabilities, indent=2))
573
 
574
+ # Run comprehensive debug
575
+ debug_storage_capabilities()
576
+
577
+ # Demonstrate API configuration
578
+ api_credentials = APIConfigManager.get_api_credentials()
579
+ print("\nAPI Credentials:")
580
  for key in api_credentials:
581
  print(f"{key}: {'*' * 8 if api_credentials[key] else 'Not Set'}")
 
 
 
 
 
 
 
582
 
583
  if __name__ == "__main__":
584
  main()