superlocalmemory 3.4.62 → 3.4.63

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to SuperLocalMemory V3 will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.4.63] - 2026-05-31 — Dashboard search: fix async blocking + fast mode
9
+
10
+ Fixes "signal is aborted without reason" in dashboard search (second root cause,
11
+ different from v3.4.61's WorkerPool fix).
12
+
13
+ ### Root Cause
14
+ `engine.recall()` is a synchronous blocking Python call (~2-10s). Calling it
15
+ directly inside an `async` FastAPI route blocks the ASGI event loop for the
16
+ full duration. Chrome detects a stalled HTTP connection (no response headers
17
+ being sent) and fires `controller.abort()` with no reason — producing the
18
+ "signal is aborted without reason" browser error, regardless of fetch timeout.
19
+
20
+ ### Fix
21
+ 1. `await loop.run_in_executor(None, lambda: engine.recall(...))` — offloads
22
+ the blocking call to a thread pool. Event loop stays alive to send HTTP
23
+ keepalive frames, preventing Chrome from aborting the connection.
24
+ 2. `fast=True` — skips spreading_activation + Hopfield channels, reducing
25
+ recall from 9.5s to <2s. These channels add precision for MCP/session_init
26
+ but are unnecessary for dashboard search results.
27
+
28
+ ### Result
29
+ Dashboard search: 919ms cold, 1.7s warm. Zero browser aborts.
30
+ Works immediately after `slm restart` — no wait needed.
31
+
32
+ ### Changed
33
+ - `server/routes/memories.py`: `search_memories` uses `run_in_executor` + `fast=True`
34
+
35
+ ---
36
+
8
37
  ## [3.4.62] - 2026-05-31 — Recall engine pre-warm on startup
9
38
 
10
39
  Adds a `recall-warmup` background thread that fires one full 6-channel recall
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superlocalmemory",
3
- "version": "3.4.62",
3
+ "version": "3.4.63",
4
4
  "description": "Information-geometric agent memory with mathematical guarantees. 4-channel retrieval, Fisher-Rao similarity, zero-LLM mode, EU AI Act compliant. Works with Claude, Cursor, Windsurf, and 17+ AI tools.",
5
5
  "keywords": [
6
6
  "ai-memory",
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "3.4.62"
3
+ version = "3.4.63"
4
4
  description = "Information-geometric agent memory with mathematical guarantees"
5
5
  readme = "README.md"
6
6
  license = {text = "AGPL-3.0-or-later"}
@@ -28,7 +28,7 @@ if "OMP_NUM_THREADS" not in os.environ:
28
28
  os.environ["OMP_NUM_THREADS"] = "2"
29
29
  # ---------------------------------------------------------------------------
30
30
 
31
- __version__ = "3.4.62"
31
+ __version__ = "3.4.63"
32
32
 
33
33
  _REQUIRED_VERSIONS = {
34
34
  "sentence_transformers": "5.3.0",
@@ -412,12 +412,23 @@ async def search_memories(request: Request, body: SearchRequest):
412
412
  from superlocalmemory.core.recall_gate import begin_recall, end_recall
413
413
  begin_recall()
414
414
  try:
415
- # Use the daemon engine directly — already loaded, shares warm cache
415
+ # Use the daemon engine directly — already loaded, shares warm cache.
416
+ # v3.4.63: engine.recall() is synchronous/blocking (~2-10s). Calling it
417
+ # directly in an async route blocks the ASGI event loop — Chrome detects
418
+ # a stalled connection and aborts with "signal is aborted without reason"
419
+ # before the response arrives. Fix: run in a thread-pool executor so the
420
+ # event loop stays alive to send keepalive frames. Also fast=True skips
421
+ # spreading_activation + Hopfield (saves ~7s on cold graph traversal).
422
+ import asyncio
423
+ import time as _time
416
424
  engine = _get_engine(request)
417
425
  if engine is not None:
418
- import time as _time
426
+ loop = asyncio.get_event_loop()
419
427
  t0 = _time.monotonic()
420
- response = engine.recall(body.query, limit=body.limit)
428
+ response = await loop.run_in_executor(
429
+ None,
430
+ lambda: engine.recall(body.query, limit=body.limit, fast=True),
431
+ )
421
432
  elapsed_ms = round((_time.monotonic() - t0) * 1000, 1)
422
433
  results = []
423
434
  for r in response.results[: body.limit]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: superlocalmemory
3
- Version: 3.4.62
3
+ Version: 3.4.63
4
4
  Summary: Information-geometric agent memory with mathematical guarantees
5
5
  Author-email: Varun Pratap Bhardwaj <admin@superlocalmemory.com>
6
6
  License: AGPL-3.0-or-later