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,444 @@
1
+ """Service factory for dependency injection and initialization.
2
+
3
+ This module provides a factory pattern for creating and wiring all services
4
+ used by the SpatialMemoryServer. It centralizes configuration and dependency
5
+ injection, making the server initialization cleaner and services more testable.
6
+
7
+ Usage:
8
+ from spatial_memory.factory import ServiceFactory
9
+
10
+ factory = ServiceFactory(settings)
11
+ services = factory.create_all()
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from dataclasses import dataclass
18
+
19
+ from spatial_memory.adapters.lancedb_repository import LanceDBMemoryRepository
20
+ from spatial_memory.config import Settings
21
+ from spatial_memory.core.cache import ResponseCache
22
+ from spatial_memory.core.database import Database
23
+ from spatial_memory.core.embeddings import EmbeddingService
24
+ from spatial_memory.core.models import AutoDecayConfig
25
+ from spatial_memory.core.rate_limiter import AgentAwareRateLimiter, RateLimiter
26
+ from spatial_memory.ports.repositories import (
27
+ EmbeddingServiceProtocol,
28
+ MemoryRepositoryProtocol,
29
+ )
30
+ from spatial_memory.services.decay_manager import DecayManager
31
+ from spatial_memory.services.export_import import ExportImportConfig, ExportImportService
32
+ from spatial_memory.services.lifecycle import LifecycleConfig, LifecycleService
33
+ from spatial_memory.services.memory import MemoryService
34
+ from spatial_memory.services.spatial import SpatialConfig, SpatialService
35
+ from spatial_memory.services.utility import UtilityConfig, UtilityService
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ @dataclass
41
+ class ServiceContainer:
42
+ """Container for all initialized services.
43
+
44
+ Provides access to all service instances created by the factory.
45
+ This allows the server to access services through a single container
46
+ rather than managing individual references.
47
+
48
+ Attributes:
49
+ embeddings: Embedding service for vector generation.
50
+ database: Database connection and operations.
51
+ repository: Memory repository for CRUD operations.
52
+ memory: Memory service for remember/recall operations.
53
+ spatial: Spatial service for exploration operations.
54
+ lifecycle: Lifecycle service for decay/reinforce/consolidate.
55
+ utility: Utility service for stats/namespaces/hybrid search.
56
+ export_import: Export/import service for data portability.
57
+ decay_manager: Automatic decay manager for real-time importance decay.
58
+ rate_limiter: Simple rate limiter (if per-agent disabled).
59
+ agent_rate_limiter: Per-agent rate limiter (if enabled).
60
+ cache: Response cache for read operations.
61
+ per_agent_rate_limiting: Whether per-agent rate limiting is enabled.
62
+ cache_enabled: Whether response caching is enabled.
63
+ regions_cache_ttl: TTL for regions cache entries.
64
+ """
65
+
66
+ embeddings: EmbeddingServiceProtocol
67
+ database: Database | None
68
+ repository: MemoryRepositoryProtocol
69
+ memory: MemoryService
70
+ spatial: SpatialService
71
+ lifecycle: LifecycleService
72
+ utility: UtilityService
73
+ export_import: ExportImportService
74
+ decay_manager: DecayManager | None
75
+ rate_limiter: RateLimiter | None
76
+ agent_rate_limiter: AgentAwareRateLimiter | None
77
+ cache: ResponseCache | None
78
+ per_agent_rate_limiting: bool
79
+ cache_enabled: bool
80
+ regions_cache_ttl: float
81
+
82
+
83
+ class ServiceFactory:
84
+ """Factory for creating and wiring all services.
85
+
86
+ Centralizes service creation with proper dependency injection.
87
+ This simplifies server initialization and improves testability.
88
+
89
+ Example:
90
+ factory = ServiceFactory(settings)
91
+ services = factory.create_all()
92
+ # Use services.memory, services.spatial, etc.
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ settings: Settings,
98
+ repository: MemoryRepositoryProtocol | None = None,
99
+ embeddings: EmbeddingServiceProtocol | None = None,
100
+ ) -> None:
101
+ """Initialize the factory.
102
+
103
+ Args:
104
+ settings: Application settings.
105
+ repository: Optional repository override for testing.
106
+ embeddings: Optional embeddings override for testing.
107
+ """
108
+ self._settings = settings
109
+ self._injected_repository = repository
110
+ self._injected_embeddings = embeddings
111
+
112
+ def create_embedding_service(self) -> EmbeddingService:
113
+ """Create the embedding service.
114
+
115
+ Returns:
116
+ Configured EmbeddingService instance.
117
+ """
118
+ return EmbeddingService(
119
+ model_name=self._settings.embedding_model,
120
+ openai_api_key=self._settings.openai_api_key,
121
+ backend=self._settings.embedding_backend, # type: ignore[arg-type]
122
+ )
123
+
124
+ def create_database(self, embedding_dim: int) -> Database:
125
+ """Create the database connection.
126
+
127
+ Args:
128
+ embedding_dim: Dimension of embedding vectors.
129
+
130
+ Returns:
131
+ Configured Database instance.
132
+ """
133
+ db = Database(
134
+ storage_path=self._settings.memory_path,
135
+ embedding_dim=embedding_dim,
136
+ auto_create_indexes=self._settings.auto_create_indexes,
137
+ vector_index_threshold=self._settings.vector_index_threshold,
138
+ enable_fts=self._settings.enable_fts_index,
139
+ index_nprobes=self._settings.index_nprobes,
140
+ index_refine_factor=self._settings.index_refine_factor,
141
+ max_retry_attempts=self._settings.max_retry_attempts,
142
+ retry_backoff_seconds=self._settings.retry_backoff_seconds,
143
+ read_consistency_interval_ms=self._settings.read_consistency_interval_ms,
144
+ index_wait_timeout_seconds=self._settings.index_wait_timeout_seconds,
145
+ fts_stem=self._settings.fts_stem,
146
+ fts_remove_stop_words=self._settings.fts_remove_stop_words,
147
+ fts_language=self._settings.fts_language,
148
+ index_type=self._settings.index_type,
149
+ hnsw_m=self._settings.hnsw_m,
150
+ hnsw_ef_construction=self._settings.hnsw_ef_construction,
151
+ enable_memory_expiration=self._settings.enable_memory_expiration,
152
+ default_memory_ttl_days=self._settings.default_memory_ttl_days,
153
+ acknowledge_network_filesystem_risk=self._settings.acknowledge_network_filesystem_risk,
154
+ )
155
+ db.connect()
156
+ return db
157
+
158
+ def create_repository(self, database: Database) -> LanceDBMemoryRepository:
159
+ """Create the memory repository.
160
+
161
+ Args:
162
+ database: Database instance.
163
+
164
+ Returns:
165
+ LanceDBMemoryRepository instance.
166
+ """
167
+ return LanceDBMemoryRepository(database)
168
+
169
+ def create_memory_service(
170
+ self,
171
+ repository: MemoryRepositoryProtocol,
172
+ embeddings: EmbeddingServiceProtocol,
173
+ ) -> MemoryService:
174
+ """Create the memory service.
175
+
176
+ Args:
177
+ repository: Memory repository.
178
+ embeddings: Embedding service.
179
+
180
+ Returns:
181
+ Configured MemoryService instance.
182
+ """
183
+ return MemoryService(
184
+ repository=repository,
185
+ embeddings=embeddings,
186
+ )
187
+
188
+ def create_spatial_service(
189
+ self,
190
+ repository: MemoryRepositoryProtocol,
191
+ embeddings: EmbeddingServiceProtocol,
192
+ ) -> SpatialService:
193
+ """Create the spatial service.
194
+
195
+ Args:
196
+ repository: Memory repository.
197
+ embeddings: Embedding service.
198
+
199
+ Returns:
200
+ Configured SpatialService instance.
201
+ """
202
+ return SpatialService(
203
+ repository=repository,
204
+ embeddings=embeddings,
205
+ config=SpatialConfig(
206
+ journey_max_steps=self._settings.max_journey_steps,
207
+ wander_max_steps=self._settings.max_wander_steps,
208
+ regions_max_memories=self._settings.regions_max_memories,
209
+ visualize_max_memories=self._settings.max_visualize_memories,
210
+ visualize_n_neighbors=self._settings.umap_n_neighbors,
211
+ visualize_min_dist=self._settings.umap_min_dist,
212
+ visualize_similarity_threshold=self._settings.visualize_similarity_threshold,
213
+ ),
214
+ )
215
+
216
+ def create_lifecycle_service(
217
+ self,
218
+ repository: MemoryRepositoryProtocol,
219
+ embeddings: EmbeddingServiceProtocol,
220
+ ) -> LifecycleService:
221
+ """Create the lifecycle service.
222
+
223
+ Args:
224
+ repository: Memory repository.
225
+ embeddings: Embedding service.
226
+
227
+ Returns:
228
+ Configured LifecycleService instance.
229
+ """
230
+ return LifecycleService(
231
+ repository=repository,
232
+ embeddings=embeddings,
233
+ config=LifecycleConfig(
234
+ decay_default_half_life_days=self._settings.decay_default_half_life_days,
235
+ decay_default_function=self._settings.decay_default_function,
236
+ decay_min_importance_floor=self._settings.decay_min_importance_floor,
237
+ decay_batch_size=self._settings.decay_batch_size,
238
+ reinforce_default_boost=self._settings.reinforce_default_boost,
239
+ reinforce_max_importance=self._settings.reinforce_max_importance,
240
+ extract_max_text_length=self._settings.extract_max_text_length,
241
+ extract_max_candidates=self._settings.extract_max_candidates,
242
+ extract_default_importance=self._settings.extract_default_importance,
243
+ extract_default_namespace=self._settings.extract_default_namespace,
244
+ consolidate_min_threshold=self._settings.consolidate_min_threshold,
245
+ consolidate_content_weight=self._settings.consolidate_content_weight,
246
+ consolidate_max_batch=self._settings.consolidate_max_batch,
247
+ ),
248
+ )
249
+
250
+ def create_utility_service(
251
+ self,
252
+ repository: MemoryRepositoryProtocol,
253
+ embeddings: EmbeddingServiceProtocol,
254
+ ) -> UtilityService:
255
+ """Create the utility service.
256
+
257
+ Args:
258
+ repository: Memory repository.
259
+ embeddings: Embedding service.
260
+
261
+ Returns:
262
+ Configured UtilityService instance.
263
+ """
264
+ return UtilityService(
265
+ repository=repository,
266
+ embeddings=embeddings,
267
+ config=UtilityConfig(
268
+ hybrid_default_alpha=self._settings.hybrid_default_alpha,
269
+ hybrid_min_alpha=self._settings.hybrid_min_alpha,
270
+ hybrid_max_alpha=self._settings.hybrid_max_alpha,
271
+ stats_include_index_details=True,
272
+ namespace_batch_size=self._settings.namespace_batch_size,
273
+ delete_namespace_require_confirmation=self._settings.destructive_require_namespace_confirmation,
274
+ ),
275
+ )
276
+
277
+ def create_export_import_service(
278
+ self,
279
+ repository: MemoryRepositoryProtocol,
280
+ embeddings: EmbeddingServiceProtocol,
281
+ ) -> ExportImportService:
282
+ """Create the export/import service.
283
+
284
+ Args:
285
+ repository: Memory repository.
286
+ embeddings: Embedding service.
287
+
288
+ Returns:
289
+ Configured ExportImportService instance.
290
+ """
291
+ return ExportImportService(
292
+ repository=repository,
293
+ embeddings=embeddings,
294
+ config=ExportImportConfig(
295
+ default_export_format=self._settings.export_default_format,
296
+ export_batch_size=self._settings.export_batch_size,
297
+ import_batch_size=self._settings.import_batch_size,
298
+ import_deduplicate=self._settings.import_deduplicate_default,
299
+ import_dedup_threshold=self._settings.import_dedup_threshold,
300
+ validate_on_import=self._settings.import_validate_vectors,
301
+ parquet_compression="zstd",
302
+ max_import_records=self._settings.import_max_records,
303
+ csv_include_vectors=self._settings.csv_include_vectors,
304
+ max_export_records=self._settings.max_export_records,
305
+ ),
306
+ allowed_export_paths=self._settings.export_allowed_paths,
307
+ allowed_import_paths=self._settings.import_allowed_paths,
308
+ allow_symlinks=self._settings.export_allow_symlinks,
309
+ max_import_size_bytes=int(self._settings.import_max_file_size_mb * 1024 * 1024),
310
+ )
311
+
312
+ def create_rate_limiter(self) -> tuple[RateLimiter | None, AgentAwareRateLimiter | None, bool]:
313
+ """Create rate limiter based on settings.
314
+
315
+ Returns:
316
+ Tuple of (simple_limiter, agent_limiter, per_agent_enabled).
317
+ """
318
+ per_agent = self._settings.rate_limit_per_agent_enabled
319
+ if per_agent:
320
+ return (
321
+ None,
322
+ AgentAwareRateLimiter(
323
+ global_rate=self._settings.embedding_rate_limit,
324
+ per_agent_rate=self._settings.rate_limit_per_agent_rate,
325
+ max_agents=self._settings.rate_limit_max_tracked_agents,
326
+ ),
327
+ True,
328
+ )
329
+ return (
330
+ RateLimiter(
331
+ rate=self._settings.embedding_rate_limit,
332
+ capacity=int(self._settings.embedding_rate_limit * 2),
333
+ ),
334
+ None,
335
+ False,
336
+ )
337
+
338
+ def create_cache(self) -> tuple[ResponseCache | None, bool, float]:
339
+ """Create response cache based on settings.
340
+
341
+ Returns:
342
+ Tuple of (cache, enabled, regions_ttl).
343
+ """
344
+ if not self._settings.response_cache_enabled:
345
+ return None, False, 0.0
346
+ return (
347
+ ResponseCache(
348
+ max_size=self._settings.response_cache_max_size,
349
+ default_ttl=self._settings.response_cache_default_ttl,
350
+ ),
351
+ True,
352
+ self._settings.response_cache_regions_ttl,
353
+ )
354
+
355
+ def create_decay_manager(
356
+ self,
357
+ repository: MemoryRepositoryProtocol,
358
+ ) -> DecayManager | None:
359
+ """Create the decay manager based on settings.
360
+
361
+ Args:
362
+ repository: Repository for persisting decay updates.
363
+
364
+ Returns:
365
+ DecayManager if auto-decay is enabled, None otherwise.
366
+ """
367
+ if not self._settings.auto_decay_enabled:
368
+ return None
369
+
370
+ config = AutoDecayConfig(
371
+ enabled=self._settings.auto_decay_enabled,
372
+ persist_enabled=self._settings.auto_decay_persist_enabled,
373
+ persist_batch_size=self._settings.auto_decay_persist_batch_size,
374
+ persist_flush_interval_seconds=self._settings.auto_decay_persist_flush_interval_seconds,
375
+ min_change_threshold=self._settings.auto_decay_min_change_threshold,
376
+ max_queue_size=self._settings.auto_decay_max_queue_size,
377
+ half_life_days=self._settings.decay_default_half_life_days,
378
+ min_importance_floor=self._settings.decay_min_importance_floor,
379
+ access_weight=0.3, # Default access weight
380
+ decay_function=self._settings.auto_decay_function, # type: ignore[arg-type]
381
+ )
382
+
383
+ return DecayManager(repository=repository, config=config)
384
+
385
+ def create_all(self) -> ServiceContainer:
386
+ """Create all services with proper dependency wiring.
387
+
388
+ Returns:
389
+ ServiceContainer with all services initialized.
390
+ """
391
+ # Use injected dependencies or create new ones
392
+ embeddings: EmbeddingServiceProtocol
393
+ if self._injected_embeddings is None:
394
+ embeddings = self.create_embedding_service()
395
+ else:
396
+ embeddings = self._injected_embeddings
397
+
398
+ # Auto-detect embedding dimensions
399
+ embedding_dim = embeddings.dimensions
400
+ logger.info(f"Auto-detected embedding dimensions: {embedding_dim}")
401
+ logger.info(f"Embedding backend: {embeddings.backend}")
402
+
403
+ # Create database and repository
404
+ database: Database | None = None
405
+ repository: MemoryRepositoryProtocol
406
+ if self._injected_repository is None:
407
+ database = self.create_database(embedding_dim)
408
+ repository = self.create_repository(database)
409
+ else:
410
+ repository = self._injected_repository
411
+
412
+ # Create services with shared dependencies
413
+ memory = self.create_memory_service(repository, embeddings)
414
+ spatial = self.create_spatial_service(repository, embeddings)
415
+ lifecycle = self.create_lifecycle_service(repository, embeddings)
416
+ utility = self.create_utility_service(repository, embeddings)
417
+ export_import = self.create_export_import_service(repository, embeddings)
418
+
419
+ # Create decay manager
420
+ decay_manager = self.create_decay_manager(repository)
421
+
422
+ # Create rate limiter
423
+ rate_limiter, agent_rate_limiter, per_agent_enabled = self.create_rate_limiter()
424
+
425
+ # Create cache
426
+ cache, cache_enabled, regions_cache_ttl = self.create_cache()
427
+
428
+ return ServiceContainer(
429
+ embeddings=embeddings,
430
+ database=database,
431
+ repository=repository,
432
+ memory=memory,
433
+ spatial=spatial,
434
+ lifecycle=lifecycle,
435
+ utility=utility,
436
+ export_import=export_import,
437
+ decay_manager=decay_manager,
438
+ rate_limiter=rate_limiter,
439
+ agent_rate_limiter=agent_rate_limiter,
440
+ cache=cache,
441
+ per_agent_rate_limiting=per_agent_enabled,
442
+ cache_enabled=cache_enabled,
443
+ regions_cache_ttl=regions_cache_ttl,
444
+ )
@@ -0,0 +1,40 @@
1
+ """Database migrations for spatial-memory-mcp.
2
+
3
+ This package contains database migration scripts for schema changes.
4
+ Each migration is a module that exports a Migration class.
5
+
6
+ Migrations should be numbered sequentially (001, 002, etc.) and use
7
+ semantic versioning for their version string.
8
+
9
+ Example:
10
+ # migrations/001_add_expires_at.py
11
+ from spatial_memory.core.db_migrations import Migration
12
+
13
+ class Migration001AddExpiresAt(Migration):
14
+ version = "1.1.0"
15
+ description = "Add expires_at column for TTL support"
16
+
17
+ def up(self, db, embeddings=None):
18
+ # Apply migration
19
+ pass
20
+
21
+ def down(self, db):
22
+ # Rollback migration (optional)
23
+ raise NotImplementedError("Rollback not supported")
24
+ """
25
+
26
+ from spatial_memory.core.db_migrations import (
27
+ CURRENT_SCHEMA_VERSION,
28
+ Migration,
29
+ MigrationManager,
30
+ MigrationResult,
31
+ check_migration_status,
32
+ )
33
+
34
+ __all__ = [
35
+ "CURRENT_SCHEMA_VERSION",
36
+ "Migration",
37
+ "MigrationManager",
38
+ "MigrationResult",
39
+ "check_migration_status",
40
+ ]
@@ -0,0 +1,11 @@
1
+ """Port interfaces for Spatial Memory MCP Server."""
2
+
3
+ from spatial_memory.ports.repositories import (
4
+ EmbeddingServiceProtocol,
5
+ MemoryRepositoryProtocol,
6
+ )
7
+
8
+ __all__ = [
9
+ "EmbeddingServiceProtocol",
10
+ "MemoryRepositoryProtocol",
11
+ ]