adi-super-memory 0.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.
@@ -0,0 +1 @@
1
+ from .core import SuperMemory\nfrom .policy import MemoryPolicy\n
@@ -0,0 +1 @@
1
+ from .langchain import LangChainMemoryAdapter\n
@@ -0,0 +1,9 @@
1
+ class LangChainMemoryAdapter:
2
+ def __init__(self, super_memory, key: str = "history"):
3
+ self.mem=super_memory; self.key=key
4
+ @property
5
+ def memory_variables(self): return [self.key]
6
+ def load_memory_variables(self, inputs): return {self.key: self.mem.stm.messages()}
7
+ def save_context(self, inputs, outputs):
8
+ self.mem.stm.add("user", str(inputs))
9
+ self.mem.stm.add("assistant", str(outputs))
@@ -0,0 +1,26 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, Iterable, List, Optional, Tuple
3
+ import threading
4
+ from .types import MemoryItem
5
+
6
+ class Backend(ABC):
7
+ @abstractmethod
8
+ def upsert(self, item: MemoryItem, embedding: List[float]) -> None: ...
9
+ @abstractmethod
10
+ def iter_items(self, tenant: str, kinds: Optional[List[str]] = None) -> Iterable[Tuple[MemoryItem, List[float]]]: ...
11
+
12
+ class InMemoryBackend(Backend):
13
+ def __init__(self):
14
+ self._lock = threading.RLock()
15
+ self._store: Dict[str, Tuple[MemoryItem, List[float]]] = {}
16
+
17
+ def upsert(self, item: MemoryItem, embedding: List[float]) -> None:
18
+ with self._lock:
19
+ self._store[item.id] = (item, embedding)
20
+
21
+ def iter_items(self, tenant: str, kinds: Optional[List[str]] = None):
22
+ with self._lock:
23
+ for item, emb in self._store.values():
24
+ if item.tenant != tenant: continue
25
+ if kinds and item.kind not in kinds: continue
26
+ yield item, emb
@@ -0,0 +1,80 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime, timezone, timedelta
3
+ from typing import Dict, List, Optional
4
+ import uuid, hashlib, json
5
+
6
+ from .stm import MessageSTM
7
+ from .policy import MemoryPolicy
8
+ from .embeddings import HashEmbeddingProvider, EmbeddingProvider
9
+ from .backends import Backend, InMemoryBackend
10
+ from .types import MemoryItem
11
+ from .retrieval import cosine, recency_weight
12
+ from .distillers import Distiller, DeterministicDistiller
13
+ from .safety.modes import EnterpriseSafetyMode, SafetyMode
14
+
15
+ @dataclass
16
+ class SuperMemory:
17
+ backend: Backend = InMemoryBackend()
18
+ policy: MemoryPolicy = MemoryPolicy.default()
19
+ embedder: EmbeddingProvider = HashEmbeddingProvider()
20
+ tenant: str = "default"
21
+ safety: SafetyMode = EnterpriseSafetyMode.default()
22
+
23
+ def __post_init__(self):
24
+ self.stm = MessageSTM()
25
+ self._distiller: Distiller = DeterministicDistiller()
26
+ self._audit_last = "0"*64
27
+ self._items: Dict[str, MemoryItem] = {}
28
+ self._emb: Dict[str, List[float]] = {}
29
+
30
+ def set_distiller(self, distiller: Distiller):
31
+ self._distiller = distiller
32
+
33
+ def _audit(self, action: str, item_id: str):
34
+ body = json.dumps({"t": self.tenant, "a": action, "id": item_id, "prev": self._audit_last}, sort_keys=True).encode("utf-8")
35
+ self._audit_last = hashlib.sha256(body).hexdigest()
36
+
37
+ def audit_last_hash(self) -> str:
38
+ return self._audit_last
39
+
40
+ def add_event(self, actor: str, subkind: str, text: str, tags: Optional[List[str]] = None, score: float = 0.0):
41
+ return self._add("event", actor, subkind.capitalize(), text, tags or [], score, {"subkind": subkind})
42
+
43
+ def add_knowledge(self, namespace: str, title: str, content: str, tags: Optional[List[str]] = None):
44
+ return self._add("knowledge", "system", title, content, tags or [], 0.0, {"namespace": namespace})
45
+
46
+ def add_super(self, title: str, text: str, tags: Optional[List[str]] = None, score: float = 0.0):
47
+ return self._add("super", "system", title, text, tags or [], score, {})
48
+
49
+ def _add(self, kind: str, actor: str, title: str, text: str, tags: List[str], score: float, md: Dict[str,str]):
50
+ now = datetime.now(timezone.utc)
51
+ title = self.safety.redaction(title)
52
+ text = self.safety.redaction(text)
53
+ item = MemoryItem(id=str(uuid.uuid4()), created_at=now, kind=kind, actor=actor, title=title, text=text, tags=tags, metadata=md, score=float(score), tenant=self.tenant)
54
+ emb = self.embedder.embed([title + "\n" + text + "\n" + " ".join(tags)])[0]
55
+ self.backend.upsert(item, emb)
56
+ self._audit(f"upsert:{kind}", item.id)
57
+ return item
58
+
59
+ def recall(self, query: str, top_k: int = 8, kinds: Optional[List[str]] = None):
60
+ kinds = kinds or ["event","knowledge","super"]
61
+ qemb = self.embedder.embed([query])[0]
62
+ scored=[]
63
+ for item, emb in self.backend.iter_items(self.tenant, kinds=kinds):
64
+ sim = cosine(qemb, emb)
65
+ rec = recency_weight(item.created_at, self.policy.recency_half_life_days)
66
+ scored.append((0.7*sim+0.2*rec+0.1*float(item.score), item))
67
+ scored.sort(key=lambda x: x[0], reverse=True)
68
+ return [i for _, i in scored[:top_k]]
69
+
70
+ def distill(self, window_days: int = 30):
71
+ cutoff = datetime.now(timezone.utc) - timedelta(days=window_days)
72
+ events=[]
73
+ for item, _ in self.backend.iter_items(self.tenant, kinds=["event"]):
74
+ if item.created_at >= cutoff and (item.score >= self.policy.distill_min_score or item.metadata.get("subkind") == "outcome"):
75
+ events.append(item)
76
+ distilled = self._distiller.distill(events)
77
+ created=[]
78
+ for d in distilled:
79
+ created.append(self.add_super(d.get("title","Wisdom"), d.get("text",""), [d.get("tag","wisdom")], float(d.get("avg_score",0) or 0.0)).id)
80
+ return {"source_events": len(events), "created_super_memories": len(created), "super_memory_ids": created}
@@ -0,0 +1,39 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, List
3
+ from .types import MemoryItem
4
+
5
+ class Distiller(ABC):
6
+ @abstractmethod
7
+ def distill(self, events: List[MemoryItem]) -> List[Dict[str,str]]: ...
8
+
9
+ class DeterministicDistiller(Distiller):
10
+ def distill(self, events: List[MemoryItem]) -> List[Dict[str,str]]:
11
+ by_tag = {}
12
+ for e in events:
13
+ for t in (e.tags or ["untagged"]):
14
+ by_tag.setdefault(t, []).append(e)
15
+ out=[]
16
+ for tag, items in by_tag.items():
17
+ avg = sum(float(x.score) for x in items)/max(len(items),1)
18
+ out.append({"title": f"Wisdom: {tag}", "text": f"avg_score={avg:.2f} over {len(items)} events", "tag": tag, "avg_score": f"{avg:.3f}", "examples": ""})
19
+ return out
20
+
21
+ class OpenAIChatDistiller(Distiller):
22
+ def __init__(self, model: str = "gpt-4.1-mini", api_key=None):
23
+ self.model=model; self.api_key=api_key
24
+ def distill(self, events: List[MemoryItem]) -> List[Dict[str,str]]:
25
+ try:
26
+ from openai import OpenAI
27
+ except Exception as e:
28
+ raise RuntimeError("Install OpenAI extras: pip install 'adi-super-memory[llm]'") from e
29
+ client = OpenAI(api_key=self.api_key)
30
+ lines=[f"- score={e.score:.2f} tags={','.join(e.tags)}: {e.text[:200]}" for e in events[:80]]
31
+ prompt="Return JSON array of heuristics (title,text,tag,avg_score,examples).\nEVENTS:\n" + "\n".join(lines)
32
+ resp = client.chat.completions.create(model=self.model, messages=[{"role":"user","content":prompt}], temperature=0.2)
33
+ import json as _json
34
+ content = resp.choices[0].message.content or "[]"
35
+ try:
36
+ data = _json.loads(content)
37
+ return data if isinstance(data, list) else []
38
+ except Exception:
39
+ return [{"title":"Wisdom: distilled","text":content[:1500],"tag":"distilled","avg_score":"0.0","examples":""}]
@@ -0,0 +1,27 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List
3
+ import hashlib, math
4
+
5
+ class EmbeddingProvider(ABC):
6
+ @property
7
+ @abstractmethod
8
+ def dim(self) -> int: ...
9
+ @abstractmethod
10
+ def embed(self, texts: List[str]) -> List[List[float]]: ...
11
+
12
+ class HashEmbeddingProvider(EmbeddingProvider):
13
+ def __init__(self, dim: int = 256): self._dim = dim
14
+ @property
15
+ def dim(self) -> int: return self._dim
16
+ def _one(self, text: str) -> List[float]:
17
+ b = text.encode("utf-8", errors="ignore")
18
+ seed = hashlib.sha256(b).digest()
19
+ out = b""; cur = seed
20
+ while len(out) < self._dim:
21
+ cur = hashlib.sha256(cur + seed).digest()
22
+ out += cur
23
+ v = [((out[i]-128)/128.0) for i in range(self._dim)]
24
+ n = math.sqrt(sum(x*x for x in v)) or 1.0
25
+ return [x/n for x in v]
26
+ def embed(self, texts: List[str]) -> List[List[float]]:
27
+ return [self._one(t) for t in texts]
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass
2
+ from datetime import timedelta
3
+ from typing import Callable, Optional
4
+
5
+ RedactionFn = Callable[[str], str]
6
+
7
+ @dataclass(frozen=True)
8
+ class MemoryPolicy:
9
+ episodic_ttl: Optional[timedelta] = timedelta(days=30)
10
+ recency_half_life_days: float = 14.0
11
+ distill_min_score: float = 0.6
12
+ redaction: Optional[RedactionFn] = None
13
+
14
+ @staticmethod
15
+ def default() -> "MemoryPolicy":
16
+ return MemoryPolicy()
File without changes
@@ -0,0 +1,17 @@
1
+ from datetime import datetime, timezone
2
+ from typing import List
3
+ import math
4
+
5
+ def cosine(a: List[float], b: List[float]) -> float:
6
+ dot=na=nb=0.0
7
+ for x,y in zip(a,b):
8
+ dot += x*y; na += x*x; nb += y*y
9
+ d = math.sqrt(na)*math.sqrt(nb)
10
+ return (dot/d) if d else 0.0
11
+
12
+ def recency_weight(created_at: datetime, half_life_days: float) -> float:
13
+ now = datetime.now(timezone.utc)
14
+ if created_at.tzinfo is None:
15
+ created_at = created_at.replace(tzinfo=timezone.utc)
16
+ age_days = max((now-created_at).total_seconds(),0.0)/86400.0
17
+ return math.pow(0.5, age_days/half_life_days) if half_life_days>0 else 1.0
@@ -0,0 +1 @@
1
+ from .runtime import RealTimePolicy\n
@@ -0,0 +1,6 @@
1
+ from dataclasses import dataclass
2
+ @dataclass(frozen=True)
3
+ class RealTimePolicy:
4
+ max_plan_latency_ms: int = 50
5
+ max_action_latency_ms: int = 20
6
+ fail_closed: bool = True
@@ -0,0 +1,2 @@
1
+ from .modes import EnterpriseSafetyMode, GovernmentSafetyMode
2
+ from .redaction import default_redactor
@@ -0,0 +1,22 @@
1
+ from dataclasses import dataclass
2
+ from .redaction import default_redactor
3
+
4
+ @dataclass(frozen=True)
5
+ class SafetyMode:
6
+ name: str
7
+ redaction: callable
8
+ allow_external_network: bool
9
+ allow_cloud_models: bool
10
+ require_signed_audit: bool
11
+
12
+ @dataclass(frozen=True)
13
+ class EnterpriseSafetyMode(SafetyMode):
14
+ @staticmethod
15
+ def default():
16
+ return EnterpriseSafetyMode("enterprise", default_redactor, True, True, True)
17
+
18
+ @dataclass(frozen=True)
19
+ class GovernmentSafetyMode(SafetyMode):
20
+ @staticmethod
21
+ def default():
22
+ return GovernmentSafetyMode("government", default_redactor, False, False, True)
@@ -0,0 +1,7 @@
1
+ import re
2
+ _P=[(re.compile(r"\b\d{3}-\d{2}-\d{4}\b"),"[REDACTED_SSN]"),
3
+ (re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"),"[REDACTED_EMAIL]")]
4
+ def default_redactor(text: str) -> str:
5
+ out=text
6
+ for p,r in _P: out=p.sub(r,out)
7
+ return out
@@ -0,0 +1,21 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Dict, List
3
+ import threading
4
+
5
+ @dataclass
6
+ class MessageSTM:
7
+ _kv: Dict[str, Any] = field(default_factory=dict)
8
+ _messages: List[Dict[str, str]] = field(default_factory=list)
9
+ _lock: threading.RLock = field(default_factory=threading.RLock)
10
+
11
+ def set(self, key: str, value: Any) -> None:
12
+ with self._lock: self._kv[key] = value
13
+
14
+ def get(self, key: str, default: Any = None) -> Any:
15
+ with self._lock: return self._kv.get(key, default)
16
+
17
+ def add(self, role: str, content: str) -> None:
18
+ with self._lock: self._messages.append({"role": role, "content": content})
19
+
20
+ def messages(self, limit: int = 50) -> List[Dict[str, str]]:
21
+ with self._lock: return list(self._messages[-limit:])
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime
3
+ from typing import Dict, List
4
+
5
+ @dataclass(frozen=True)
6
+ class MemoryItem:
7
+ id: str
8
+ created_at: datetime
9
+ kind: str
10
+ actor: str
11
+ title: str
12
+ text: str
13
+ tags: List[str] = field(default_factory=list)
14
+ metadata: Dict[str,str] = field(default_factory=dict)
15
+ score: float = 0.0
16
+ tenant: str = "default"
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: adi-super-memory
3
+ Version: 0.3.0
4
+ Summary: ADI Super Memory: STM + LTM + distilled Super Memory with adapters and safety modes.
5
+ Author: Adi's American Soft LLC
6
+ License: Apache-2.0
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pydantic>=2.0; python_version >= "3.9"
10
+ Provides-Extra: server
11
+ Requires-Dist: fastapi>=0.110; extra == "server"
12
+ Requires-Dist: uvicorn>=0.23; extra == "server"
13
+ Provides-Extra: llm
14
+ Requires-Dist: openai>=1.0.0; extra == "llm"
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=8.0; extra == "dev"
17
+ Requires-Dist: build>=1.2; extra == "dev"
18
+ Requires-Dist: twine>=5.0; extra == "dev"
19
+
20
+ # adi-super-memory v0.3 (PyPI-ready)\n
@@ -0,0 +1,21 @@
1
+ adi_super_memory/__init__.py,sha256=lGC9AL8vmW7GzrEb83eeEXm8p0v2gBK7iDNZygRsMko,65
2
+ adi_super_memory/backends.py,sha256=iplPvVfx67IDp8RsXBHOi3vGAA6DmGIogd_ER9RUTMg,1010
3
+ adi_super_memory/core.py,sha256=iyOK3bLgzhLY3DvHy7Ypow0a9U7BRezRmXpl6_hpaZs,4003
4
+ adi_super_memory/distillers.py,sha256=sW_DAJjG9L_K47EwjVHita49SwtiBsqoBzvaQzBuxCo,1927
5
+ adi_super_memory/embeddings.py,sha256=96OQaldgeOY3UUFvMfseebTjaI2o8CXuSUioqvwZvJg,951
6
+ adi_super_memory/policy.py,sha256=NxVXwxvpftJKvpP0UNhuDH6LF_z2KN1EvGgqtM_Wnk4,449
7
+ adi_super_memory/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ adi_super_memory/retrieval.py,sha256=riy6Y2xML294fsZb21-vrpOPRClWugjPgRuWQRtpj2g,634
9
+ adi_super_memory/stm.py,sha256=5xNYtAddsDkoyPhTemaWSHJQdHWZMXNnNK6grtHZZcw,789
10
+ adi_super_memory/types.py,sha256=BH6iwnBpPZgbz4rV-YArlrajAzezWZpxJT6wJOEB5AE,398
11
+ adi_super_memory/adapters/__init__.py,sha256=ppoqcdxFAikKxRmIcxapkNwsent6pKbI9LethDSLbh8,47
12
+ adi_super_memory/adapters/langchain.py,sha256=Y4wlpv69zqfj6N_RD1UxcSCJjd15z3A1MDpSoH-MyNk,429
13
+ adi_super_memory/robotics/__init__.py,sha256=EOMHWilw1ZuQwmTN1yvX7fYpQbkW_ZOBSiMECC7souM,37
14
+ adi_super_memory/robotics/runtime.py,sha256=KY7PNre9Tw3EpQK2sqT942ZQw071Wakw-rZgRy0EdGM,179
15
+ adi_super_memory/safety/__init__.py,sha256=ILKFalih5cUX0FohFH412nUgBydloOBtyOWmfc5Wp4Q,102
16
+ adi_super_memory/safety/modes.py,sha256=uKa29ihC_d-WpW2d03coF3lq_c0HuUEexvYJG8vTXnk,626
17
+ adi_super_memory/safety/redaction.py,sha256=vtBrXkr4wohrfyytfipEyRfjGBIxDEdSPeneldzxLZI,267
18
+ adi_super_memory-0.3.0.dist-info/METADATA,sha256=IB_g23hxsiUSZvD5_BwAsoshOgJG6zWffxB5xGrlI8c,715
19
+ adi_super_memory-0.3.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
20
+ adi_super_memory-0.3.0.dist-info/top_level.txt,sha256=6n6xha3RX2TEyVBrdWp959fyizdkEULt677xSL4lZOw,17
21
+ adi_super_memory-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ adi_super_memory