agentmemo-py 1.0.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.
agentmemo/__init__.py ADDED
@@ -0,0 +1,132 @@
1
+ """AgentMemo Python SDK — persistent memory for AI agents.
2
+
3
+ Sync client uses only the standard library (urllib). Optional async methods use
4
+ httpx if installed.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import urllib.request
10
+ import urllib.error
11
+ import urllib.parse
12
+ from typing import Any, Optional
13
+
14
+ __version__ = "1.0.0"
15
+ __all__ = ["MemoryClient", "AgentMemoError"]
16
+
17
+ DEFAULT_BASE_URL = "https://agentmemo.dev"
18
+
19
+
20
+ class AgentMemoError(Exception):
21
+ def __init__(self, message: str, status: int = 0, code: Optional[str] = None, body: Any = None):
22
+ super().__init__(message)
23
+ self.status = status
24
+ self.code = code
25
+ self.body = body
26
+
27
+
28
+ class MemoryClient:
29
+ def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
30
+ if not api_key:
31
+ raise AgentMemoError("api_key is required", code="no_api_key")
32
+ self.api_key = api_key
33
+ self.base_url = base_url.rstrip("/")
34
+
35
+ # ---- internal -------------------------------------------------------
36
+ def _req(self, method: str, path: str, body: Optional[dict] = None) -> dict:
37
+ url = self.base_url + path
38
+ data = json.dumps(body).encode() if body is not None else None
39
+ headers = {"Authorization": f"Bearer {self.api_key}"}
40
+ if data is not None:
41
+ headers["Content-Type"] = "application/json"
42
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
43
+ try:
44
+ with urllib.request.urlopen(req) as resp:
45
+ return json.loads(resp.read().decode() or "{}")
46
+ except urllib.error.HTTPError as e:
47
+ payload = {}
48
+ try:
49
+ payload = json.loads(e.read().decode() or "{}")
50
+ except Exception:
51
+ pass
52
+ msg = payload.get("error") if isinstance(payload, dict) else str(e)
53
+ raise AgentMemoError(msg or "request failed", e.code, payload.get("code"), payload)
54
+
55
+ @staticmethod
56
+ def _qs(params: dict) -> str:
57
+ clean = {k: v for k, v in params.items() if v is not None}
58
+ return urllib.parse.urlencode(clean)
59
+
60
+ # ---- API ------------------------------------------------------------
61
+ def store(self, user_id: str, agent_id: str, content: str, metadata: dict = None,
62
+ ttl_seconds: int = None, tags: list = None, namespace: str = "default",
63
+ importance: int = 5, outcome: str = "unknown", detect_conflicts: bool = False) -> dict:
64
+ return self._req("POST", "/memory/store", {
65
+ "user_id": user_id, "agent_id": agent_id, "content": content,
66
+ "metadata": metadata, "ttl_seconds": ttl_seconds, "tags": tags,
67
+ "namespace": namespace, "importance": importance, "outcome": outcome,
68
+ "detect_conflicts": detect_conflicts,
69
+ })
70
+
71
+ def search(self, user_id: str, query: str, agent_id: str = None, limit: int = 10,
72
+ namespace: str = None, tags: list = None, min_importance: int = None) -> dict:
73
+ qs = self._qs({
74
+ "user_id": user_id, "q": query, "agent_id": agent_id, "limit": limit,
75
+ "namespace": namespace, "tags": ",".join(tags) if tags else None,
76
+ "min_importance": min_importance,
77
+ })
78
+ return self._req("GET", "/memory/retrieve?" + qs)
79
+
80
+ def delete(self, id: str = None, user_id: str = None, agent_id: str = None) -> dict:
81
+ return self._req("DELETE", "/memory/forget?" + self._qs({"id": id, "user_id": user_id, "agent_id": agent_id}))
82
+
83
+ def context(self, user_id: str, agent_id: str = None, max_tokens: int = 2000, format: str = "raw") -> dict:
84
+ return self._req("GET", "/memory/context?" + self._qs({
85
+ "user_id": user_id, "agent_id": agent_id, "max_tokens": max_tokens, "format": format}))
86
+
87
+ def feedback(self, memory_id: str, outcome: str, confidence: float = 1.0) -> dict:
88
+ return self._req("POST", "/memory/feedback", {"memory_id": memory_id, "outcome": outcome, "confidence": confidence})
89
+
90
+ def batch(self, memories: list) -> dict:
91
+ return self._req("POST", "/memory/batch", {"memories": memories})
92
+
93
+ def stats(self, user_id: str = None) -> dict:
94
+ return self._req("GET", "/memory/stats?" + self._qs({"user_id": user_id}))
95
+
96
+ def usage(self) -> dict:
97
+ return self._req("GET", "/usage")
98
+
99
+ # ---- async (requires httpx) ----------------------------------------
100
+ async def async_store(self, **kwargs) -> dict:
101
+ return await self._async_req("POST", "/memory/store", self._store_body(**kwargs))
102
+
103
+ async def async_search(self, user_id: str, query: str, **kwargs) -> dict:
104
+ qs = self._qs({"user_id": user_id, "q": query, **kwargs})
105
+ return await self._async_req("GET", "/memory/retrieve?" + qs)
106
+
107
+ def _store_body(self, user_id, agent_id, content, **kw):
108
+ return {"user_id": user_id, "agent_id": agent_id, "content": content, **kw}
109
+
110
+ async def _async_req(self, method: str, path: str, body: Optional[dict] = None) -> dict:
111
+ try:
112
+ import httpx # optional dependency
113
+ except ImportError as e: # pragma: no cover
114
+ raise AgentMemoError("async methods require 'httpx' (pip install httpx)") from e
115
+ headers = {"Authorization": f"Bearer {self.api_key}"}
116
+ async with httpx.AsyncClient(base_url=self.base_url) as client:
117
+ resp = await client.request(method, path, json=body, headers=headers)
118
+ data = resp.json() if resp.content else {}
119
+ if resp.status_code >= 400:
120
+ raise AgentMemoError(data.get("error", "request failed"), resp.status_code, data.get("code"), data)
121
+ return data
122
+
123
+ @staticmethod
124
+ def signup(name: str, base_url: str = DEFAULT_BASE_URL) -> dict:
125
+ req = urllib.request.Request(
126
+ base_url.rstrip("/") + "/signup",
127
+ data=json.dumps({"name": name}).encode(),
128
+ headers={"Content-Type": "application/json"},
129
+ method="POST",
130
+ )
131
+ with urllib.request.urlopen(req) as resp:
132
+ return json.loads(resp.read().decode() or "{}")
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentmemo-py
3
+ Version: 1.0.0
4
+ Summary: Persistent memory for AI agents — store, retrieve, and recall context across sessions.
5
+ Author-email: "Dr. Nadeem Shaikh" <nadeembyit@gmail.com>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://agentmemo.dev
8
+ Project-URL: Documentation, https://agentmemo.dev/docs
9
+ Project-URL: Repository, https://github.com/skullbase-ecom/agentmemo
10
+ Keywords: ai,agent,memory,llm,mcp,semantic-search,agentmemo
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Provides-Extra: async
14
+ Requires-Dist: httpx>=0.24; extra == "async"
15
+
16
+ # agentmemo
17
+
18
+ Persistent memory for AI agents. Store, semantically retrieve, and recall context across sessions. Pure standard library (no required dependencies).
19
+
20
+ ```bash
21
+ pip install agentmemo
22
+ ```
23
+
24
+ ## Quick start
25
+
26
+ ```python
27
+ from agentmemo import MemoryClient
28
+
29
+ # Get a free key (or pass an existing one)
30
+ key = MemoryClient.signup("my-agent")["api_key"]
31
+ mem = MemoryClient(key)
32
+
33
+ # Store
34
+ mem.store(
35
+ user_id="user_123",
36
+ agent_id="assistant",
37
+ content="User prefers dark mode and works in TypeScript.",
38
+ tags=["preference"],
39
+ importance=8,
40
+ )
41
+
42
+ # Retrieve semantically
43
+ hits = mem.search(user_id="user_123", query="what are the user's preferences?")
44
+
45
+ # Context for an LLM system prompt
46
+ ctx = mem.context(user_id="user_123", format="anthropic")["context"]
47
+ ```
48
+
49
+ ## API
50
+
51
+ - `store(user_id, agent_id, content, metadata=None, ttl_seconds=None, tags=None, namespace="default", importance=5, outcome="unknown", detect_conflicts=False)`
52
+ - `search(user_id, query, agent_id=None, limit=10, namespace=None, tags=None, min_importance=None)`
53
+ - `delete(id=None, user_id=None, agent_id=None)`
54
+ - `context(user_id, agent_id=None, max_tokens=2000, format="raw")`
55
+ - `feedback(memory_id, outcome, confidence=1.0)`
56
+ - `batch(memories)`
57
+ - `stats(user_id=None)`, `usage()`
58
+ - `MemoryClient.signup(name)` — static, returns a free API key
59
+ - Async: `await mem.async_store(...)`, `await mem.async_search(...)` (requires `pip install agentmemo[async]`)
60
+
61
+ Errors raise `AgentMemoError` with `.status`, `.code`, `.body`.
62
+
63
+ Docs: https://agentmemo.dev/docs · Apache-2.0 · Built by Dr. Nadeem Shaikh
@@ -0,0 +1,5 @@
1
+ agentmemo/__init__.py,sha256=Bd8jdXsF0n_tS5xaGEWt7hn2SIhrlZB_FBa0kJoxZEI,6071
2
+ agentmemo_py-1.0.0.dist-info/METADATA,sha256=tkzz3x1LytsymtNPHKkSXsQzaRlmfIZMHwlHZoc68yQ,2230
3
+ agentmemo_py-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
4
+ agentmemo_py-1.0.0.dist-info/top_level.txt,sha256=kwGLVvBxY_gj2Xafi6wK-qmDtRjx5V5JMSfVWuScx8c,10
5
+ agentmemo_py-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ agentmemo