spatial-memory-mcp 1.5.3__py3-none-any.whl → 1.6.0__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 spatial-memory-mcp might be problematic. Click here for more details.
- spatial_memory/__init__.py +1 -1
- spatial_memory/__main__.py +241 -2
- spatial_memory/adapters/lancedb_repository.py +74 -5
- spatial_memory/config.py +10 -2
- spatial_memory/core/__init__.py +9 -0
- spatial_memory/core/connection_pool.py +41 -3
- spatial_memory/core/consolidation_strategies.py +402 -0
- spatial_memory/core/database.py +774 -918
- spatial_memory/core/db_idempotency.py +242 -0
- spatial_memory/core/db_indexes.py +575 -0
- spatial_memory/core/db_migrations.py +584 -0
- spatial_memory/core/db_search.py +509 -0
- spatial_memory/core/db_versioning.py +177 -0
- spatial_memory/core/embeddings.py +65 -18
- spatial_memory/core/errors.py +75 -3
- spatial_memory/core/filesystem.py +178 -0
- spatial_memory/core/models.py +4 -0
- spatial_memory/core/rate_limiter.py +26 -9
- spatial_memory/core/response_types.py +497 -0
- spatial_memory/core/validation.py +86 -2
- spatial_memory/factory.py +407 -0
- spatial_memory/migrations/__init__.py +40 -0
- spatial_memory/ports/repositories.py +52 -2
- spatial_memory/server.py +131 -189
- spatial_memory/services/export_import.py +61 -43
- spatial_memory/services/lifecycle.py +397 -122
- spatial_memory/services/memory.py +2 -2
- spatial_memory/services/spatial.py +129 -46
- {spatial_memory_mcp-1.5.3.dist-info → spatial_memory_mcp-1.6.0.dist-info}/METADATA +83 -3
- spatial_memory_mcp-1.6.0.dist-info/RECORD +54 -0
- spatial_memory_mcp-1.5.3.dist-info/RECORD +0 -44
- {spatial_memory_mcp-1.5.3.dist-info → spatial_memory_mcp-1.6.0.dist-info}/WHEEL +0 -0
- {spatial_memory_mcp-1.5.3.dist-info → spatial_memory_mcp-1.6.0.dist-info}/entry_points.txt +0 -0
- {spatial_memory_mcp-1.5.3.dist-info → spatial_memory_mcp-1.6.0.dist-info}/licenses/LICENSE +0 -0
spatial_memory/server.py
CHANGED
|
@@ -14,7 +14,9 @@ import signal
|
|
|
14
14
|
import sys
|
|
15
15
|
import uuid
|
|
16
16
|
from collections.abc import Callable
|
|
17
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
17
18
|
from dataclasses import asdict
|
|
19
|
+
from functools import partial
|
|
18
20
|
from typing import TYPE_CHECKING, Any
|
|
19
21
|
|
|
20
22
|
from mcp.server import Server
|
|
@@ -22,15 +24,12 @@ from mcp.server.stdio import stdio_server
|
|
|
22
24
|
from mcp.types import TextContent, Tool
|
|
23
25
|
|
|
24
26
|
from spatial_memory import __version__
|
|
25
|
-
from spatial_memory.adapters.lancedb_repository import LanceDBMemoryRepository
|
|
26
27
|
from spatial_memory.config import ConfigurationError, get_settings, validate_startup
|
|
27
|
-
from spatial_memory.
|
|
28
|
+
from spatial_memory.factory import ServiceFactory
|
|
28
29
|
from spatial_memory.core.database import (
|
|
29
|
-
Database,
|
|
30
30
|
clear_connection_cache,
|
|
31
31
|
set_connection_pool_max_size,
|
|
32
32
|
)
|
|
33
|
-
from spatial_memory.core.embeddings import EmbeddingService
|
|
34
33
|
from spatial_memory.core.errors import (
|
|
35
34
|
ConsolidationError,
|
|
36
35
|
DecayError,
|
|
@@ -47,21 +46,40 @@ from spatial_memory.core.errors import (
|
|
|
47
46
|
SpatialMemoryError,
|
|
48
47
|
ValidationError,
|
|
49
48
|
)
|
|
49
|
+
from spatial_memory.core.response_types import (
|
|
50
|
+
ConsolidateResponse,
|
|
51
|
+
DecayResponse,
|
|
52
|
+
DeleteNamespaceResponse,
|
|
53
|
+
ExportResponse,
|
|
54
|
+
ExtractResponse,
|
|
55
|
+
ForgetBatchResponse,
|
|
56
|
+
ForgetResponse,
|
|
57
|
+
HandlerResponse,
|
|
58
|
+
HealthResponse,
|
|
59
|
+
HybridRecallResponse,
|
|
60
|
+
ImportResponse,
|
|
61
|
+
JourneyResponse,
|
|
62
|
+
NamespacesResponse,
|
|
63
|
+
NearbyResponse,
|
|
64
|
+
RecallResponse,
|
|
65
|
+
RegionsResponse,
|
|
66
|
+
ReinforceResponse,
|
|
67
|
+
RememberBatchResponse,
|
|
68
|
+
RememberResponse,
|
|
69
|
+
RenameNamespaceResponse,
|
|
70
|
+
StatsResponse,
|
|
71
|
+
VisualizeResponse,
|
|
72
|
+
WanderResponse,
|
|
73
|
+
)
|
|
50
74
|
from spatial_memory.core.health import HealthChecker
|
|
51
75
|
from spatial_memory.core.logging import configure_logging
|
|
52
76
|
from spatial_memory.core.metrics import is_available as metrics_available
|
|
53
77
|
from spatial_memory.core.metrics import record_request
|
|
54
|
-
from spatial_memory.core.rate_limiter import AgentAwareRateLimiter, RateLimiter
|
|
55
78
|
from spatial_memory.core.tracing import (
|
|
56
79
|
RequestContext,
|
|
57
80
|
TimingContext,
|
|
58
81
|
request_context,
|
|
59
82
|
)
|
|
60
|
-
from spatial_memory.services.export_import import ExportImportConfig, ExportImportService
|
|
61
|
-
from spatial_memory.services.lifecycle import LifecycleConfig, LifecycleService
|
|
62
|
-
from spatial_memory.services.memory import MemoryService
|
|
63
|
-
from spatial_memory.services.spatial import SpatialConfig, SpatialService
|
|
64
|
-
from spatial_memory.services.utility import UtilityConfig, UtilityService
|
|
65
83
|
from spatial_memory.tools import TOOLS
|
|
66
84
|
|
|
67
85
|
if TYPE_CHECKING:
|
|
@@ -149,163 +167,45 @@ class SpatialMemoryServer:
|
|
|
149
167
|
embeddings: Optional embedding service (uses local model if not provided).
|
|
150
168
|
"""
|
|
151
169
|
self._settings = get_settings()
|
|
152
|
-
self._db: Database | None = None
|
|
153
170
|
|
|
154
171
|
# Configure connection pool size from settings
|
|
155
172
|
set_connection_pool_max_size(self._settings.connection_pool_max_size)
|
|
156
173
|
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if embeddings is None:
|
|
161
|
-
embeddings = EmbeddingService(
|
|
162
|
-
model_name=self._settings.embedding_model,
|
|
163
|
-
openai_api_key=self._settings.openai_api_key,
|
|
164
|
-
backend=self._settings.embedding_backend, # type: ignore[arg-type]
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
# Auto-detect embedding dimensions from the model
|
|
168
|
-
embedding_dim = embeddings.dimensions
|
|
169
|
-
logger.info(f"Auto-detected embedding dimensions: {embedding_dim}")
|
|
170
|
-
logger.info(f"Embedding backend: {embeddings.backend}")
|
|
171
|
-
|
|
172
|
-
# Create database with all config values wired
|
|
173
|
-
self._db = Database(
|
|
174
|
-
storage_path=self._settings.memory_path,
|
|
175
|
-
embedding_dim=embedding_dim,
|
|
176
|
-
auto_create_indexes=self._settings.auto_create_indexes,
|
|
177
|
-
vector_index_threshold=self._settings.vector_index_threshold,
|
|
178
|
-
enable_fts=self._settings.enable_fts_index,
|
|
179
|
-
index_nprobes=self._settings.index_nprobes,
|
|
180
|
-
index_refine_factor=self._settings.index_refine_factor,
|
|
181
|
-
max_retry_attempts=self._settings.max_retry_attempts,
|
|
182
|
-
retry_backoff_seconds=self._settings.retry_backoff_seconds,
|
|
183
|
-
read_consistency_interval_ms=self._settings.read_consistency_interval_ms,
|
|
184
|
-
index_wait_timeout_seconds=self._settings.index_wait_timeout_seconds,
|
|
185
|
-
fts_stem=self._settings.fts_stem,
|
|
186
|
-
fts_remove_stop_words=self._settings.fts_remove_stop_words,
|
|
187
|
-
fts_language=self._settings.fts_language,
|
|
188
|
-
index_type=self._settings.index_type,
|
|
189
|
-
hnsw_m=self._settings.hnsw_m,
|
|
190
|
-
hnsw_ef_construction=self._settings.hnsw_ef_construction,
|
|
191
|
-
enable_memory_expiration=self._settings.enable_memory_expiration,
|
|
192
|
-
default_memory_ttl_days=self._settings.default_memory_ttl_days,
|
|
193
|
-
)
|
|
194
|
-
self._db.connect()
|
|
195
|
-
|
|
196
|
-
if repository is None:
|
|
197
|
-
repository = LanceDBMemoryRepository(self._db)
|
|
198
|
-
|
|
199
|
-
self._memory_service = MemoryService(
|
|
174
|
+
# Use ServiceFactory for dependency injection
|
|
175
|
+
factory = ServiceFactory(
|
|
176
|
+
settings=self._settings,
|
|
200
177
|
repository=repository,
|
|
201
178
|
embeddings=embeddings,
|
|
202
179
|
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
#
|
|
220
|
-
self.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
reinforce_default_boost=self._settings.reinforce_default_boost,
|
|
229
|
-
reinforce_max_importance=self._settings.reinforce_max_importance,
|
|
230
|
-
extract_max_text_length=self._settings.extract_max_text_length,
|
|
231
|
-
extract_max_candidates=self._settings.extract_max_candidates,
|
|
232
|
-
extract_default_importance=self._settings.extract_default_importance,
|
|
233
|
-
extract_default_namespace=self._settings.extract_default_namespace,
|
|
234
|
-
consolidate_min_threshold=self._settings.consolidate_min_threshold,
|
|
235
|
-
consolidate_content_weight=self._settings.consolidate_content_weight,
|
|
236
|
-
consolidate_max_batch=self._settings.consolidate_max_batch,
|
|
237
|
-
),
|
|
180
|
+
services = factory.create_all()
|
|
181
|
+
|
|
182
|
+
# Store service references
|
|
183
|
+
self._db = services.database
|
|
184
|
+
self._embeddings = services.embeddings
|
|
185
|
+
self._memory_service = services.memory
|
|
186
|
+
self._spatial_service = services.spatial
|
|
187
|
+
self._lifecycle_service = services.lifecycle
|
|
188
|
+
self._utility_service = services.utility
|
|
189
|
+
self._export_import_service = services.export_import
|
|
190
|
+
|
|
191
|
+
# Rate limiting
|
|
192
|
+
self._per_agent_rate_limiting = services.per_agent_rate_limiting
|
|
193
|
+
self._rate_limiter = services.rate_limiter
|
|
194
|
+
self._agent_rate_limiter = services.agent_rate_limiter
|
|
195
|
+
|
|
196
|
+
# Response cache
|
|
197
|
+
self._cache_enabled = services.cache_enabled
|
|
198
|
+
self._cache = services.cache
|
|
199
|
+
self._regions_cache_ttl = services.regions_cache_ttl
|
|
200
|
+
|
|
201
|
+
# ThreadPoolExecutor for non-blocking embedding operations
|
|
202
|
+
self._executor = ThreadPoolExecutor(
|
|
203
|
+
max_workers=2,
|
|
204
|
+
thread_name_prefix="embed-",
|
|
238
205
|
)
|
|
239
206
|
|
|
240
|
-
# Create utility service for stats, namespaces, and hybrid search
|
|
241
|
-
self._utility_service = UtilityService(
|
|
242
|
-
repository=repository,
|
|
243
|
-
embeddings=embeddings,
|
|
244
|
-
config=UtilityConfig(
|
|
245
|
-
hybrid_default_alpha=self._settings.hybrid_default_alpha,
|
|
246
|
-
hybrid_min_alpha=self._settings.hybrid_min_alpha,
|
|
247
|
-
hybrid_max_alpha=self._settings.hybrid_max_alpha,
|
|
248
|
-
stats_include_index_details=True,
|
|
249
|
-
namespace_batch_size=self._settings.namespace_batch_size,
|
|
250
|
-
delete_namespace_require_confirmation=self._settings.destructive_require_namespace_confirmation,
|
|
251
|
-
),
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
# Create export/import service for data portability
|
|
255
|
-
self._export_import_service = ExportImportService(
|
|
256
|
-
repository=repository,
|
|
257
|
-
embeddings=embeddings,
|
|
258
|
-
config=ExportImportConfig(
|
|
259
|
-
default_export_format=self._settings.export_default_format,
|
|
260
|
-
export_batch_size=self._settings.export_batch_size,
|
|
261
|
-
import_batch_size=self._settings.import_batch_size,
|
|
262
|
-
import_deduplicate=self._settings.import_deduplicate_default,
|
|
263
|
-
import_dedup_threshold=self._settings.import_dedup_threshold,
|
|
264
|
-
validate_on_import=self._settings.import_validate_vectors,
|
|
265
|
-
parquet_compression="zstd",
|
|
266
|
-
max_import_records=self._settings.import_max_records,
|
|
267
|
-
csv_include_vectors=self._settings.csv_include_vectors,
|
|
268
|
-
max_export_records=self._settings.max_export_records,
|
|
269
|
-
),
|
|
270
|
-
allowed_export_paths=self._settings.export_allowed_paths,
|
|
271
|
-
allowed_import_paths=self._settings.import_allowed_paths,
|
|
272
|
-
allow_symlinks=self._settings.export_allow_symlinks,
|
|
273
|
-
max_import_size_bytes=int(self._settings.import_max_file_size_mb * 1024 * 1024),
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
# Store embeddings and database for health checks
|
|
277
|
-
self._embeddings = embeddings
|
|
278
|
-
|
|
279
|
-
# Rate limiting for resource protection
|
|
280
|
-
# Use per-agent rate limiter if enabled, otherwise fall back to simple rate limiter
|
|
281
|
-
self._per_agent_rate_limiting = self._settings.rate_limit_per_agent_enabled
|
|
282
|
-
self._agent_rate_limiter: AgentAwareRateLimiter | None = None
|
|
283
|
-
self._rate_limiter: RateLimiter | None = None
|
|
284
|
-
if self._per_agent_rate_limiting:
|
|
285
|
-
self._agent_rate_limiter = AgentAwareRateLimiter(
|
|
286
|
-
global_rate=self._settings.embedding_rate_limit,
|
|
287
|
-
per_agent_rate=self._settings.rate_limit_per_agent_rate,
|
|
288
|
-
max_agents=self._settings.rate_limit_max_tracked_agents,
|
|
289
|
-
)
|
|
290
|
-
else:
|
|
291
|
-
self._rate_limiter = RateLimiter(
|
|
292
|
-
rate=self._settings.embedding_rate_limit,
|
|
293
|
-
capacity=int(self._settings.embedding_rate_limit * 2)
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
# Response cache for read-only operations
|
|
297
|
-
self._cache_enabled = self._settings.response_cache_enabled
|
|
298
|
-
self._cache: ResponseCache | None = None
|
|
299
|
-
self._regions_cache_ttl = 0.0
|
|
300
|
-
if self._cache_enabled:
|
|
301
|
-
self._cache = ResponseCache(
|
|
302
|
-
max_size=self._settings.response_cache_max_size,
|
|
303
|
-
default_ttl=self._settings.response_cache_default_ttl,
|
|
304
|
-
)
|
|
305
|
-
self._regions_cache_ttl = self._settings.response_cache_regions_ttl
|
|
306
|
-
|
|
307
207
|
# Tool handler registry for dispatch pattern
|
|
308
|
-
self._tool_handlers: dict[str, Callable[[dict[str, Any]],
|
|
208
|
+
self._tool_handlers: dict[str, Callable[[dict[str, Any]], HandlerResponse]] = {
|
|
309
209
|
"remember": self._handle_remember,
|
|
310
210
|
"remember_batch": self._handle_remember_batch,
|
|
311
211
|
"recall": self._handle_recall,
|
|
@@ -344,6 +244,42 @@ class SpatialMemoryServer:
|
|
|
344
244
|
)
|
|
345
245
|
self._setup_handlers()
|
|
346
246
|
|
|
247
|
+
async def _run_in_executor(self, func: Callable[..., Any], *args: Any) -> Any:
|
|
248
|
+
"""Run a synchronous function in the thread pool executor.
|
|
249
|
+
|
|
250
|
+
This allows CPU-bound or blocking operations (like embedding generation)
|
|
251
|
+
to run without blocking the asyncio event loop.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
func: The synchronous function to run.
|
|
255
|
+
*args: Arguments to pass to the function.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
The result of the function call.
|
|
259
|
+
"""
|
|
260
|
+
loop = asyncio.get_running_loop()
|
|
261
|
+
return await loop.run_in_executor(self._executor, partial(func, *args))
|
|
262
|
+
|
|
263
|
+
async def _handle_tool_async(
|
|
264
|
+
self, name: str, arguments: dict[str, Any]
|
|
265
|
+
) -> HandlerResponse:
|
|
266
|
+
"""Handle tool call asynchronously by running handler in executor.
|
|
267
|
+
|
|
268
|
+
This wraps synchronous handlers to run in a thread pool, preventing
|
|
269
|
+
blocking operations from stalling the event loop.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
name: Tool name.
|
|
273
|
+
arguments: Tool arguments.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Tool result as typed dictionary.
|
|
277
|
+
|
|
278
|
+
Raises:
|
|
279
|
+
ValidationError: If tool name is unknown.
|
|
280
|
+
"""
|
|
281
|
+
return await self._run_in_executor(self._handle_tool, name, arguments)
|
|
282
|
+
|
|
347
283
|
def _setup_handlers(self) -> None:
|
|
348
284
|
"""Set up MCP tool handlers."""
|
|
349
285
|
|
|
@@ -397,13 +333,15 @@ class SpatialMemoryServer:
|
|
|
397
333
|
result = cached_result
|
|
398
334
|
else:
|
|
399
335
|
with timing.measure("handler"):
|
|
400
|
-
|
|
336
|
+
# Run handler in executor to avoid blocking event loop
|
|
337
|
+
result = await self._handle_tool_async(name, arguments)
|
|
401
338
|
# Cache the result with appropriate TTL
|
|
402
339
|
ttl = self._regions_cache_ttl if name == "regions" else None
|
|
403
340
|
self._cache.set(cache_key, result, ttl=ttl)
|
|
404
341
|
else:
|
|
405
342
|
with timing.measure("handler"):
|
|
406
|
-
|
|
343
|
+
# Run handler in executor to avoid blocking event loop
|
|
344
|
+
result = await self._handle_tool_async(name, arguments)
|
|
407
345
|
|
|
408
346
|
# Invalidate cache on mutations
|
|
409
347
|
if self._cache_enabled and self._cache is not None:
|
|
@@ -466,7 +404,7 @@ class SpatialMemoryServer:
|
|
|
466
404
|
# Tool Handler Methods
|
|
467
405
|
# =========================================================================
|
|
468
406
|
|
|
469
|
-
def _handle_remember(self, arguments: dict[str, Any]) ->
|
|
407
|
+
def _handle_remember(self, arguments: dict[str, Any]) -> RememberResponse:
|
|
470
408
|
"""Handle remember tool call."""
|
|
471
409
|
remember_result = self._memory_service.remember(
|
|
472
410
|
content=arguments["content"],
|
|
@@ -475,16 +413,16 @@ class SpatialMemoryServer:
|
|
|
475
413
|
importance=arguments.get("importance", 0.5),
|
|
476
414
|
metadata=arguments.get("metadata"),
|
|
477
415
|
)
|
|
478
|
-
return asdict(remember_result)
|
|
416
|
+
return asdict(remember_result) # type: ignore[return-value]
|
|
479
417
|
|
|
480
|
-
def _handle_remember_batch(self, arguments: dict[str, Any]) ->
|
|
418
|
+
def _handle_remember_batch(self, arguments: dict[str, Any]) -> RememberBatchResponse:
|
|
481
419
|
"""Handle remember_batch tool call."""
|
|
482
420
|
batch_result = self._memory_service.remember_batch(
|
|
483
421
|
memories=arguments["memories"],
|
|
484
422
|
)
|
|
485
|
-
return asdict(batch_result)
|
|
423
|
+
return asdict(batch_result) # type: ignore[return-value]
|
|
486
424
|
|
|
487
|
-
def _handle_recall(self, arguments: dict[str, Any]) ->
|
|
425
|
+
def _handle_recall(self, arguments: dict[str, Any]) -> RecallResponse:
|
|
488
426
|
"""Handle recall tool call."""
|
|
489
427
|
recall_result = self._memory_service.recall(
|
|
490
428
|
query=arguments["query"],
|
|
@@ -509,7 +447,7 @@ class SpatialMemoryServer:
|
|
|
509
447
|
"total": recall_result.total,
|
|
510
448
|
}
|
|
511
449
|
|
|
512
|
-
def _handle_nearby(self, arguments: dict[str, Any]) ->
|
|
450
|
+
def _handle_nearby(self, arguments: dict[str, Any]) -> NearbyResponse:
|
|
513
451
|
"""Handle nearby tool call."""
|
|
514
452
|
nearby_result = self._memory_service.nearby(
|
|
515
453
|
memory_id=arguments["memory_id"],
|
|
@@ -533,21 +471,21 @@ class SpatialMemoryServer:
|
|
|
533
471
|
],
|
|
534
472
|
}
|
|
535
473
|
|
|
536
|
-
def _handle_forget(self, arguments: dict[str, Any]) ->
|
|
474
|
+
def _handle_forget(self, arguments: dict[str, Any]) -> ForgetResponse:
|
|
537
475
|
"""Handle forget tool call."""
|
|
538
476
|
forget_result = self._memory_service.forget(
|
|
539
477
|
memory_id=arguments["memory_id"],
|
|
540
478
|
)
|
|
541
|
-
return asdict(forget_result)
|
|
479
|
+
return asdict(forget_result) # type: ignore[return-value]
|
|
542
480
|
|
|
543
|
-
def _handle_forget_batch(self, arguments: dict[str, Any]) ->
|
|
481
|
+
def _handle_forget_batch(self, arguments: dict[str, Any]) -> ForgetBatchResponse:
|
|
544
482
|
"""Handle forget_batch tool call."""
|
|
545
483
|
forget_batch_result = self._memory_service.forget_batch(
|
|
546
484
|
memory_ids=arguments["memory_ids"],
|
|
547
485
|
)
|
|
548
|
-
return asdict(forget_batch_result)
|
|
486
|
+
return asdict(forget_batch_result) # type: ignore[return-value]
|
|
549
487
|
|
|
550
|
-
def _handle_health(self, arguments: dict[str, Any]) ->
|
|
488
|
+
def _handle_health(self, arguments: dict[str, Any]) -> HealthResponse:
|
|
551
489
|
"""Handle health tool call."""
|
|
552
490
|
verbose = arguments.get("verbose", False)
|
|
553
491
|
|
|
@@ -559,7 +497,7 @@ class SpatialMemoryServer:
|
|
|
559
497
|
|
|
560
498
|
report = health_checker.get_health_report()
|
|
561
499
|
|
|
562
|
-
result:
|
|
500
|
+
result: HealthResponse = {
|
|
563
501
|
"version": __version__,
|
|
564
502
|
"status": report.status.value,
|
|
565
503
|
"timestamp": report.timestamp.isoformat(),
|
|
@@ -580,7 +518,7 @@ class SpatialMemoryServer:
|
|
|
580
518
|
|
|
581
519
|
return result
|
|
582
520
|
|
|
583
|
-
def _handle_journey(self, arguments: dict[str, Any]) ->
|
|
521
|
+
def _handle_journey(self, arguments: dict[str, Any]) -> JourneyResponse:
|
|
584
522
|
"""Handle journey tool call."""
|
|
585
523
|
journey_result = self._spatial_service.journey(
|
|
586
524
|
start_id=arguments["start_id"],
|
|
@@ -610,7 +548,7 @@ class SpatialMemoryServer:
|
|
|
610
548
|
"path_coverage": journey_result.path_coverage,
|
|
611
549
|
}
|
|
612
550
|
|
|
613
|
-
def _handle_wander(self, arguments: dict[str, Any]) ->
|
|
551
|
+
def _handle_wander(self, arguments: dict[str, Any]) -> WanderResponse:
|
|
614
552
|
"""Handle wander tool call."""
|
|
615
553
|
start_id = arguments.get("start_id")
|
|
616
554
|
if start_id is None:
|
|
@@ -649,7 +587,7 @@ class SpatialMemoryServer:
|
|
|
649
587
|
"total_distance": wander_result.total_distance,
|
|
650
588
|
}
|
|
651
589
|
|
|
652
|
-
def _handle_regions(self, arguments: dict[str, Any]) ->
|
|
590
|
+
def _handle_regions(self, arguments: dict[str, Any]) -> RegionsResponse:
|
|
653
591
|
"""Handle regions tool call."""
|
|
654
592
|
regions_result = self._spatial_service.regions(
|
|
655
593
|
namespace=arguments.get("namespace"),
|
|
@@ -683,7 +621,7 @@ class SpatialMemoryServer:
|
|
|
683
621
|
"clustering_quality": regions_result.clustering_quality,
|
|
684
622
|
}
|
|
685
623
|
|
|
686
|
-
def _handle_visualize(self, arguments: dict[str, Any]) ->
|
|
624
|
+
def _handle_visualize(self, arguments: dict[str, Any]) -> VisualizeResponse:
|
|
687
625
|
"""Handle visualize tool call."""
|
|
688
626
|
visualize_result = self._spatial_service.visualize(
|
|
689
627
|
memory_ids=arguments.get("memory_ids"),
|
|
@@ -723,7 +661,7 @@ class SpatialMemoryServer:
|
|
|
723
661
|
"format": visualize_result.format,
|
|
724
662
|
}
|
|
725
663
|
|
|
726
|
-
def _handle_decay(self, arguments: dict[str, Any]) ->
|
|
664
|
+
def _handle_decay(self, arguments: dict[str, Any]) -> DecayResponse:
|
|
727
665
|
"""Handle decay tool call."""
|
|
728
666
|
decay_result = self._lifecycle_service.decay(
|
|
729
667
|
namespace=arguments.get("namespace"),
|
|
@@ -752,7 +690,7 @@ class SpatialMemoryServer:
|
|
|
752
690
|
"dry_run": decay_result.dry_run,
|
|
753
691
|
}
|
|
754
692
|
|
|
755
|
-
def _handle_reinforce(self, arguments: dict[str, Any]) ->
|
|
693
|
+
def _handle_reinforce(self, arguments: dict[str, Any]) -> ReinforceResponse:
|
|
756
694
|
"""Handle reinforce tool call."""
|
|
757
695
|
reinforce_result = self._lifecycle_service.reinforce(
|
|
758
696
|
memory_ids=arguments["memory_ids"],
|
|
@@ -776,7 +714,7 @@ class SpatialMemoryServer:
|
|
|
776
714
|
"not_found": reinforce_result.not_found,
|
|
777
715
|
}
|
|
778
716
|
|
|
779
|
-
def _handle_extract(self, arguments: dict[str, Any]) ->
|
|
717
|
+
def _handle_extract(self, arguments: dict[str, Any]) -> ExtractResponse:
|
|
780
718
|
"""Handle extract tool call."""
|
|
781
719
|
extract_result = self._lifecycle_service.extract(
|
|
782
720
|
text=arguments["text"],
|
|
@@ -803,7 +741,7 @@ class SpatialMemoryServer:
|
|
|
803
741
|
],
|
|
804
742
|
}
|
|
805
743
|
|
|
806
|
-
def _handle_consolidate(self, arguments: dict[str, Any]) ->
|
|
744
|
+
def _handle_consolidate(self, arguments: dict[str, Any]) -> ConsolidateResponse:
|
|
807
745
|
"""Handle consolidate tool call."""
|
|
808
746
|
consolidate_result = self._lifecycle_service.consolidate(
|
|
809
747
|
namespace=arguments["namespace"],
|
|
@@ -828,7 +766,7 @@ class SpatialMemoryServer:
|
|
|
828
766
|
"dry_run": consolidate_result.dry_run,
|
|
829
767
|
}
|
|
830
768
|
|
|
831
|
-
def _handle_stats(self, arguments: dict[str, Any]) ->
|
|
769
|
+
def _handle_stats(self, arguments: dict[str, Any]) -> StatsResponse:
|
|
832
770
|
"""Handle stats tool call."""
|
|
833
771
|
stats_result = self._utility_service.stats(
|
|
834
772
|
namespace=arguments.get("namespace"),
|
|
@@ -866,7 +804,7 @@ class SpatialMemoryServer:
|
|
|
866
804
|
"avg_content_length": stats_result.avg_content_length,
|
|
867
805
|
}
|
|
868
806
|
|
|
869
|
-
def _handle_namespaces(self, arguments: dict[str, Any]) ->
|
|
807
|
+
def _handle_namespaces(self, arguments: dict[str, Any]) -> NamespacesResponse:
|
|
870
808
|
"""Handle namespaces tool call."""
|
|
871
809
|
namespaces_result = self._utility_service.namespaces(
|
|
872
810
|
include_stats=arguments.get("include_stats", True),
|
|
@@ -889,7 +827,7 @@ class SpatialMemoryServer:
|
|
|
889
827
|
"total_memories": namespaces_result.total_memories,
|
|
890
828
|
}
|
|
891
829
|
|
|
892
|
-
def _handle_delete_namespace(self, arguments: dict[str, Any]) ->
|
|
830
|
+
def _handle_delete_namespace(self, arguments: dict[str, Any]) -> DeleteNamespaceResponse:
|
|
893
831
|
"""Handle delete_namespace tool call."""
|
|
894
832
|
delete_result = self._utility_service.delete_namespace(
|
|
895
833
|
namespace=arguments["namespace"],
|
|
@@ -904,7 +842,7 @@ class SpatialMemoryServer:
|
|
|
904
842
|
"dry_run": delete_result.dry_run,
|
|
905
843
|
}
|
|
906
844
|
|
|
907
|
-
def _handle_rename_namespace(self, arguments: dict[str, Any]) ->
|
|
845
|
+
def _handle_rename_namespace(self, arguments: dict[str, Any]) -> RenameNamespaceResponse:
|
|
908
846
|
"""Handle rename_namespace tool call."""
|
|
909
847
|
rename_result = self._utility_service.rename_namespace(
|
|
910
848
|
old_namespace=arguments["old_namespace"],
|
|
@@ -918,7 +856,7 @@ class SpatialMemoryServer:
|
|
|
918
856
|
"message": rename_result.message,
|
|
919
857
|
}
|
|
920
858
|
|
|
921
|
-
def _handle_export_memories(self, arguments: dict[str, Any]) ->
|
|
859
|
+
def _handle_export_memories(self, arguments: dict[str, Any]) -> ExportResponse:
|
|
922
860
|
"""Handle export_memories tool call."""
|
|
923
861
|
export_result = self._export_import_service.export_memories(
|
|
924
862
|
output_path=arguments["output_path"],
|
|
@@ -937,7 +875,7 @@ class SpatialMemoryServer:
|
|
|
937
875
|
"compression": export_result.compression,
|
|
938
876
|
}
|
|
939
877
|
|
|
940
|
-
def _handle_import_memories(self, arguments: dict[str, Any]) ->
|
|
878
|
+
def _handle_import_memories(self, arguments: dict[str, Any]) -> ImportResponse:
|
|
941
879
|
"""Handle import_memories tool call."""
|
|
942
880
|
dry_run = arguments.get("dry_run", True)
|
|
943
881
|
import_result = self._export_import_service.import_memories(
|
|
@@ -979,7 +917,7 @@ class SpatialMemoryServer:
|
|
|
979
917
|
] if import_result.imported_memories else [],
|
|
980
918
|
}
|
|
981
919
|
|
|
982
|
-
def _handle_hybrid_recall(self, arguments: dict[str, Any]) ->
|
|
920
|
+
def _handle_hybrid_recall(self, arguments: dict[str, Any]) -> HybridRecallResponse:
|
|
983
921
|
"""Handle hybrid_recall tool call."""
|
|
984
922
|
hybrid_result = self._utility_service.hybrid_recall(
|
|
985
923
|
query=arguments["query"],
|
|
@@ -1016,7 +954,7 @@ class SpatialMemoryServer:
|
|
|
1016
954
|
# Tool Routing
|
|
1017
955
|
# =========================================================================
|
|
1018
956
|
|
|
1019
|
-
def _handle_tool(self, name: str, arguments: dict[str, Any]) ->
|
|
957
|
+
def _handle_tool(self, name: str, arguments: dict[str, Any]) -> HandlerResponse:
|
|
1020
958
|
"""Route tool call to appropriate handler.
|
|
1021
959
|
|
|
1022
960
|
Args:
|
|
@@ -1024,7 +962,7 @@ class SpatialMemoryServer:
|
|
|
1024
962
|
arguments: Tool arguments.
|
|
1025
963
|
|
|
1026
964
|
Returns:
|
|
1027
|
-
Tool result as dictionary.
|
|
965
|
+
Tool result as typed dictionary.
|
|
1028
966
|
|
|
1029
967
|
Raises:
|
|
1030
968
|
ValidationError: If tool name is unknown.
|
|
@@ -1033,7 +971,7 @@ class SpatialMemoryServer:
|
|
|
1033
971
|
with record_request(name, "success"):
|
|
1034
972
|
return self._handle_tool_impl(name, arguments)
|
|
1035
973
|
|
|
1036
|
-
def _handle_tool_impl(self, name: str, arguments: dict[str, Any]) ->
|
|
974
|
+
def _handle_tool_impl(self, name: str, arguments: dict[str, Any]) -> HandlerResponse:
|
|
1037
975
|
"""Implementation of tool routing using dispatch pattern.
|
|
1038
976
|
|
|
1039
977
|
Args:
|
|
@@ -1116,6 +1054,10 @@ Then use `extract` to automatically capture important information.
|
|
|
1116
1054
|
|
|
1117
1055
|
def close(self) -> None:
|
|
1118
1056
|
"""Clean up resources."""
|
|
1057
|
+
# Shutdown the thread pool executor
|
|
1058
|
+
if hasattr(self, "_executor"):
|
|
1059
|
+
self._executor.shutdown(wait=False)
|
|
1060
|
+
|
|
1119
1061
|
if self._db is not None:
|
|
1120
1062
|
self._db.close()
|
|
1121
1063
|
|