spatial-memory-mcp 1.9.1__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.
Files changed (55) hide show
  1. spatial_memory/__init__.py +97 -0
  2. spatial_memory/__main__.py +271 -0
  3. spatial_memory/adapters/__init__.py +7 -0
  4. spatial_memory/adapters/lancedb_repository.py +880 -0
  5. spatial_memory/config.py +769 -0
  6. spatial_memory/core/__init__.py +118 -0
  7. spatial_memory/core/cache.py +317 -0
  8. spatial_memory/core/circuit_breaker.py +297 -0
  9. spatial_memory/core/connection_pool.py +220 -0
  10. spatial_memory/core/consolidation_strategies.py +401 -0
  11. spatial_memory/core/database.py +3072 -0
  12. spatial_memory/core/db_idempotency.py +242 -0
  13. spatial_memory/core/db_indexes.py +576 -0
  14. spatial_memory/core/db_migrations.py +588 -0
  15. spatial_memory/core/db_search.py +512 -0
  16. spatial_memory/core/db_versioning.py +178 -0
  17. spatial_memory/core/embeddings.py +558 -0
  18. spatial_memory/core/errors.py +317 -0
  19. spatial_memory/core/file_security.py +701 -0
  20. spatial_memory/core/filesystem.py +178 -0
  21. spatial_memory/core/health.py +289 -0
  22. spatial_memory/core/helpers.py +79 -0
  23. spatial_memory/core/import_security.py +433 -0
  24. spatial_memory/core/lifecycle_ops.py +1067 -0
  25. spatial_memory/core/logging.py +194 -0
  26. spatial_memory/core/metrics.py +192 -0
  27. spatial_memory/core/models.py +660 -0
  28. spatial_memory/core/rate_limiter.py +326 -0
  29. spatial_memory/core/response_types.py +500 -0
  30. spatial_memory/core/security.py +588 -0
  31. spatial_memory/core/spatial_ops.py +430 -0
  32. spatial_memory/core/tracing.py +300 -0
  33. spatial_memory/core/utils.py +110 -0
  34. spatial_memory/core/validation.py +406 -0
  35. spatial_memory/factory.py +444 -0
  36. spatial_memory/migrations/__init__.py +40 -0
  37. spatial_memory/ports/__init__.py +11 -0
  38. spatial_memory/ports/repositories.py +630 -0
  39. spatial_memory/py.typed +0 -0
  40. spatial_memory/server.py +1214 -0
  41. spatial_memory/services/__init__.py +70 -0
  42. spatial_memory/services/decay_manager.py +411 -0
  43. spatial_memory/services/export_import.py +1031 -0
  44. spatial_memory/services/lifecycle.py +1139 -0
  45. spatial_memory/services/memory.py +412 -0
  46. spatial_memory/services/spatial.py +1152 -0
  47. spatial_memory/services/utility.py +429 -0
  48. spatial_memory/tools/__init__.py +5 -0
  49. spatial_memory/tools/definitions.py +695 -0
  50. spatial_memory/verify.py +140 -0
  51. spatial_memory_mcp-1.9.1.dist-info/METADATA +509 -0
  52. spatial_memory_mcp-1.9.1.dist-info/RECORD +55 -0
  53. spatial_memory_mcp-1.9.1.dist-info/WHEEL +4 -0
  54. spatial_memory_mcp-1.9.1.dist-info/entry_points.txt +2 -0
  55. spatial_memory_mcp-1.9.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,317 @@
