synapse-memory-engine 1.3.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.
- synapse_memory/__init__.py +31 -0
- synapse_memory/api/fastapi_server.py +1334 -0
- synapse_memory/cli/synapse_cli.py +112 -0
- synapse_memory/client.py +127 -0
- synapse_memory/core/__init__.py +50 -0
- synapse_memory/core/auth.py +1273 -0
- synapse_memory/core/decay_engine.py +123 -0
- synapse_memory/core/deduplicator.py +98 -0
- synapse_memory/core/embedder.py +119 -0
- synapse_memory/core/embedding_provider.py +241 -0
- synapse_memory/core/encryption.py +226 -0
- synapse_memory/core/hybrid_search.py +199 -0
- synapse_memory/core/knapsack_packer.py +103 -0
- synapse_memory/core/memory_manager.py +233 -0
- synapse_memory/core/migrations.py +241 -0
- synapse_memory/core/observability.py +48 -0
- synapse_memory/core/pgvector_db.py +1276 -0
- synapse_memory/core/pruner.py +68 -0
- synapse_memory/core/rate_limiter.py +180 -0
- synapse_memory/core/sqlite_db.py +564 -0
- synapse_memory/examples/langchain_integration.py +12 -0
- synapse_memory/examples/llamaindex_integration.py +12 -0
- synapse_memory/integrations/__init__.py +9 -0
- synapse_memory/integrations/langchain.py +209 -0
- synapse_memory/integrations/llamaindex.py +295 -0
- synapse_memory/main.py +112 -0
- synapse_memory/py.typed +0 -0
- synapse_memory_engine-1.3.0.dist-info/METADATA +75 -0
- synapse_memory_engine-1.3.0.dist-info/RECORD +32 -0
- synapse_memory_engine-1.3.0.dist-info/WHEEL +5 -0
- synapse_memory_engine-1.3.0.dist-info/entry_points.txt +4 -0
- synapse_memory_engine-1.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Align python sys path to resolve absolute imports correctly
|
|
7
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
8
|
+
|
|
9
|
+
from synapse_memory.core.decay_engine import DecayEngine
|
|
10
|
+
from synapse_memory.core.embedder import SynapseEmbedder
|
|
11
|
+
from synapse_memory.core.hybrid_search import HybridSearch
|
|
12
|
+
from synapse_memory.core.knapsack_packer import KnapsackPacker
|
|
13
|
+
from synapse_memory.core.memory_manager import MemoryManager
|
|
14
|
+
from synapse_memory.core.sqlite_db import SQLiteMemoryStore
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
description="SynapseMemory: Sovereign Command-Line Cognitive Console & Debugger."
|
|
20
|
+
)
|
|
21
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
|
22
|
+
|
|
23
|
+
# Ingest subcommand
|
|
24
|
+
ingest_parser = subparsers.add_parser("ingest", help="Ingest a cognitive memory segment with semantic deduplication")
|
|
25
|
+
ingest_parser.add_argument("--content", required=True, type=str, help="Text content of the memory")
|
|
26
|
+
ingest_parser.add_argument("--category", type=str, default="interaction", help="Cognitive category")
|
|
27
|
+
ingest_parser.add_argument("--confidence", type=float, default=0.95, help="Confidence factor (0.0 to 1.0)")
|
|
28
|
+
|
|
29
|
+
# Query subcommand
|
|
30
|
+
query_parser = subparsers.add_parser("query", help="Query long-term memory with Knapsack context budget constraints")
|
|
31
|
+
query_parser.add_argument("--prompt", required=True, type=str, help="The search or context prompt")
|
|
32
|
+
query_parser.add_argument("--budget", type=int, default=1500, help="Maximum tokens allowed in context pack")
|
|
33
|
+
|
|
34
|
+
# List subcommand
|
|
35
|
+
subparsers.add_parser("list", help="List all indexed sqlite memories")
|
|
36
|
+
|
|
37
|
+
# Clear subcommand
|
|
38
|
+
subparsers.add_parser("clear", help="Clear the entire local SQLite memory table")
|
|
39
|
+
|
|
40
|
+
# Prune subcommand
|
|
41
|
+
prune_parser = subparsers.add_parser("prune", help="Manually force a temporal Ebbinghaus decay pruning sweep")
|
|
42
|
+
prune_parser.add_argument("--limit", type=int, default=10, help="Set database maximum record limit")
|
|
43
|
+
|
|
44
|
+
args = parser.parse_args()
|
|
45
|
+
|
|
46
|
+
# Initialize store, embedder and engines
|
|
47
|
+
store = SQLiteMemoryStore("synapse_memory.db")
|
|
48
|
+
embedder = SynapseEmbedder(provider="local")
|
|
49
|
+
manager = MemoryManager(store=store, embedder=embedder)
|
|
50
|
+
searcher = HybridSearch()
|
|
51
|
+
decay = DecayEngine()
|
|
52
|
+
packer = KnapsackPacker()
|
|
53
|
+
|
|
54
|
+
if args.command == "ingest":
|
|
55
|
+
mem_id, action, cost = manager.ingest_with_deduplication(
|
|
56
|
+
content=args.content,
|
|
57
|
+
category=args.category,
|
|
58
|
+
confidence=args.confidence
|
|
59
|
+
)
|
|
60
|
+
print("[+] Ingestion Finished:")
|
|
61
|
+
print(f" - Memory ID : {mem_id}")
|
|
62
|
+
print(f" - Event Type : {action}")
|
|
63
|
+
print(f" - Token Cost : {cost} tokens")
|
|
64
|
+
|
|
65
|
+
elif args.command == "query":
|
|
66
|
+
memories = store.get_all_memories()
|
|
67
|
+
if not memories:
|
|
68
|
+
print("[-] Store is empty. Ingest memories before querying.")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
candidates = searcher.fused_search(args.prompt, memories, top_k=25)
|
|
72
|
+
|
|
73
|
+
# Apply temporal decay calculations
|
|
74
|
+
decayed = []
|
|
75
|
+
for c in candidates:
|
|
76
|
+
current = decay.calculate_retention(
|
|
77
|
+
base_relevance=c["relevance_score"],
|
|
78
|
+
created_epoch=c["created_at"],
|
|
79
|
+
access_count=c["access_count"]
|
|
80
|
+
)
|
|
81
|
+
c_copy = c.copy()
|
|
82
|
+
c_copy["relevance_score"] = current
|
|
83
|
+
decayed.append(c_copy)
|
|
84
|
+
|
|
85
|
+
packed = packer.pack(decayed, args.budget)
|
|
86
|
+
|
|
87
|
+
print(f"\n[+] Retained {len(packed)} optimal context segments under a {args.budget}-token budget:")
|
|
88
|
+
for idx, m in enumerate(packed):
|
|
89
|
+
print(f" {idx + 1}. [{m['id']}] (Cost: {m['token_cost']} tokens, Relevance: {m['relevance_score']:.3f}):")
|
|
90
|
+
print(f" \"{m['content']}\"")
|
|
91
|
+
|
|
92
|
+
elif args.command == "list":
|
|
93
|
+
memories = store.get_all_memories()
|
|
94
|
+
print(f"[+] Active Database Records ({len(memories)} items):")
|
|
95
|
+
for m in memories:
|
|
96
|
+
print(f" - [{m['id']}] Category: {m['category'].upper()} | Cost: {m['token_cost']} | Content: \"{m['content'][:60]}...\"")
|
|
97
|
+
|
|
98
|
+
elif args.command == "clear":
|
|
99
|
+
os.remove("synapse_memory.db") if os.path.exists("synapse_memory.db") else None
|
|
100
|
+
print("[+] Erased local sqlite database table cleanly.")
|
|
101
|
+
|
|
102
|
+
elif args.command == "prune":
|
|
103
|
+
manager.max_record_limit = args.limit
|
|
104
|
+
evicted = manager.auto_prune_store()
|
|
105
|
+
print(f"[+] Manual Prune Complete: Evicted {evicted} memories. Active store contains max {args.limit} items.")
|
|
106
|
+
|
|
107
|
+
else:
|
|
108
|
+
parser.print_help()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
main()
|
synapse_memory/client.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Canonical SynapseMemory Python client (Phase 6.1).
|
|
2
|
+
|
|
3
|
+
Small, typed surface over the REST gateway — the supported path for
|
|
4
|
+
external applications. Handles auth headers, timeouts, retries on
|
|
5
|
+
retryable (429/5xx) failures, and the canonical {success, data} envelope.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import time
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
import requests
|
|
14
|
+
|
|
15
|
+
__all__ = ["SynapseClient", "SynapseError", "CreateResult", "QueryResult"]
|
|
16
|
+
|
|
17
|
+
SDK_VERSION = "1.2.0"
|
|
18
|
+
API_COMPAT_VERSION = "v1"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SynapseError(Exception):
|
|
22
|
+
def __init__(self, message: str, *, status: Optional[int] = None, retryable: bool = False):
|
|
23
|
+
super().__init__(message)
|
|
24
|
+
self.status = status
|
|
25
|
+
self.retryable = retryable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class CreateResult:
|
|
30
|
+
id: str
|
|
31
|
+
status: str
|
|
32
|
+
token_cost: int
|
|
33
|
+
tenant_id: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class QueryResult:
|
|
38
|
+
fused_context: str
|
|
39
|
+
injected_nodes_count: int
|
|
40
|
+
injected_nodes: List[Dict[str, Any]]
|
|
41
|
+
storage_backend: str
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SynapseClient:
|
|
45
|
+
"""Synchronous client; also usable as a context manager."""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
base_url: str = "http://127.0.0.1:8008",
|
|
50
|
+
api_key: Optional[str] = None,
|
|
51
|
+
tenant_id: str = "default",
|
|
52
|
+
timeout: float = 10.0,
|
|
53
|
+
max_retries: int = 3,
|
|
54
|
+
):
|
|
55
|
+
self.base_url = base_url.rstrip("/")
|
|
56
|
+
self.api_key = api_key
|
|
57
|
+
self.tenant_id = tenant_id
|
|
58
|
+
self.timeout = timeout
|
|
59
|
+
self.max_retries = max_retries
|
|
60
|
+
self._session = requests.Session()
|
|
61
|
+
|
|
62
|
+
def __enter__(self) -> "SynapseClient":
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def __exit__(self, *exc: Any) -> None:
|
|
66
|
+
self.close()
|
|
67
|
+
|
|
68
|
+
def close(self) -> None:
|
|
69
|
+
self._session.close()
|
|
70
|
+
|
|
71
|
+
def _headers(self) -> Dict[str, str]:
|
|
72
|
+
headers = {"Content-Type": "application/json", "X-Tenant-ID": self.tenant_id}
|
|
73
|
+
if self.api_key:
|
|
74
|
+
headers["X-Synapse-API-Key"] = self.api_key
|
|
75
|
+
return headers
|
|
76
|
+
|
|
77
|
+
def _request(self, method: str, path: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
78
|
+
url = f"{self.base_url}{path}"
|
|
79
|
+
last_error: Optional[SynapseError] = None
|
|
80
|
+
for attempt in range(self.max_retries + 1):
|
|
81
|
+
try:
|
|
82
|
+
resp = self._session.request(method, url, json=payload, headers=self._headers(), timeout=self.timeout)
|
|
83
|
+
except requests.RequestException as e:
|
|
84
|
+
last_error = SynapseError(f"Connection failed: {e}", retryable=True)
|
|
85
|
+
else:
|
|
86
|
+
if resp.status_code in (429, 502, 503, 504):
|
|
87
|
+
last_error = SynapseError(f"Retryable {resp.status_code}: {resp.text[:200]}", status=resp.status_code, retryable=True)
|
|
88
|
+
elif not resp.ok:
|
|
89
|
+
raise SynapseError(f"Request failed {resp.status_code}: {resp.text[:500]}", status=resp.status_code)
|
|
90
|
+
else:
|
|
91
|
+
body = resp.json()
|
|
92
|
+
if isinstance(body, dict) and "success" in body and "data" in body:
|
|
93
|
+
if not body["success"]:
|
|
94
|
+
raise SynapseError(f"API error: {body.get('error')}", status=resp.status_code)
|
|
95
|
+
return body["data"]
|
|
96
|
+
return body
|
|
97
|
+
if attempt < self.max_retries:
|
|
98
|
+
time.sleep(min(2.0, 0.25 * (2 ** attempt)))
|
|
99
|
+
raise last_error or SynapseError("Request failed")
|
|
100
|
+
|
|
101
|
+
def create_memory(self, content: str, category: str = "interaction", confidence: float = 0.95) -> CreateResult:
|
|
102
|
+
data = self._request("POST", "/ingest", {"content": content, "category": category, "confidence": confidence})
|
|
103
|
+
return CreateResult(id=data["id"], status=data["status"], token_cost=data["token_cost"], tenant_id=data["tenant_id"])
|
|
104
|
+
|
|
105
|
+
def retrieve(self, query: str, max_tokens: int = 1500) -> QueryResult:
|
|
106
|
+
data = self._request("POST", "/query", {"prompt": query, "maxTokens": max_tokens})
|
|
107
|
+
return QueryResult(
|
|
108
|
+
fused_context=data.get("fused_context", ""),
|
|
109
|
+
injected_nodes_count=data.get("injected_nodes_count", 0),
|
|
110
|
+
injected_nodes=data.get("injected_nodes", []),
|
|
111
|
+
storage_backend=data.get("active_storage_backend", "unknown"),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def list_memories(self, limit: int = 50, offset: int = 0) -> Dict[str, Any]:
|
|
115
|
+
return self._request("GET", f"/api/memories/list?limit={limit}&offset={offset}")
|
|
116
|
+
|
|
117
|
+
def delete_memory(self, memory_id: str) -> Dict[str, Any]:
|
|
118
|
+
return self._request("DELETE", f"/api/memories/{memory_id}")
|
|
119
|
+
|
|
120
|
+
def feedback(self, memory_id: str, feedback_type: str) -> Dict[str, Any]:
|
|
121
|
+
return self._request("POST", "/feedback", {"memory_id": memory_id, "feedback_type": feedback_type})
|
|
122
|
+
|
|
123
|
+
def health(self) -> Dict[str, Any]:
|
|
124
|
+
return self._request("GET", "/health")
|
|
125
|
+
|
|
126
|
+
def readiness(self) -> Dict[str, Any]:
|
|
127
|
+
return self._request("GET", "/readiness")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Expose core components for internal and public access
|
|
2
|
+
from synapse_memory.core.sqlite_db import SQLiteMemoryStore
|
|
3
|
+
from synapse_memory.core.embedder import SynapseEmbedder
|
|
4
|
+
from synapse_memory.core.memory_manager import MemoryManager
|
|
5
|
+
from synapse_memory.core.hybrid_search import HybridSearch
|
|
6
|
+
from synapse_memory.core.knapsack_packer import KnapsackPacker, MemoryNodeResult
|
|
7
|
+
from synapse_memory.core.decay_engine import DecayEngine
|
|
8
|
+
from synapse_memory.core.deduplicator import Deduplicator, Action, DeduplicationResult
|
|
9
|
+
from synapse_memory.core.pruner import (
|
|
10
|
+
PruningEngine,
|
|
11
|
+
CapacityPruningPolicy,
|
|
12
|
+
TTLPruningPolicy,
|
|
13
|
+
ConfidencePruningPolicy,
|
|
14
|
+
)
|
|
15
|
+
from synapse_memory.core.embedding_provider import (
|
|
16
|
+
EmbeddingManager,
|
|
17
|
+
LocalEmbeddingProvider,
|
|
18
|
+
GeminiEmbeddingProvider,
|
|
19
|
+
OpenAIEmbeddingProvider,
|
|
20
|
+
)
|
|
21
|
+
from synapse_memory.core.encryption import EncryptedMemoryStore, FernetKeyManager
|
|
22
|
+
from synapse_memory.core.pgvector_db import PGVectorMemoryStore
|
|
23
|
+
from synapse_memory.core.auth import SecurityManager, APIKeyRecord, AuthAuditLog
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"SQLiteMemoryStore",
|
|
27
|
+
"PGVectorMemoryStore",
|
|
28
|
+
"SecurityManager",
|
|
29
|
+
"APIKeyRecord",
|
|
30
|
+
"AuthAuditLog",
|
|
31
|
+
"SynapseEmbedder",
|
|
32
|
+
"MemoryManager",
|
|
33
|
+
"HybridSearch",
|
|
34
|
+
"KnapsackPacker",
|
|
35
|
+
"MemoryNodeResult",
|
|
36
|
+
"DecayEngine",
|
|
37
|
+
"Deduplicator",
|
|
38
|
+
"Action",
|
|
39
|
+
"DeduplicationResult",
|
|
40
|
+
"PruningEngine",
|
|
41
|
+
"CapacityPruningPolicy",
|
|
42
|
+
"TTLPruningPolicy",
|
|
43
|
+
"ConfidencePruningPolicy",
|
|
44
|
+
"EmbeddingManager",
|
|
45
|
+
"LocalEmbeddingProvider",
|
|
46
|
+
"GeminiEmbeddingProvider",
|
|
47
|
+
"OpenAIEmbeddingProvider",
|
|
48
|
+
"EncryptedMemoryStore",
|
|
49
|
+
"FernetKeyManager",
|
|
50
|
+
]
|