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.
|
|
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
|
@@ -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.
|
|
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
|
-
|
|
426
|
+
loop = asyncio.get_event_loop()
|
|
419
427
|
t0 = _time.monotonic()
|
|
420
|
-
response =
|
|
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]:
|