1
+ """Custom exceptions for Spatial Memory MCP Server."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def sanitize_path_for_error(path: str | Path) -> str:
7
+ """Extract only the filename from a path for safe error messages.
8
+
9
+ Prevents leaking full system paths in error messages which could
10
+ expose sensitive directory structure information.
11
+
12
+ Args:
13
+ path: Full path or filename.
14
+
15
+ Returns:
16
+ Just the filename portion.
17
+ """
18
+ if isinstance(path, Path):
19
+ return path.name
20
+ return Path(path).name
21
+
22
+
23
+ class SpatialMemoryError(Exception):
24
+ """Base exception for all spatial memory errors."""
25
+
26
+ pass
27
+
28
+
29
+ class MemoryNotFoundError(SpatialMemoryError):
30
+ """Raised when a memory ID doesn't exist."""
31
+
32
+ def __init__(self, memory_id: str) -> None:
33
+ self.memory_id = memory_id
34
+ super().__init__(f"Memory not found: {memory_id}")
35
+
36
+
37
+ class NamespaceNotFoundError(SpatialMemoryError):
38
+ """Raised when a namespace doesn't exist."""
39
+
40
+ def __init__(self, namespace: str) -> None:
41
+ self.namespace = namespace
42
+ super().__init__(f"Namespace not found: {namespace}")
43
+
44
+
45
+ class EmbeddingError(SpatialMemoryError):
46
+ """Raised when embedding generation fails."""
47
+
48
+ pass
49
+
50
+
51
+ class StorageError(SpatialMemoryError):
52
+ """Raised when database operations fail."""
53
+
54
+ pass
55
+
56
+
57
+ class PartialBatchInsertError(StorageError):
58
+ """Raised when batch insert partially fails.
59
+
60
+ Provides information about which records were successfully inserted
61
+ before the failure, enabling recovery or rollback.
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ message: str,
67
+ succeeded_ids: list[str],
68
+ total_requested: int,
69
+ failed_batch_index: int | None = None,
70
+ ) -> None:
71
+ """Initialize with details about partial failure.
72
+
73
+ Args:
74
+ message: Error description.
75
+ succeeded_ids: IDs of successfully inserted records.
76
+ total_requested: Total number of records requested to insert.
77
+ failed_batch_index: Index of the batch that failed (if batched).
78
+ """
79
+ self.succeeded_ids = succeeded_ids
80
+ self.total_requested = total_requested
81
+ self.failed_batch_index = failed_batch_index
82
+ super().__init__(
83
+ f"{message}. "
84
+ f"Inserted {len(succeeded_ids)}/{total_requested} records before failure."
85
+ )
86
+
87
+
88
+ class ValidationError(SpatialMemoryError):
89
+ """Raised when input validation fails."""
90
+
91
+ pass
92
+
93
+
94
+ class ConfigurationError(SpatialMemoryError):
95
+ """Raised when configuration is invalid."""
96
+
97
+ pass
98
+
99
+
100
+ class ClusteringError(SpatialMemoryError):
101
+ """Raised when clustering fails (e.g., too few memories)."""
102
+
103
+ pass
104
+
105
+
106
+ class VisualizationError(SpatialMemoryError):
107
+ """Raised when visualization generation fails."""
108
+
109
+ pass
110
+
111
+
112
+ class InsufficientMemoriesError(SpatialMemoryError):
113
+ """Raised when operation requires more memories than available."""
114
+
115
+ def __init__(self, required: int, available: int, operation: str) -> None:
116
+ self.required = required
117
+ self.available = available
118
+ self.operation = operation
119
+ super().__init__(
120
+ f"{operation} requires at least {required} memories, but only {available} available"
121
+ )
122
+
123
+
124
+ class JourneyError(SpatialMemoryError):
125
+ """Raised when journey path cannot be computed."""
126
+
127
+ pass
128
+
129
+
130
+ class WanderError(SpatialMemoryError):
131
+ """Raised when wander cannot continue."""
132
+
133
+ pass
134
+
135
+
136
+ class DecayError(SpatialMemoryError):
137
+ """Raised when decay calculation or application fails."""
138
+
139
+ pass
140
+
141
+
142
+ class ReinforcementError(SpatialMemoryError):
143
+ """Raised when reinforcement fails."""
144
+
145
+ pass
146
+
147
+
148
+ class ExtractionError(SpatialMemoryError):
149
+ """Raised when memory extraction fails."""
150
+
151
+ pass
152
+
153
+
154
+ class ConsolidationError(SpatialMemoryError):
155
+ """Raised when consolidation fails."""
156
+
157
+ pass
158
+
159
+
160
+ # =============================================================================
161
+ # Phase 5 Error Types - Utility Operations
162
+ # =============================================================================
163
+
164
+
165
+ class ExportError(SpatialMemoryError):
166
+ """Raised when memory export fails."""
167
+
168
+ pass
169
+
170
+
171
+ class MemoryImportError(SpatialMemoryError):
172
+ """Raised when memory import fails.
173
+
174
+ Note: Named MemoryImportError to avoid shadowing Python's built-in ImportError.
175
+ """
176
+
177
+ pass
178
+
179
+
180
+ class NamespaceOperationError(SpatialMemoryError):
181
+ """Raised when namespace operation fails."""
182
+
183
+ pass
184
+
185
+
186
+ class PathSecurityError(SpatialMemoryError):
187
+ """Raised when a file path violates security constraints.
188
+
189
+ Examples:
190
+ - Path traversal attempt (../)
191
+ - Path outside allowed directories
192
+ - Symlink to disallowed location
193
+ - Invalid file extension
194
+
195
+ Note:
196
+ Error messages only include the filename, not the full path,
197
+ to avoid leaking system directory structure.
198
+ """
199
+
200
+ def __init__(
201
+ self,
202
+ path: str,
203
+ violation_type: str,
204
+ message: str | None = None,
205
+ ) -> None:
206
+ self.path = path
207
+ self.violation_type = violation_type
208
+ safe_name = sanitize_path_for_error(path)
209
+ self.message = message or f"Path security violation ({violation_type}): {safe_name}"
210
+ super().__init__(self.message)
211
+
212
+
213
+ class FileSizeLimitError(SpatialMemoryError):
214
+ """Raised when a file exceeds size limits.
215
+
216
+ Note:
217
+ Error messages only include the filename, not the full path,
218
+ to avoid leaking system directory structure.
219
+ """
220
+
221
+ def __init__(
222
+ self,
223
+ path: str,
224
+ actual_size_bytes: int,
225
+ max_size_bytes: int,
226
+ ) -> None:
227
+ self.path = path
228
+ self.actual_size_bytes = actual_size_bytes
229
+ self.max_size_bytes = max_size_bytes
230
+ actual_mb = actual_size_bytes / (1024 * 1024)
231
+ max_mb = max_size_bytes / (1024 * 1024)
232
+ safe_name = sanitize_path_for_error(path)
233
+ super().__init__(
234
+ f"File exceeds size limit: {safe_name} is {actual_mb:.2f}MB "
235
+ f"(max: {max_mb:.2f}MB)"
236
+ )
237
+
238
+
239
+ class DimensionMismatchError(ValidationError):
240
+ """Raised when imported vectors have wrong dimensions."""
241
+
242
+ def __init__(
243
+ self,
244
+ expected_dim: int,
245
+ actual_dim: int,
246
+ record_index: int | None = None,
247
+ ) -> None:
248
+ self.expected_dim = expected_dim
249
+ self.actual_dim = actual_dim
250
+ self.record_index = record_index
251
+ location = f" at record {record_index}" if record_index is not None else ""
252
+ super().__init__(
253
+ f"Vector dimension mismatch{location}: expected {expected_dim}, "
254
+ f"got {actual_dim}"
255
+ )
256
+
257
+
258
+ class SchemaValidationError(ValidationError):
259
+ """Raised when import data fails schema validation."""
260
+
261
+ def __init__(
262
+ self,
263
+ field: str,
264
+ error: str,
265
+ record_index: int | None = None,
266
+ ) -> None:
267
+ self.field = field
268
+ self.error = error
269
+ self.record_index = record_index
270
+ location = f" at record {record_index}" if record_index is not None else ""
271
+ super().__init__(f"Schema validation failed for '{field}'{location}: {error}")
272
+
273
+
274
+ class ImportRecordLimitError(SpatialMemoryError):
275
+ """Raised when import file contains too many records."""
276
+
277
+ def __init__(
278
+ self,
279
+ actual_count: int,
280
+ max_count: int,
281
+ ) -> None:
282
+ self.actual_count = actual_count
283
+ self.max_count = max_count
284
+ super().__init__(
285
+ f"Import file contains {actual_count} records " f"(max: {max_count})"
286
+ )
287
+
288
+
289
+ # =============================================================================
290
+ # Cross-Process Locking Error
291
+ # =============================================================================
292
+
293
+
294
+ class FileLockError(SpatialMemoryError):
295
+ """Raised when cross-process file lock cannot be acquired."""
296
+
297
+ def __init__(
298
+ self,
299
+ lock_path: str,
300
+ timeout: float,
301
+ message: str | None = None,
302
+ ) -> None:
303
+ self.lock_path = lock_path
304
+ self.timeout = timeout
305
+ self.message = message or f"Failed to acquire file lock at {lock_path} after {timeout}s"
306
+ super().__init__(self.message)
307
+
308
+
309
+ # =============================================================================
310
+ # Migration Error
311
+ # =============================================================================
312
+
313
+
314
+ class MigrationError(SpatialMemoryError):
315
+ """Raised when a database migration fails."""
316
+
317
+ pass