superlocalmemory 3.4.60 → 3.4.61
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.61] - 2026-05-31 — Dashboard search fix (in-process engine)
|
|
9
|
+
|
|
10
|
+
**Fixes dashboard search always timing out** with "signal is aborted without reason".
|
|
11
|
+
|
|
12
|
+
### Root Cause
|
|
13
|
+
`POST /api/search` (used by the SLM dashboard memories pane) called
|
|
14
|
+
`WorkerPool.shared()` — the legacy subprocess-based worker pool from v3.4.32
|
|
15
|
+
(pre-unified-daemon). This spawned a fresh Python subprocess and loaded the full
|
|
16
|
+
SLM engine cold on **every single search request**, taking 15–20s. The browser's
|
|
17
|
+
AbortController always fired before the response arrived.
|
|
18
|
+
|
|
19
|
+
The `/recall` HTTP endpoint (used by MCP `session_init`) uses the daemon engine
|
|
20
|
+
directly and is warm at <1s. The dashboard used a completely different code path.
|
|
21
|
+
|
|
22
|
+
### Fix
|
|
23
|
+
`search_memories` now calls `_get_engine(request).recall()` — the daemon's own
|
|
24
|
+
in-process engine that is already loaded and shares the warm SQLite page cache.
|
|
25
|
+
Falls back to direct LIKE text search if engine is unavailable during startup.
|
|
26
|
+
|
|
27
|
+
### Result
|
|
28
|
+
Dashboard search: **<1s warm** (was >15s → browser abort).
|
|
29
|
+
GitHub sync failure shown in sidebar is a separate backup connectivity issue,
|
|
30
|
+
unrelated to search.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- `server/routes/memories.py`: `search_memories` uses daemon engine, not WorkerPool
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
8
37
|
## [3.4.60] - 2026-05-31 — Daemon OpenMP Crash Hotfix
|
|
9
38
|
|
|
10
39
|
**Hotfix for v3.4.59.** Forces `OMP_NUM_THREADS=1` and `KMP_DUPLICATE_LIB_OK=TRUE`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superlocalmemory",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.61",
|
|
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.61"
|
|
32
32
|
|
|
33
33
|
_REQUIRED_VERSIONS = {
|
|
34
34
|
"sentence_transformers": "5.3.0",
|
|
@@ -398,27 +398,47 @@ async def get_graph(
|
|
|
398
398
|
|
|
399
399
|
@router.post("/api/search")
|
|
400
400
|
async def search_memories(request: Request, body: SearchRequest):
|
|
401
|
-
"""Semantic search
|
|
401
|
+
"""Semantic search using the daemon's in-process engine.
|
|
402
402
|
|
|
403
|
-
v3.4.
|
|
403
|
+
v3.4.61: Replaced WorkerPool.shared() (subprocess-based, cold-starts on
|
|
404
|
+
every request, always >15s) with the daemon's own engine that is already
|
|
405
|
+
loaded and warm. WorkerPool.shared() was legacy from v3.4.32 before the
|
|
406
|
+
unified daemon architecture. Using the daemon engine matches what the /recall
|
|
407
|
+
HTTP endpoint does and shares its warm SQLite page cache, bringing dashboard
|
|
408
|
+
search from >15s timeout to <1s warm.
|
|
409
|
+
|
|
410
|
+
Falls back to direct DB LIKE search if engine is unavailable.
|
|
404
411
|
"""
|
|
405
412
|
from superlocalmemory.core.recall_gate import begin_recall, end_recall
|
|
406
413
|
begin_recall()
|
|
407
414
|
try:
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
415
|
+
# Use the daemon engine directly — already loaded, shares warm cache
|
|
416
|
+
engine = _get_engine(request)
|
|
417
|
+
if engine is not None:
|
|
418
|
+
import time as _time
|
|
419
|
+
t0 = _time.monotonic()
|
|
420
|
+
response = engine.recall(body.query, limit=body.limit)
|
|
421
|
+
elapsed_ms = round((_time.monotonic() - t0) * 1000, 1)
|
|
422
|
+
results = []
|
|
423
|
+
for r in response.results[: body.limit]:
|
|
424
|
+
results.append({
|
|
425
|
+
"fact_id": r.fact.fact_id,
|
|
426
|
+
"memory_id": getattr(r.fact, "memory_id", ""),
|
|
427
|
+
"content": r.fact.content[:300],
|
|
428
|
+
"score": round(r.score, 4),
|
|
429
|
+
"confidence": round(getattr(r, "confidence", 0.0), 4),
|
|
430
|
+
"channel_scores": getattr(r, "channel_scores", {}),
|
|
431
|
+
"created_at": getattr(r.fact, "created_at", ""),
|
|
432
|
+
})
|
|
413
433
|
return {
|
|
414
434
|
"query": body.query,
|
|
415
|
-
"results":
|
|
416
|
-
"total":
|
|
417
|
-
"query_type":
|
|
418
|
-
"retrieval_time_ms":
|
|
435
|
+
"results": results,
|
|
436
|
+
"total": len(results),
|
|
437
|
+
"query_type": getattr(response, "query_type", "semantic"),
|
|
438
|
+
"retrieval_time_ms": elapsed_ms,
|
|
419
439
|
}
|
|
420
440
|
|
|
421
|
-
# Fallback: direct DB text search (
|
|
441
|
+
# Fallback: direct DB text search (engine not yet initialised)
|
|
422
442
|
conn = get_db_connection()
|
|
423
443
|
conn.row_factory = dict_factory
|
|
424
444
|
cursor = conn.cursor()
|