auto-coder 0.1.397__py3-none-any.whl → 0.1.399__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

Files changed (85) hide show
  1. auto_coder-0.1.399.dist-info/METADATA +396 -0
  2. {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/RECORD +81 -28
  3. {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/WHEEL +1 -1
  4. {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/entry_points.txt +2 -0
  5. autocoder/agent/base_agentic/base_agent.py +2 -2
  6. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
  7. autocoder/agent/entry_command_agent/__init__.py +29 -0
  8. autocoder/agent/entry_command_agent/auto_tool.py +61 -0
  9. autocoder/agent/entry_command_agent/chat.py +475 -0
  10. autocoder/agent/entry_command_agent/designer.py +53 -0
  11. autocoder/agent/entry_command_agent/generate_command.py +50 -0
  12. autocoder/agent/entry_command_agent/project_reader.py +58 -0
  13. autocoder/agent/entry_command_agent/voice2text.py +71 -0
  14. autocoder/auto_coder.py +23 -548
  15. autocoder/auto_coder_rag.py +1 -0
  16. autocoder/auto_coder_runner.py +510 -8
  17. autocoder/chat/rules_command.py +1 -1
  18. autocoder/chat_auto_coder.py +8 -0
  19. autocoder/common/ac_style_command_parser/__init__.py +15 -0
  20. autocoder/common/ac_style_command_parser/example.py +7 -0
  21. autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +1 -33
  22. autocoder/common/ac_style_command_parser/test_parser.py +516 -0
  23. autocoder/common/command_completer_v2.py +1 -1
  24. autocoder/common/command_file_manager/examples.py +22 -8
  25. autocoder/common/command_file_manager/manager.py +37 -6
  26. autocoder/common/conversations/__init__.py +84 -39
  27. autocoder/common/conversations/backup/__init__.py +14 -0
  28. autocoder/common/conversations/backup/backup_manager.py +564 -0
  29. autocoder/common/conversations/backup/restore_manager.py +546 -0
  30. autocoder/common/conversations/cache/__init__.py +16 -0
  31. autocoder/common/conversations/cache/base_cache.py +89 -0
  32. autocoder/common/conversations/cache/cache_manager.py +368 -0
  33. autocoder/common/conversations/cache/memory_cache.py +224 -0
  34. autocoder/common/conversations/config.py +195 -0
  35. autocoder/common/conversations/exceptions.py +72 -0
  36. autocoder/common/conversations/file_locker.py +145 -0
  37. autocoder/common/conversations/get_conversation_manager.py +143 -0
  38. autocoder/common/conversations/manager.py +1028 -0
  39. autocoder/common/conversations/models.py +154 -0
  40. autocoder/common/conversations/search/__init__.py +15 -0
  41. autocoder/common/conversations/search/filter_manager.py +431 -0
  42. autocoder/common/conversations/search/text_searcher.py +366 -0
  43. autocoder/common/conversations/storage/__init__.py +16 -0
  44. autocoder/common/conversations/storage/base_storage.py +82 -0
  45. autocoder/common/conversations/storage/file_storage.py +267 -0
  46. autocoder/common/conversations/storage/index_manager.py +406 -0
  47. autocoder/common/v2/agent/agentic_edit.py +131 -18
  48. autocoder/common/v2/agent/agentic_edit_types.py +10 -0
  49. autocoder/common/v2/code_auto_generate_editblock.py +10 -2
  50. autocoder/dispacher/__init__.py +10 -0
  51. autocoder/rags.py +73 -50
  52. autocoder/run_context.py +1 -0
  53. autocoder/sdk/__init__.py +188 -0
  54. autocoder/sdk/cli/__init__.py +15 -0
  55. autocoder/sdk/cli/__main__.py +26 -0
  56. autocoder/sdk/cli/completion_wrapper.py +38 -0
  57. autocoder/sdk/cli/formatters.py +211 -0
  58. autocoder/sdk/cli/handlers.py +174 -0
  59. autocoder/sdk/cli/install_completion.py +301 -0
  60. autocoder/sdk/cli/main.py +284 -0
  61. autocoder/sdk/cli/options.py +72 -0
  62. autocoder/sdk/constants.py +102 -0
  63. autocoder/sdk/core/__init__.py +20 -0
  64. autocoder/sdk/core/auto_coder_core.py +867 -0
  65. autocoder/sdk/core/bridge.py +497 -0
  66. autocoder/sdk/example.py +0 -0
  67. autocoder/sdk/exceptions.py +72 -0
  68. autocoder/sdk/models/__init__.py +19 -0
  69. autocoder/sdk/models/messages.py +209 -0
  70. autocoder/sdk/models/options.py +194 -0
  71. autocoder/sdk/models/responses.py +311 -0
  72. autocoder/sdk/session/__init__.py +32 -0
  73. autocoder/sdk/session/session.py +106 -0
  74. autocoder/sdk/session/session_manager.py +56 -0
  75. autocoder/sdk/utils/__init__.py +24 -0
  76. autocoder/sdk/utils/formatters.py +216 -0
  77. autocoder/sdk/utils/io_utils.py +302 -0
  78. autocoder/sdk/utils/validators.py +287 -0
  79. autocoder/version.py +2 -1
  80. auto_coder-0.1.397.dist-info/METADATA +0 -111
  81. autocoder/common/conversations/compatibility.py +0 -303
  82. autocoder/common/conversations/conversation_manager.py +0 -502
  83. autocoder/common/conversations/example.py +0 -152
  84. {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info/licenses}/LICENSE +0 -0
  85. {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,546 @@
1
+ """
2
+ Restore manager for conversation data.
3
+
4
+ This module provides functionality to restore conversation data from backups,
5
+ including version management and data validation.
6
+ """
7
+
8
+ import os
9
+ import json
10
+ import shutil
11
+ import threading
12
+ from datetime import datetime
13
+ from typing import Dict, List, Optional, Set, Tuple
14
+ from pathlib import Path
15
+
16
+ from ..exceptions import RestoreError, ConversationManagerError, BackupError
17
+ from ..config import ConversationManagerConfig
18
+ from .backup_manager import BackupManager, BackupMetadata
19
+
20
+
21
+ class RestoreManager:
22
+ """
23
+ Manages restore operations for conversation data.
24
+
25
+ Provides functionality to restore data from full and incremental backups,
26
+ with validation and version management.
27
+ """
28
+
29
+ def __init__(self, config: ConversationManagerConfig, backup_manager: BackupManager):
30
+ """
31
+ Initialize restore manager.
32
+
33
+ Args:
34
+ config: Configuration object containing restore settings
35
+ backup_manager: BackupManager instance for accessing backup metadata
36
+ """
37
+ self.config = config
38
+ self.backup_manager = backup_manager
39
+ self.storage_path = Path(config.storage_path)
40
+ self.backup_path = self.storage_path / "backups"
41
+ self.temp_path = self.storage_path / "temp"
42
+
43
+ # Ensure directories exist
44
+ self.temp_path.mkdir(parents=True, exist_ok=True)
45
+
46
+ # Thread lock for restore operations
47
+ self._restore_lock = threading.Lock()
48
+
49
+ def restore_conversation(
50
+ self,
51
+ conversation_id: str,
52
+ backup_id: str,
53
+ target_directory: Optional[str] = None
54
+ ) -> bool:
55
+ """
56
+ Restore a specific conversation from backup.
57
+
58
+ Args:
59
+ conversation_id: ID of the conversation to restore
60
+ backup_id: ID of the backup to restore from
61
+ target_directory: Optional target directory for restoration
62
+
63
+ Returns:
64
+ True if restoration was successful
65
+
66
+ Raises:
67
+ RestoreError: If restoration fails
68
+ """
69
+ with self._restore_lock:
70
+ try:
71
+ # Get backup metadata
72
+ metadata = self.backup_manager.get_backup_metadata(backup_id)
73
+ if metadata is None:
74
+ raise RestoreError(f"Backup {backup_id} not found")
75
+
76
+ # Verify backup integrity
77
+ if not self.backup_manager.verify_backup(backup_id):
78
+ raise RestoreError(f"Backup {backup_id} integrity check failed")
79
+
80
+ # Build complete backup chain for incremental backups
81
+ backup_chain = self._build_backup_chain(backup_id)
82
+
83
+ # Find conversation file in backup chain
84
+ conversation_data = self._find_conversation_in_backups(
85
+ conversation_id, backup_chain
86
+ )
87
+
88
+ if conversation_data is None:
89
+ raise RestoreError(
90
+ f"Conversation {conversation_id} not found in backup {backup_id}"
91
+ )
92
+
93
+ # Determine target directory
94
+ if target_directory is None:
95
+ target_dir = self.storage_path / "conversations"
96
+ else:
97
+ target_dir = Path(target_directory)
98
+
99
+ target_dir.mkdir(parents=True, exist_ok=True)
100
+
101
+ # Create backup of existing conversation if it exists
102
+ existing_file = target_dir / f"{conversation_id}.json"
103
+ if existing_file.exists():
104
+ backup_file = existing_file.with_suffix(
105
+ f".backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
106
+ )
107
+ shutil.copy2(existing_file, backup_file)
108
+
109
+ # Write restored conversation data
110
+ with open(existing_file, 'w', encoding='utf-8') as f:
111
+ json.dump(conversation_data, f, indent=2, ensure_ascii=False)
112
+
113
+ return True
114
+
115
+ except Exception as e:
116
+ raise RestoreError(f"Failed to restore conversation {conversation_id}: {str(e)}") from e
117
+
118
+ def restore_full_backup(
119
+ self,
120
+ backup_id: str,
121
+ target_directory: Optional[str] = None,
122
+ overwrite_existing: bool = False
123
+ ) -> Dict[str, any]:
124
+ """
125
+ Restore all data from a full backup.
126
+
127
+ Args:
128
+ backup_id: ID of the backup to restore from
129
+ target_directory: Optional target directory for restoration
130
+ overwrite_existing: Whether to overwrite existing files
131
+
132
+ Returns:
133
+ Dictionary containing restore results
134
+
135
+ Raises:
136
+ RestoreError: If restoration fails
137
+ """
138
+ with self._restore_lock:
139
+ try:
140
+ # Get backup metadata
141
+ metadata = self.backup_manager.get_backup_metadata(backup_id)
142
+ if metadata is None:
143
+ raise RestoreError(f"Backup {backup_id} not found")
144
+
145
+ if metadata.backup_type != "full":
146
+ raise RestoreError(f"Backup {backup_id} is not a full backup")
147
+
148
+ # Verify backup integrity
149
+ if not self.backup_manager.verify_backup(backup_id):
150
+ raise RestoreError(f"Backup {backup_id} integrity check failed")
151
+
152
+ backup_dir = self.backup_path / backup_id
153
+
154
+ # Determine target directory
155
+ if target_directory is None:
156
+ target_dir = self.storage_path
157
+ else:
158
+ target_dir = Path(target_directory)
159
+
160
+ # Create timestamp for backup naming
161
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
162
+
163
+ # Backup existing data if not overwriting
164
+ existing_conversations_dir = target_dir / "conversations"
165
+ existing_index_dir = target_dir / "index"
166
+
167
+ backed_up_files = []
168
+ if not overwrite_existing:
169
+ if existing_conversations_dir.exists():
170
+ backup_conversations_dir = target_dir / f"conversations.backup_{timestamp}"
171
+ shutil.copytree(existing_conversations_dir, backup_conversations_dir)
172
+ backed_up_files.append(str(backup_conversations_dir))
173
+
174
+ if existing_index_dir.exists():
175
+ backup_index_dir = target_dir / f"index.backup_{timestamp}"
176
+ shutil.copytree(existing_index_dir, backup_index_dir)
177
+ backed_up_files.append(str(backup_index_dir))
178
+
179
+ # Restore conversations
180
+ restored_conversations = []
181
+ conversations_backup_dir = backup_dir
182
+ if conversations_backup_dir.exists():
183
+ target_conversations_dir = target_dir / "conversations"
184
+ target_conversations_dir.mkdir(parents=True, exist_ok=True)
185
+
186
+ for conv_file in conversations_backup_dir.glob("*.json"):
187
+ target_file = target_conversations_dir / conv_file.name
188
+ shutil.copy2(conv_file, target_file)
189
+ restored_conversations.append(conv_file.stem)
190
+
191
+ # Restore index files
192
+ restored_indices = []
193
+ index_backup_dir = backup_dir / "index"
194
+ if index_backup_dir.exists():
195
+ target_index_dir = target_dir / "index"
196
+ target_index_dir.mkdir(parents=True, exist_ok=True)
197
+
198
+ for index_file in index_backup_dir.glob("*.json"):
199
+ target_file = target_index_dir / index_file.name
200
+ shutil.copy2(index_file, target_file)
201
+ restored_indices.append(index_file.name)
202
+
203
+ return {
204
+ "backup_id": backup_id,
205
+ "restore_timestamp": datetime.now().isoformat(),
206
+ "restored_conversations": restored_conversations,
207
+ "restored_indices": restored_indices,
208
+ "backed_up_files": backed_up_files,
209
+ "overwrite_existing": overwrite_existing
210
+ }
211
+
212
+ except Exception as e:
213
+ raise RestoreError(f"Failed to restore full backup {backup_id}: {str(e)}") from e
214
+
215
+ def restore_point_in_time(
216
+ self,
217
+ target_timestamp: float,
218
+ target_directory: Optional[str] = None
219
+ ) -> Dict[str, any]:
220
+ """
221
+ Restore data to a specific point in time.
222
+
223
+ Args:
224
+ target_timestamp: Target timestamp to restore to
225
+ target_directory: Optional target directory for restoration
226
+
227
+ Returns:
228
+ Dictionary containing restore results
229
+
230
+ Raises:
231
+ RestoreError: If restoration fails
232
+ """
233
+ with self._restore_lock:
234
+ try:
235
+ # Find the best backup for the target timestamp
236
+ backup_id = self._find_best_backup_for_timestamp(target_timestamp)
237
+ if backup_id is None:
238
+ raise RestoreError(f"No suitable backup found for timestamp {target_timestamp}")
239
+
240
+ metadata = self.backup_manager.get_backup_metadata(backup_id)
241
+
242
+ if metadata.backup_type == "full":
243
+ return self.restore_full_backup(
244
+ backup_id,
245
+ target_directory=target_directory
246
+ )
247
+ else:
248
+ # For incremental backups, we need to apply the backup chain
249
+ return self._restore_incremental_chain(
250
+ backup_id,
251
+ target_directory=target_directory
252
+ )
253
+
254
+ except Exception as e:
255
+ raise RestoreError(f"Failed to restore to timestamp {target_timestamp}: {str(e)}") from e
256
+
257
+ def list_restorable_conversations(self, backup_id: str) -> List[str]:
258
+ """
259
+ List conversations that can be restored from a backup.
260
+
261
+ Args:
262
+ backup_id: ID of the backup to check
263
+
264
+ Returns:
265
+ List of conversation IDs that can be restored
266
+
267
+ Raises:
268
+ RestoreError: If backup cannot be accessed
269
+ """
270
+ try:
271
+ metadata = self.backup_manager.get_backup_metadata(backup_id)
272
+ if metadata is None:
273
+ raise RestoreError(f"Backup {backup_id} not found")
274
+
275
+ if metadata.backup_type == "full":
276
+ return metadata.conversation_ids or []
277
+ else:
278
+ # For incremental backups, get conversations from backup chain
279
+ backup_chain = self._build_backup_chain(backup_id)
280
+ all_conversations = set()
281
+
282
+ for chain_backup_id in backup_chain:
283
+ chain_metadata = self.backup_manager.get_backup_metadata(chain_backup_id)
284
+ if chain_metadata and chain_metadata.conversation_ids:
285
+ all_conversations.update(chain_metadata.conversation_ids)
286
+
287
+ return list(all_conversations)
288
+
289
+ except Exception as e:
290
+ raise RestoreError(f"Failed to list restorable conversations: {str(e)}") from e
291
+
292
+ def validate_backup_chain(self, backup_id: str) -> Dict[str, any]:
293
+ """
294
+ Validate the integrity of a backup chain.
295
+
296
+ Args:
297
+ backup_id: ID of the backup to validate
298
+
299
+ Returns:
300
+ Dictionary containing validation results
301
+
302
+ Raises:
303
+ RestoreError: If validation fails
304
+ """
305
+ try:
306
+ backup_chain = self._build_backup_chain(backup_id)
307
+ validation_results = {
308
+ "backup_id": backup_id,
309
+ "chain_length": len(backup_chain),
310
+ "chain_backups": [],
311
+ "is_valid": True,
312
+ "errors": []
313
+ }
314
+
315
+ for chain_backup_id in backup_chain:
316
+ metadata = self.backup_manager.get_backup_metadata(chain_backup_id)
317
+ if metadata is None:
318
+ validation_results["is_valid"] = False
319
+ validation_results["errors"].append(f"Backup {chain_backup_id} metadata not found")
320
+ continue
321
+
322
+ # Verify backup integrity
323
+ is_valid = self.backup_manager.verify_backup(chain_backup_id)
324
+
325
+ backup_info = {
326
+ "backup_id": chain_backup_id,
327
+ "backup_type": metadata.backup_type,
328
+ "timestamp": metadata.timestamp,
329
+ "is_valid": is_valid
330
+ }
331
+
332
+ if not is_valid:
333
+ validation_results["is_valid"] = False
334
+ validation_results["errors"].append(f"Backup {chain_backup_id} integrity check failed")
335
+
336
+ validation_results["chain_backups"].append(backup_info)
337
+
338
+ return validation_results
339
+
340
+ except Exception as e:
341
+ raise RestoreError(f"Failed to validate backup chain: {str(e)}") from e
342
+
343
+ def get_restore_preview(self, backup_id: str) -> Dict[str, any]:
344
+ """
345
+ Get a preview of what would be restored from a backup.
346
+
347
+ Args:
348
+ backup_id: ID of the backup to preview
349
+
350
+ Returns:
351
+ Dictionary containing restore preview information
352
+
353
+ Raises:
354
+ RestoreError: If preview generation fails
355
+ """
356
+ try:
357
+ metadata = self.backup_manager.get_backup_metadata(backup_id)
358
+ if metadata is None:
359
+ raise RestoreError(f"Backup {backup_id} not found")
360
+
361
+ backup_chain = self._build_backup_chain(backup_id)
362
+
363
+ preview = {
364
+ "backup_id": backup_id,
365
+ "backup_type": metadata.backup_type,
366
+ "backup_timestamp": metadata.timestamp,
367
+ "backup_chain": backup_chain,
368
+ "restorable_conversations": self.list_restorable_conversations(backup_id),
369
+ "total_file_count": 0,
370
+ "total_size_bytes": 0
371
+ }
372
+
373
+ # Calculate total size and file count for the chain
374
+ for chain_backup_id in backup_chain:
375
+ chain_metadata = self.backup_manager.get_backup_metadata(chain_backup_id)
376
+ if chain_metadata:
377
+ preview["total_file_count"] += chain_metadata.file_count
378
+ preview["total_size_bytes"] += chain_metadata.total_size_bytes
379
+
380
+ return preview
381
+
382
+ except Exception as e:
383
+ raise RestoreError(f"Failed to generate restore preview: {str(e)}") from e
384
+
385
+ def _build_backup_chain(self, backup_id: str) -> List[str]:
386
+ """
387
+ Build the complete backup chain for a backup.
388
+
389
+ Args:
390
+ backup_id: ID of the backup to build chain for
391
+
392
+ Returns:
393
+ List of backup IDs in the chain, ordered from base to target
394
+ """
395
+ chain = []
396
+ current_backup_id = backup_id
397
+
398
+ while current_backup_id:
399
+ metadata = self.backup_manager.get_backup_metadata(current_backup_id)
400
+ if metadata is None:
401
+ break
402
+
403
+ chain.insert(0, current_backup_id) # Insert at beginning
404
+
405
+ if metadata.backup_type == "full":
406
+ break # Reached the base backup
407
+
408
+ current_backup_id = metadata.base_backup_id
409
+
410
+ return chain
411
+
412
+ def _find_conversation_in_backups(
413
+ self,
414
+ conversation_id: str,
415
+ backup_chain: List[str]
416
+ ) -> Optional[Dict]:
417
+ """
418
+ Find conversation data in backup chain.
419
+
420
+ Args:
421
+ conversation_id: ID of the conversation to find
422
+ backup_chain: List of backup IDs to search in
423
+
424
+ Returns:
425
+ Conversation data or None if not found
426
+ """
427
+ # Search from newest to oldest backup
428
+ for backup_id in reversed(backup_chain):
429
+ backup_dir = self.backup_path / backup_id
430
+ conv_file = backup_dir / f"{conversation_id}.json"
431
+
432
+ if conv_file.exists():
433
+ try:
434
+ with open(conv_file, 'r', encoding='utf-8') as f:
435
+ return json.load(f)
436
+ except Exception:
437
+ continue # Try next backup in chain
438
+
439
+ return None
440
+
441
+ def _find_best_backup_for_timestamp(self, target_timestamp: float) -> Optional[str]:
442
+ """
443
+ Find the best backup for a target timestamp.
444
+
445
+ Args:
446
+ target_timestamp: Target timestamp
447
+
448
+ Returns:
449
+ Backup ID or None if no suitable backup found
450
+ """
451
+ backups = self.backup_manager.list_backups()
452
+
453
+ # Find backups that are before or at the target timestamp
454
+ suitable_backups = [
455
+ b for b in backups
456
+ if b.timestamp <= target_timestamp
457
+ ]
458
+
459
+ if not suitable_backups:
460
+ return None
461
+
462
+ # Return the most recent suitable backup
463
+ best_backup = max(suitable_backups, key=lambda x: x.timestamp)
464
+ return best_backup.backup_id
465
+
466
+ def _restore_incremental_chain(
467
+ self,
468
+ backup_id: str,
469
+ target_directory: Optional[str] = None
470
+ ) -> Dict[str, any]:
471
+ """
472
+ Restore from an incremental backup chain.
473
+
474
+ Args:
475
+ backup_id: ID of the incremental backup
476
+ target_directory: Optional target directory
477
+
478
+ Returns:
479
+ Dictionary containing restore results
480
+ """
481
+ backup_chain = self._build_backup_chain(backup_id)
482
+
483
+ if not backup_chain:
484
+ raise RestoreError(f"Empty backup chain for {backup_id}")
485
+
486
+ # Start with full backup restoration
487
+ full_backup_id = backup_chain[0]
488
+ results = self.restore_full_backup(
489
+ full_backup_id,
490
+ target_directory=target_directory,
491
+ overwrite_existing=True
492
+ )
493
+
494
+ # Apply incremental backups in order
495
+ for incremental_backup_id in backup_chain[1:]:
496
+ incremental_results = self._apply_incremental_backup(
497
+ incremental_backup_id,
498
+ target_directory or str(self.storage_path)
499
+ )
500
+
501
+ # Merge results
502
+ results["restored_conversations"].extend(
503
+ incremental_results.get("restored_conversations", [])
504
+ )
505
+ results["restored_indices"].extend(
506
+ incremental_results.get("restored_indices", [])
507
+ )
508
+
509
+ # Remove duplicates
510
+ results["restored_conversations"] = list(set(results["restored_conversations"]))
511
+ results["restored_indices"] = list(set(results["restored_indices"]))
512
+
513
+ return results
514
+
515
+ def _apply_incremental_backup(
516
+ self,
517
+ backup_id: str,
518
+ target_directory: str
519
+ ) -> Dict[str, any]:
520
+ """Apply an incremental backup to the target directory."""
521
+ backup_dir = self.backup_path / backup_id
522
+ target_dir = Path(target_directory)
523
+
524
+ restored_conversations = []
525
+ restored_indices = []
526
+
527
+ # Apply conversation files
528
+ for conv_file in backup_dir.glob("*.json"):
529
+ target_file = target_dir / "conversations" / conv_file.name
530
+ target_file.parent.mkdir(parents=True, exist_ok=True)
531
+ shutil.copy2(conv_file, target_file)
532
+ restored_conversations.append(conv_file.stem)
533
+
534
+ # Apply index files
535
+ index_backup_dir = backup_dir / "index"
536
+ if index_backup_dir.exists():
537
+ for index_file in index_backup_dir.glob("*.json"):
538
+ target_file = target_dir / "index" / index_file.name
539
+ target_file.parent.mkdir(parents=True, exist_ok=True)
540
+ shutil.copy2(index_file, target_file)
541
+ restored_indices.append(index_file.name)
542
+
543
+ return {
544
+ "restored_conversations": restored_conversations,
545
+ "restored_indices": restored_indices
546
+ }
@@ -0,0 +1,16 @@
1
+ """
2
+ Cache module for conversation management.
3
+
4
+ This module provides caching functionality for conversations and messages,
5
+ including memory-based caching with LRU eviction and TTL support.
6
+ """
7
+
8
+ from .base_cache import BaseCache
9
+ from .memory_cache import MemoryCache
10
+ from .cache_manager import CacheManager
11
+
12
+ __all__ = [
13
+ 'BaseCache',
14
+ 'MemoryCache',
15
+ 'CacheManager'
16
+ ]
@@ -0,0 +1,89 @@
1
+ """
2
+ Base cache interface for conversation caching.
3
+
4
+ This module defines the abstract base class for all cache implementations,
5
+ providing a consistent interface for caching operations.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Optional, Any, List
10
+
11
+
12
+ class BaseCache(ABC):
13
+ """Abstract base class for cache implementations."""
14
+
15
+ @abstractmethod
16
+ def get(self, key: str) -> Optional[Any]:
17
+ """
18
+ Get a value from the cache.
19
+
20
+ Args:
21
+ key: The cache key
22
+
23
+ Returns:
24
+ The cached value or None if not found/expired
25
+ """
26
+ pass
27
+
28
+ @abstractmethod
29
+ def set(self, key: str, value: Any, ttl: Optional[float] = None) -> None:
30
+ """
31
+ Set a value in the cache.
32
+
33
+ Args:
34
+ key: The cache key
35
+ value: The value to cache
36
+ ttl: Time to live in seconds, None for no expiration
37
+ """
38
+ pass
39
+
40
+ @abstractmethod
41
+ def delete(self, key: str) -> bool:
42
+ """
43
+ Delete a value from the cache.
44
+
45
+ Args:
46
+ key: The cache key
47
+
48
+ Returns:
49
+ True if the key was deleted, False if it didn't exist
50
+ """
51
+ pass
52
+
53
+ @abstractmethod
54
+ def clear(self) -> None:
55
+ """Clear all items from the cache."""
56
+ pass
57
+
58
+ @abstractmethod
59
+ def exists(self, key: str) -> bool:
60
+ """
61
+ Check if a key exists in the cache.
62
+
63
+ Args:
64
+ key: The cache key
65
+
66
+ Returns:
67
+ True if the key exists and is not expired, False otherwise
68
+ """
69
+ pass
70
+
71
+ @abstractmethod
72
+ def size(self) -> int:
73
+ """
74
+ Get the current number of items in the cache.
75
+
76
+ Returns:
77
+ The number of items currently in the cache
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ def keys(self) -> List[str]:
83
+ """
84
+ Get all keys currently in the cache.
85
+
86
+ Returns:
87
+ List of keys currently in the cache
88
+ """
89
+ pass