memlayer-py 0.1.0__tar.gz
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.
- memlayer_py-0.1.0/LICENSE +21 -0
- memlayer_py-0.1.0/PKG-INFO +430 -0
- memlayer_py-0.1.0/README.md +390 -0
- memlayer_py-0.1.0/memlayer/__init__.py +29 -0
- memlayer_py-0.1.0/memlayer/client.py +709 -0
- memlayer_py-0.1.0/memlayer_py.egg-info/PKG-INFO +430 -0
- memlayer_py-0.1.0/memlayer_py.egg-info/SOURCES.txt +12 -0
- memlayer_py-0.1.0/memlayer_py.egg-info/dependency_links.txt +1 -0
- memlayer_py-0.1.0/memlayer_py.egg-info/requires.txt +1 -0
- memlayer_py-0.1.0/memlayer_py.egg-info/top_level.txt +1 -0
- memlayer_py-0.1.0/setup.cfg +4 -0
- memlayer_py-0.1.0/setup.py +38 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Victor Sunday
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memlayer-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Persistent memory for AI agents — one API key, your agent remembers everything.
|
|
5
|
+
Home-page: https://github.com/yourusername/memlayer
|
|
6
|
+
Author: Victor Sunday
|
|
7
|
+
Author-email: sunvictor567@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Documentation, https://memlayer.online/docs
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/yourusername/memlayer/issues
|
|
11
|
+
Project-URL: Homepage, https://memlayer.online
|
|
12
|
+
Keywords: ai agent memory vector search langchain langgraph memlayer persistent memory
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Intended Audience :: Developers
|
|
22
|
+
Classifier: Development Status :: 4 - Beta
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Dynamic: author
|
|
28
|
+
Dynamic: author-email
|
|
29
|
+
Dynamic: classifier
|
|
30
|
+
Dynamic: description
|
|
31
|
+
Dynamic: description-content-type
|
|
32
|
+
Dynamic: home-page
|
|
33
|
+
Dynamic: keywords
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: project-url
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
40
|
+
|
|
41
|
+
# memlayer-py
|
|
42
|
+
|
|
43
|
+
> Persistent memory for AI agents. One API key. Your agent remembers everything.
|
|
44
|
+
|
|
45
|
+
[](https://badge.fury.io/py/memlayer-py)
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](https://opensource.org/licenses/MIT)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## The Problem
|
|
52
|
+
|
|
53
|
+
Every time a user starts a new conversation with your AI agent, it starts from zero. It doesn't remember their name, their preferences, what they complained about last week, or anything they've ever told it. You're either stuffing conversation history into the system prompt and hitting token limits, or your agent is asking the same questions over and over.
|
|
54
|
+
|
|
55
|
+
**MemLayer fixes that.**
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install memlayer-py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from memlayer import MemLayerClient
|
|
71
|
+
|
|
72
|
+
client = MemLayerClient(
|
|
73
|
+
api_key="ml_live_xxx",
|
|
74
|
+
base_url="https://memlayer.online",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Store what your agent learns
|
|
78
|
+
client.remember(
|
|
79
|
+
"User prefers concise bullet points over long paragraphs",
|
|
80
|
+
user_id="user_123",
|
|
81
|
+
agent_id="support_bot",
|
|
82
|
+
memory_type="semantic",
|
|
83
|
+
importance=0.8,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Load context at the start of every session
|
|
87
|
+
context = client.context(user_id="user_123", agent_id="support_bot")
|
|
88
|
+
for m in context.memories:
|
|
89
|
+
print(m.content)
|
|
90
|
+
|
|
91
|
+
# Search when the user asks something
|
|
92
|
+
memories = client.recall(
|
|
93
|
+
"what are this user's communication preferences?",
|
|
94
|
+
user_id="user_123",
|
|
95
|
+
agent_id="support_bot",
|
|
96
|
+
)
|
|
97
|
+
for m in memories:
|
|
98
|
+
print(f"[{m.score:.3f}] {m.content}")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Get an API Key
|
|
104
|
+
|
|
105
|
+
1. Visit [memlayer.online](https://memlayer.online)
|
|
106
|
+
2. Sign up for a free account
|
|
107
|
+
3. Copy your API key (`ml_live_xxx`)
|
|
108
|
+
|
|
109
|
+
Free plan includes 500 memories and 100 requests per day — enough to build and test your agent fully.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Core Methods
|
|
114
|
+
|
|
115
|
+
### `remember()` — Store a memory
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
result = client.remember(
|
|
119
|
+
content="User is based in Lagos, Nigeria",
|
|
120
|
+
user_id="user_123",
|
|
121
|
+
agent_id="support_bot",
|
|
122
|
+
memory_type="semantic", # episodic | semantic | summary
|
|
123
|
+
importance=0.8, # 0.0 (low) to 1.0 (high)
|
|
124
|
+
ttl_days=30, # auto-expire after 30 days. None = permanent
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
print(result.id) # memory UUID
|
|
128
|
+
print(result.is_duplicate) # True if already stored
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `recall()` — Semantic search
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
memories = client.recall(
|
|
135
|
+
query="what does this user prefer?",
|
|
136
|
+
user_id="user_123",
|
|
137
|
+
agent_id="support_bot",
|
|
138
|
+
top_k=5, # return top 5 results
|
|
139
|
+
min_score=0.70, # minimum relevance threshold
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
for m in memories:
|
|
143
|
+
print(m.content) # memory text
|
|
144
|
+
print(m.score) # hybrid relevance score
|
|
145
|
+
print(m.importance) # importance weight
|
|
146
|
+
print(m.memory_type) # episodic | semantic | summary
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Retrieval uses hybrid scoring — **70% semantic similarity + 20% recency + 10% importance** — so the most relevant and recent memories always rank first.
|
|
150
|
+
|
|
151
|
+
### `context()` — Load session context
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
# Call this at the start of every conversation
|
|
155
|
+
context = client.context(
|
|
156
|
+
user_id="user_123",
|
|
157
|
+
agent_id="support_bot",
|
|
158
|
+
top_k=10,
|
|
159
|
+
|
|
160
|
+
# Optional: pass the user's first message for smarter retrieval
|
|
161
|
+
current_message="I need help with my order",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Inject into your system prompt
|
|
165
|
+
system_prompt = "You are a helpful assistant.\n\nWhat you know about this user:\n"
|
|
166
|
+
for m in context.memories:
|
|
167
|
+
system_prompt += f"- {m.content}\n"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `update()` — Update a memory
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
# User moved cities — update the old memory in place
|
|
174
|
+
updated = client.update(
|
|
175
|
+
memory_id="775263ee-73af-4416-9804-1f274048ae08",
|
|
176
|
+
user_id="user_123",
|
|
177
|
+
agent_id="support_bot",
|
|
178
|
+
new_content="User moved from Lagos to Abuja, Nigeria",
|
|
179
|
+
importance=0.95,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
print(updated.content) # "User moved from Lagos to Abuja, Nigeria"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Same memory ID — just new content and a fresh embedding. No duplicate memories.
|
|
186
|
+
|
|
187
|
+
### `forget()` — Delete one memory
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
result = client.forget(
|
|
191
|
+
memory_id="775263ee-73af-4416-9804-1f274048ae08",
|
|
192
|
+
user_id="user_123",
|
|
193
|
+
agent_id="support_bot",
|
|
194
|
+
)
|
|
195
|
+
print(result.deleted) # 1
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### `forget_all()` — Wipe all memories
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
# Use for GDPR requests or account resets
|
|
202
|
+
result = client.forget_all(user_id="user_123", agent_id="support_bot")
|
|
203
|
+
print(result.deleted) # number of memories deleted
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `is_duplicate()` — Check before storing
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
# Optional — remember() already checks internally
|
|
210
|
+
# Use this when you want to check without committing to store
|
|
211
|
+
already_known = client.is_duplicate(
|
|
212
|
+
content="User is based in Lagos",
|
|
213
|
+
user_id="user_123",
|
|
214
|
+
agent_id="support_bot",
|
|
215
|
+
threshold=0.95,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if not already_known:
|
|
219
|
+
client.remember("User is based in Lagos", user_id="user_123", agent_id="support_bot")
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `list()` — Browse all memories
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
# Paginate through all stored memories
|
|
226
|
+
page = client.list(
|
|
227
|
+
user_id="user_123",
|
|
228
|
+
agent_id="support_bot",
|
|
229
|
+
limit=20,
|
|
230
|
+
offset=0,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
print(f"Total: {page.total}")
|
|
234
|
+
for m in page.memories:
|
|
235
|
+
print(m.content, m.created_at)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Memory Types
|
|
241
|
+
|
|
242
|
+
| Type | Use For | Example |
|
|
243
|
+
|---|---|---|
|
|
244
|
+
| `episodic` | Things that happened | "User complained about slow delivery" |
|
|
245
|
+
| `semantic` | Facts about the user | "User is based in Lagos, Nigeria" |
|
|
246
|
+
| `summary` | Compressed older memories | Auto-generated by MemLayer |
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Async Support
|
|
251
|
+
|
|
252
|
+
For LangGraph, FastAPI, and any async application:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from memlayer import AsyncMemLayerClient
|
|
256
|
+
|
|
257
|
+
async def main():
|
|
258
|
+
async with AsyncMemLayerClient(api_key="ml_live_xxx") as client:
|
|
259
|
+
|
|
260
|
+
# Store
|
|
261
|
+
result = await client.remember(
|
|
262
|
+
"User prefers dark mode",
|
|
263
|
+
user_id="user_123",
|
|
264
|
+
agent_id="support_bot",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Search
|
|
268
|
+
memories = await client.recall(
|
|
269
|
+
"UI preferences",
|
|
270
|
+
user_id="user_123",
|
|
271
|
+
agent_id="support_bot",
|
|
272
|
+
)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## LangGraph Integration
|
|
278
|
+
|
|
279
|
+
Drop MemLayer into any LangGraph graph as store and retrieve nodes:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from memlayer import AsyncMemLayerClient
|
|
283
|
+
from langgraph.graph import StateGraph, MessagesState
|
|
284
|
+
|
|
285
|
+
maas = AsyncMemLayerClient(api_key="ml_live_xxx")
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
async def load_memory(state: MessagesState):
|
|
289
|
+
"""Load relevant memories before the agent responds."""
|
|
290
|
+
last_message = state["messages"][-1].content
|
|
291
|
+
|
|
292
|
+
memories = await maas.recall(
|
|
293
|
+
query=last_message,
|
|
294
|
+
user_id=state["user_id"],
|
|
295
|
+
agent_id="my_agent",
|
|
296
|
+
top_k=5,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
memory_context = "\n".join(f"- {m.content}" for m in memories)
|
|
300
|
+
state["memory_context"] = memory_context
|
|
301
|
+
return state
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
async def save_memory(state: MessagesState):
|
|
305
|
+
"""Save what the agent learned after responding."""
|
|
306
|
+
last_message = state["messages"][-1].content
|
|
307
|
+
|
|
308
|
+
await maas.remember(
|
|
309
|
+
content=last_message,
|
|
310
|
+
user_id=state["user_id"],
|
|
311
|
+
agent_id="my_agent",
|
|
312
|
+
memory_type="episodic",
|
|
313
|
+
)
|
|
314
|
+
return state
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# Wire into your graph
|
|
318
|
+
builder = StateGraph(MessagesState)
|
|
319
|
+
builder.add_node("load_memory", load_memory)
|
|
320
|
+
builder.add_node("agent", your_agent_node)
|
|
321
|
+
builder.add_node("save_memory", save_memory)
|
|
322
|
+
|
|
323
|
+
builder.add_edge("load_memory", "agent")
|
|
324
|
+
builder.add_edge("agent", "save_memory")
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Error Handling
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
from memlayer import (
|
|
333
|
+
MemLayerClient,
|
|
334
|
+
AuthenticationError,
|
|
335
|
+
PlanLimitError,
|
|
336
|
+
MemoryNotFoundError,
|
|
337
|
+
DuplicateMemoryError,
|
|
338
|
+
MemLayerError,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
client = MemLayerClient(api_key="ml_live_xxx")
|
|
342
|
+
|
|
343
|
+
try:
|
|
344
|
+
result = client.remember(
|
|
345
|
+
"User prefers dark mode",
|
|
346
|
+
user_id="user_123",
|
|
347
|
+
agent_id="support_bot",
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
except AuthenticationError:
|
|
351
|
+
print("Invalid API key — check your ml_live_xxx key")
|
|
352
|
+
|
|
353
|
+
except PlanLimitError:
|
|
354
|
+
print("Memory limit reached — upgrade your plan at memlayer.online")
|
|
355
|
+
|
|
356
|
+
except DuplicateMemoryError:
|
|
357
|
+
print("This memory already exists — skipped")
|
|
358
|
+
|
|
359
|
+
except MemoryNotFoundError:
|
|
360
|
+
print("Memory ID not found")
|
|
361
|
+
|
|
362
|
+
except MemLayerError as e:
|
|
363
|
+
print(f"API error {e.status_code}: {e.detail}")
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Context Manager
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
# Sync
|
|
372
|
+
with MemLayerClient(api_key="ml_live_xxx") as client:
|
|
373
|
+
client.remember("something", user_id="u1", agent_id="bot")
|
|
374
|
+
|
|
375
|
+
# Async
|
|
376
|
+
async with AsyncMemLayerClient(api_key="ml_live_xxx") as client:
|
|
377
|
+
await client.remember("something", user_id="u1", agent_id="bot")
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Pricing
|
|
383
|
+
|
|
384
|
+
| Plan | Memories | Requests/day | Price |
|
|
385
|
+
|---|---|---|---|
|
|
386
|
+
| Free | 500 | 100 | $0/mo |
|
|
387
|
+
| Pro | 50,000 | 10,000 | $19/mo |
|
|
388
|
+
| Enterprise | Unlimited | Unlimited | $99+/mo |
|
|
389
|
+
|
|
390
|
+
Enterprise includes BYOD (Bring Your Own Database) — your data never leaves your Supabase instance.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## REST API
|
|
395
|
+
|
|
396
|
+
You don't need the SDK — every method maps to a REST endpoint:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
# Store
|
|
400
|
+
curl -X POST https://memlayer.online/memories \
|
|
401
|
+
-H "X-API-Key: ml_live_xxx" \
|
|
402
|
+
-H "Content-Type: application/json" \
|
|
403
|
+
-d '{"content": "User prefers dark mode", "user_id": "u1", "agent_id": "bot"}'
|
|
404
|
+
|
|
405
|
+
# Search
|
|
406
|
+
curl "https://memlayer.online/memories/search?query=preferences&user_id=u1&agent_id=bot" \
|
|
407
|
+
-H "X-API-Key: ml_live_xxx"
|
|
408
|
+
|
|
409
|
+
# Context
|
|
410
|
+
curl "https://memlayer.online/memories/context?user_id=u1&agent_id=bot" \
|
|
411
|
+
-H "X-API-Key: ml_live_xxx"
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Full API reference at [memlayer.online/docs](https://memlayer.online/docs).
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Links
|
|
419
|
+
|
|
420
|
+
- [Website](https://memlayer.online)
|
|
421
|
+
- [API Docs](https://memlayer.online/docs)
|
|
422
|
+
- [GitHub](https://github.com/yourusername/memlayer)
|
|
423
|
+
- [Report a Bug](https://github.com/yourusername/memlayer/issues)
|
|
424
|
+
- [Email](mailto:support@memlayer.online)
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## License
|
|
429
|
+
|
|
430
|
+
MIT — see [LICENSE](LICENSE) for details.
|