awareness-memory-cloud 2.4.2__tar.gz → 2.4.4__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.
Files changed (37) hide show
  1. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/PKG-INFO +90 -3
  2. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/README.md +89 -2
  3. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/awareness_memory_cloud.egg-info/PKG-INFO +90 -3
  4. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/client.py +268 -30
  5. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/pyproject.toml +1 -1
  6. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/LICENSE +0 -0
  7. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/awareness_memory_cloud.egg-info/SOURCES.txt +0 -0
  8. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/awareness_memory_cloud.egg-info/dependency_links.txt +0 -0
  9. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/awareness_memory_cloud.egg-info/requires.txt +0 -0
  10. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/awareness_memory_cloud.egg-info/top_level.txt +0 -0
  11. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/__init__.py +0 -0
  12. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/errors.py +0 -0
  13. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/export_reader.py +0 -0
  14. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/integrations/__init__.py +0 -0
  15. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/integrations/_base.py +0 -0
  16. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/integrations/autogen.py +0 -0
  17. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/integrations/crewai.py +0 -0
  18. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/integrations/langchain.py +0 -0
  19. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/integrations/praisonai.py +0 -0
  20. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/interceptor.py +0 -0
  21. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/query_rewrite.py +0 -0
  22. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/quickstart.py +0 -0
  23. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/memory_cloud/types.py +0 -0
  24. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/setup.cfg +0 -0
  25. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_client_chat_and_record.py +0 -0
  26. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_client_crud.py +0 -0
  27. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_client_extraction_passthrough.py +0 -0
  28. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_client_recall_and_compaction.py +0 -0
  29. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_client_tasks_agents_users.py +0 -0
  30. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_client_upload_export_keys.py +0 -0
  31. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_ecosystem_smoke.py +0 -0
  32. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_export_reader.py +0 -0
  33. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_integrations.py +0 -0
  34. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_interceptor_response_format_fallback.py +0 -0
  35. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_interceptor_wrap.py +0 -0
  36. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_quickstart.py +0 -0
  37. {awareness_memory_cloud-2.4.2 → awareness_memory_cloud-2.4.4}/tests/test_sdk_functional.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awareness-memory-cloud
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: Python SDK for Awareness Memory Cloud
5
5
  Author: Awareness Team
6
6
  License: Apache License
@@ -249,12 +249,99 @@ Dynamic: license-file
249
249
 
250
250
  # Awareness Memory SDK — Python
251
251
 
252
- [![PyPI](https://img.shields.io/pypi/v/awareness-memory-cloud?color=00d4ff)](https://pypi.org/project/awareness-memory-cloud/) [![Discord](https://img.shields.io/discord/1354000000000000000?color=5865F2&logo=discord&logoColor=white&label=Discord)](https://discord.com/invite/nMDrT538Qa)
252
+ [![PyPI](https://img.shields.io/pypi/v/awareness-memory-cloud?color=00d4ff)](https://pypi.org/project/awareness-memory-cloud/) [![LongMemEval R@5](https://img.shields.io/badge/LongMemEval_R%405-95.6%25-brightgreen)](https://arxiv.org/abs/2410.10813) [![Discord](https://img.shields.io/discord/1354000000000000000?color=5865F2&logo=discord&logoColor=white&label=Discord)](https://discord.com/invite/nMDrT538Qa)
253
253
 
254
- Python SDK for adding persistent memory to AI agents and apps.
254
+ Python SDK for adding persistent memory to AI agents and apps. **95.6% Recall@5 on [LongMemEval](https://arxiv.org/abs/2410.10813) (ICLR 2025)**.
255
255
 
256
256
  Online docs: <https://awareness.market/docs?doc=python>
257
257
 
258
+ ## Benchmark: LongMemEval (ICLR 2025)
259
+
260
+ Awareness Memory is evaluated on **[LongMemEval](https://arxiv.org/abs/2410.10813)** — the industry standard benchmark for long-term conversational memory, published at ICLR 2025. 500 human-curated questions across 5 core capabilities.
261
+
262
+ ```
263
+ ╔══════════════════════════════════════════════════════════════╗
264
+ ║ ║
265
+ ║ Awareness Memory — LongMemEval Benchmark Results ║
266
+ ║ ───────────────────────────────────────────────── ║
267
+ ║ ║
268
+ ║ Benchmark: LongMemEval (ICLR 2025) ║
269
+ ║ Dataset: 500 human-curated questions ║
270
+ ║ Variant: LongMemEval_S (~115k tokens per question) ║
271
+ ║ ║
272
+ ║ ┌─────────────────────────────────────────────────┐ ║
273
+ ║ │ │ ║
274
+ ║ │ Recall@1 77.6% (388 / 500) │ ║
275
+ ║ │ Recall@3 91.8% (459 / 500) │ ║
276
+ ║ │ Recall@5 95.6% (478 / 500) ◀ PRIMARY │ ║
277
+ ║ │ Recall@10 97.4% (487 / 500) │ ║
278
+ ║ │ │ ║
279
+ ║ └─────────────────────────────────────────────────┘ ║
280
+ ║ ║
281
+ ║ Method: Hybrid RRF (BM25 + Semantic Vector Search) ║
282
+ ║ Embedding: all-MiniLM-L6-v2 (384d) ║
283
+ ║ LLM Calls: 0 (pure retrieval, no generation cost) ║
284
+ ║ Hardware: Apple M1, 8GB RAM — 14 min total ║
285
+ ║ ║
286
+ ╚══════════════════════════════════════════════════════════════╝
287
+ ```
288
+
289
+ ### Leaderboard
290
+
291
+ ```
292
+ ┌─────────────────────────────────────────────────────────────┐
293
+ │ Long-Term Memory Retrieval — R@5 Leaderboard │
294
+ │ LongMemEval (ICLR 2025, 500 questions) │
295
+ ├─────────────────────────────────┬───────────┬───────────────┤
296
+ │ System │ R@5 │ Note │
297
+ ├─────────────────────────────────┼───────────┼───────────────┤
298
+ │ MemPalace (ChromaDB raw) │ 96.6% │ R@5 only * │
299
+ │ ★ Awareness Memory (Hybrid) │ 95.6% │ Hybrid RRF │
300
+ │ OMEGA │ 95.4% │ QA Accuracy │
301
+ │ Mastra (GPT-5-mini) │ 94.9% │ QA Accuracy │
302
+ │ Mastra (GPT-4o) │ 84.2% │ QA Accuracy │
303
+ │ Supermemory │ 81.6% │ QA Accuracy │
304
+ │ Zep / Graphiti │ 71.2% │ QA Accuracy │
305
+ │ GPT-4o (full context) │ 60.6% │ QA Accuracy │
306
+ ├─────────────────────────────────┴───────────┴───────────────┤
307
+ │ * MemPalace 96.6% is Recall@5 only, not QA Accuracy. │
308
+ │ Palace hierarchy was NOT used in the evaluation. │
309
+ └─────────────────────────────────────────────────────────────┘
310
+ ```
311
+
312
+ ### Accuracy by Question Type
313
+
314
+ ```
315
+ ┌─────────────────────────────────────────────────────────────┐
316
+ │ Awareness Memory — R@5 by Question Type │
317
+ │ │
318
+ │ knowledge-update ████████████████████████████ 100% │
319
+ │ multi-session ███████████████████████████▋ 98.5%│
320
+ │ single-session-asst ███████████████████████████▌ 98.2%│
321
+ │ temporal-reasoning █████████████████████████▊ 94.7%│
322
+ │ single-session-user ████████████████████████▎ 88.6%│
323
+ │ single-session-pref ███████████████████████▏ 86.7%│
324
+ │ │
325
+ │ Overall █████████████████████████▉ 95.6%│
326
+ │ │
327
+ │ ┌───────────────────────────────────────────────┐ │
328
+ │ │ Ablation Study │ │
329
+ │ │ ───────────────────────────────────────── │ │
330
+ │ │ Vector-only: 92.6% ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ │ │
331
+ │ │ BM25-only: 91.4% ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ │ │
332
+ │ │ Hybrid RRF: 95.6% ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ★ │ │
333
+ │ │ Hybrid = +3% over any │ │
334
+ │ │ single method alone │ │
335
+ │ └───────────────────────────────────────────────┘ │
336
+ │ │
337
+ │ arxiv.org/abs/2410.10813 awareness.market │
338
+ └─────────────────────────────────────────────────────────────┘
339
+ ```
340
+
341
+ Zero LLM calls. Runs on Apple M1 8GB in 14 minutes. [Reproducible benchmark scripts →](https://github.com/edwin-hao-ai/Awareness/tree/main/benchmarks/longmemeval)
342
+
343
+ ---
344
+
258
345
  ## Install
259
346
 
260
347
  ```bash
@@ -1,11 +1,98 @@
1
1
  # Awareness Memory SDK — Python
2
2
 
3
- [![PyPI](https://img.shields.io/pypi/v/awareness-memory-cloud?color=00d4ff)](https://pypi.org/project/awareness-memory-cloud/) [![Discord](https://img.shields.io/discord/1354000000000000000?color=5865F2&logo=discord&logoColor=white&label=Discord)](https://discord.com/invite/nMDrT538Qa)
3
+ [![PyPI](https://img.shields.io/pypi/v/awareness-memory-cloud?color=00d4ff)](https://pypi.org/project/awareness-memory-cloud/) [![LongMemEval R@5](https://img.shields.io/badge/LongMemEval_R%405-95.6%25-brightgreen)](https://arxiv.org/abs/2410.10813) [![Discord](https://img.shields.io/discord/1354000000000000000?color=5865F2&logo=discord&logoColor=white&label=Discord)](https://discord.com/invite/nMDrT538Qa)
4
4
 
5
- Python SDK for adding persistent memory to AI agents and apps.
5
+ Python SDK for adding persistent memory to AI agents and apps. **95.6% Recall@5 on [LongMemEval](https://arxiv.org/abs/2410.10813) (ICLR 2025)**.
6
6
 
7
7
  Online docs: <https://awareness.market/docs?doc=python>
8
8
 
9
+ ## Benchmark: LongMemEval (ICLR 2025)
10
+
11
+ Awareness Memory is evaluated on **[LongMemEval](https://arxiv.org/abs/2410.10813)** — the industry standard benchmark for long-term conversational memory, published at ICLR 2025. 500 human-curated questions across 5 core capabilities.
12
+
13
+ ```
14
+ ╔══════════════════════════════════════════════════════════════╗
15
+ ║ ║
16
+ ║ Awareness Memory — LongMemEval Benchmark Results ║
17
+ ║ ───────────────────────────────────────────────── ║
18
+ ║ ║
19
+ ║ Benchmark: LongMemEval (ICLR 2025) ║
20
+ ║ Dataset: 500 human-curated questions ║
21
+ ║ Variant: LongMemEval_S (~115k tokens per question) ║
22
+ ║ ║
23
+ ║ ┌─────────────────────────────────────────────────┐ ║
24
+ ║ │ │ ║
25
+ ║ │ Recall@1 77.6% (388 / 500) │ ║
26
+ ║ │ Recall@3 91.8% (459 / 500) │ ║
27
+ ║ │ Recall@5 95.6% (478 / 500) ◀ PRIMARY │ ║
28
+ ║ │ Recall@10 97.4% (487 / 500) │ ║
29
+ ║ │ │ ║
30
+ ║ └─────────────────────────────────────────────────┘ ║
31
+ ║ ║
32
+ ║ Method: Hybrid RRF (BM25 + Semantic Vector Search) ║
33
+ ║ Embedding: all-MiniLM-L6-v2 (384d) ║
34
+ ║ LLM Calls: 0 (pure retrieval, no generation cost) ║
35
+ ║ Hardware: Apple M1, 8GB RAM — 14 min total ║
36
+ ║ ║
37
+ ╚══════════════════════════════════════════════════════════════╝
38
+ ```
39
+
40
+ ### Leaderboard
41
+
42
+ ```
43
+ ┌─────────────────────────────────────────────────────────────┐
44
+ │ Long-Term Memory Retrieval — R@5 Leaderboard │
45
+ │ LongMemEval (ICLR 2025, 500 questions) │
46
+ ├─────────────────────────────────┬───────────┬───────────────┤
47
+ │ System │ R@5 │ Note │
48
+ ├─────────────────────────────────┼───────────┼───────────────┤
49
+ │ MemPalace (ChromaDB raw) │ 96.6% │ R@5 only * │
50
+ │ ★ Awareness Memory (Hybrid) │ 95.6% │ Hybrid RRF │
51
+ │ OMEGA │ 95.4% │ QA Accuracy │
52
+ │ Mastra (GPT-5-mini) │ 94.9% │ QA Accuracy │
53
+ │ Mastra (GPT-4o) │ 84.2% │ QA Accuracy │
54
+ │ Supermemory │ 81.6% │ QA Accuracy │
55
+ │ Zep / Graphiti │ 71.2% │ QA Accuracy │
56
+ │ GPT-4o (full context) │ 60.6% │ QA Accuracy │
57
+ ├─────────────────────────────────┴───────────┴───────────────┤
58
+ │ * MemPalace 96.6% is Recall@5 only, not QA Accuracy. │
59
+ │ Palace hierarchy was NOT used in the evaluation. │
60
+ └─────────────────────────────────────────────────────────────┘
61
+ ```
62
+
63
+ ### Accuracy by Question Type
64
+
65
+ ```
66
+ ┌─────────────────────────────────────────────────────────────┐
67
+ │ Awareness Memory — R@5 by Question Type │
68
+ │ │
69
+ │ knowledge-update ████████████████████████████ 100% │
70
+ │ multi-session ███████████████████████████▋ 98.5%│
71
+ │ single-session-asst ███████████████████████████▌ 98.2%│
72
+ │ temporal-reasoning █████████████████████████▊ 94.7%│
73
+ │ single-session-user ████████████████████████▎ 88.6%│
74
+ │ single-session-pref ███████████████████████▏ 86.7%│
75
+ │ │
76
+ │ Overall █████████████████████████▉ 95.6%│
77
+ │ │
78
+ │ ┌───────────────────────────────────────────────┐ │
79
+ │ │ Ablation Study │ │
80
+ │ │ ───────────────────────────────────────── │ │
81
+ │ │ Vector-only: 92.6% ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ │ │
82
+ │ │ BM25-only: 91.4% ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ │ │
83
+ │ │ Hybrid RRF: 95.6% ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ★ │ │
84
+ │ │ Hybrid = +3% over any │ │
85
+ │ │ single method alone │ │
86
+ │ └───────────────────────────────────────────────┘ │
87
+ │ │
88
+ │ arxiv.org/abs/2410.10813 awareness.market │
89
+ └─────────────────────────────────────────────────────────────┘
90
+ ```
91
+
92
+ Zero LLM calls. Runs on Apple M1 8GB in 14 minutes. [Reproducible benchmark scripts →](https://github.com/edwin-hao-ai/Awareness/tree/main/benchmarks/longmemeval)
93
+
94
+ ---
95
+
9
96
  ## Install
10
97
 
11
98
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awareness-memory-cloud
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: Python SDK for Awareness Memory Cloud
5
5
  Author: Awareness Team
6
6
  License: Apache License
@@ -249,12 +249,99 @@ Dynamic: license-file
249
249
 
250
250
  # Awareness Memory SDK — Python
251
251
 
252
- [![PyPI](https://img.shields.io/pypi/v/awareness-memory-cloud?color=00d4ff)](https://pypi.org/project/awareness-memory-cloud/) [![Discord](https://img.shields.io/discord/1354000000000000000?color=5865F2&logo=discord&logoColor=white&label=Discord)](https://discord.com/invite/nMDrT538Qa)
252
+ [![PyPI](https://img.shields.io/pypi/v/awareness-memory-cloud?color=00d4ff)](https://pypi.org/project/awareness-memory-cloud/) [![LongMemEval R@5](https://img.shields.io/badge/LongMemEval_R%405-95.6%25-brightgreen)](https://arxiv.org/abs/2410.10813) [![Discord](https://img.shields.io/discord/1354000000000000000?color=5865F2&logo=discord&logoColor=white&label=Discord)](https://discord.com/invite/nMDrT538Qa)
253
253
 
254
- Python SDK for adding persistent memory to AI agents and apps.
254
+ Python SDK for adding persistent memory to AI agents and apps. **95.6% Recall@5 on [LongMemEval](https://arxiv.org/abs/2410.10813) (ICLR 2025)**.
255
255
 
256
256
  Online docs: <https://awareness.market/docs?doc=python>
257
257
 
258
+ ## Benchmark: LongMemEval (ICLR 2025)
259
+
260
+ Awareness Memory is evaluated on **[LongMemEval](https://arxiv.org/abs/2410.10813)** — the industry standard benchmark for long-term conversational memory, published at ICLR 2025. 500 human-curated questions across 5 core capabilities.
261
+
262
+ ```
263
+ ╔══════════════════════════════════════════════════════════════╗
264
+ ║ ║
265
+ ║ Awareness Memory — LongMemEval Benchmark Results ║
266
+ ║ ───────────────────────────────────────────────── ║
267
+ ║ ║
268
+ ║ Benchmark: LongMemEval (ICLR 2025) ║
269
+ ║ Dataset: 500 human-curated questions ║
270
+ ║ Variant: LongMemEval_S (~115k tokens per question) ║
271
+ ║ ║
272
+ ║ ┌─────────────────────────────────────────────────┐ ║
273
+ ║ │ │ ║
274
+ ║ │ Recall@1 77.6% (388 / 500) │ ║
275
+ ║ │ Recall@3 91.8% (459 / 500) │ ║
276
+ ║ │ Recall@5 95.6% (478 / 500) ◀ PRIMARY │ ║
277
+ ║ │ Recall@10 97.4% (487 / 500) │ ║
278
+ ║ │ │ ║
279
+ ║ └─────────────────────────────────────────────────┘ ║
280
+ ║ ║
281
+ ║ Method: Hybrid RRF (BM25 + Semantic Vector Search) ║
282
+ ║ Embedding: all-MiniLM-L6-v2 (384d) ║
283
+ ║ LLM Calls: 0 (pure retrieval, no generation cost) ║
284
+ ║ Hardware: Apple M1, 8GB RAM — 14 min total ║
285
+ ║ ║
286
+ ╚══════════════════════════════════════════════════════════════╝
287
+ ```
288
+
289
+ ### Leaderboard
290
+
291
+ ```
292
+ ┌─────────────────────────────────────────────────────────────┐
293
+ │ Long-Term Memory Retrieval — R@5 Leaderboard │
294
+ │ LongMemEval (ICLR 2025, 500 questions) │
295
+ ├─────────────────────────────────┬───────────┬───────────────┤
296
+ │ System │ R@5 │ Note │
297
+ ├─────────────────────────────────┼───────────┼───────────────┤
298
+ │ MemPalace (ChromaDB raw) │ 96.6% │ R@5 only * │
299
+ │ ★ Awareness Memory (Hybrid) │ 95.6% │ Hybrid RRF │
300
+ │ OMEGA │ 95.4% │ QA Accuracy │
301
+ │ Mastra (GPT-5-mini) │ 94.9% │ QA Accuracy │
302
+ │ Mastra (GPT-4o) │ 84.2% │ QA Accuracy │
303
+ │ Supermemory │ 81.6% │ QA Accuracy │
304
+ │ Zep / Graphiti │ 71.2% │ QA Accuracy │
305
+ │ GPT-4o (full context) │ 60.6% │ QA Accuracy │
306
+ ├─────────────────────────────────┴───────────┴───────────────┤
307
+ │ * MemPalace 96.6% is Recall@5 only, not QA Accuracy. │
308
+ │ Palace hierarchy was NOT used in the evaluation. │
309
+ └─────────────────────────────────────────────────────────────┘
310
+ ```
311
+
312
+ ### Accuracy by Question Type
313
+
314
+ ```
315
+ ┌─────────────────────────────────────────────────────────────┐
316
+ │ Awareness Memory — R@5 by Question Type │
317
+ │ │
318
+ │ knowledge-update ████████████████████████████ 100% │
319
+ │ multi-session ███████████████████████████▋ 98.5%│
320
+ │ single-session-asst ███████████████████████████▌ 98.2%│
321
+ │ temporal-reasoning █████████████████████████▊ 94.7%│
322
+ │ single-session-user ████████████████████████▎ 88.6%│
323
+ │ single-session-pref ███████████████████████▏ 86.7%│
324
+ │ │
325
+ │ Overall █████████████████████████▉ 95.6%│
326
+ │ │
327
+ │ ┌───────────────────────────────────────────────┐ │
328
+ │ │ Ablation Study │ │
329
+ │ │ ───────────────────────────────────────── │ │
330
+ │ │ Vector-only: 92.6% ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ │ │
331
+ │ │ BM25-only: 91.4% ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ │ │
332
+ │ │ Hybrid RRF: 95.6% ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ★ │ │
333
+ │ │ Hybrid = +3% over any │ │
334
+ │ │ single method alone │ │
335
+ │ └───────────────────────────────────────────────┘ │
336
+ │ │
337
+ │ arxiv.org/abs/2410.10813 awareness.market │
338
+ └─────────────────────────────────────────────────────────────┘
339
+ ```
340
+
341
+ Zero LLM calls. Runs on Apple M1 8GB in 14 minutes. [Reproducible benchmark scripts →](https://github.com/edwin-hao-ai/Awareness/tree/main/benchmarks/longmemeval)
342
+
343
+ ---
344
+
258
345
  ## Install
259
346
 
260
347
  ```bash
@@ -19,11 +19,62 @@ RETRYABLE_STATUSES = {429, 500, 502, 503, 504}
19
19
 
20
20
  _DEFAULT_EXTRACTION_MAX_TOKENS = 16384
21
21
 
22
+ # Cloud REST default — public Awareness Cloud, NEVER a local dev server.
23
+ DEFAULT_CLOUD_BASE_URL = "https://awareness.market/api/v1"
24
+ # Local daemon root — `@awareness-sdk/local` (single-tenant) listens here.
25
+ DEFAULT_LOCAL_DAEMON_URL = "http://localhost:37800"
26
+
27
+ # The four MCP tools the local daemon exposes via JSON-RPC at /mcp.
28
+ DAEMON_SUPPORTED_TOOLS = frozenset(
29
+ {"awareness_init", "awareness_recall", "awareness_record", "awareness_lookup"}
30
+ )
31
+
32
+
33
+ def _parse_recall_markdown(text: str) -> List[Dict[str, Any]]:
34
+ """Parse the markdown summary that the local daemon's `awareness_recall` returns
35
+ into structured items.
36
+
37
+ Daemon format example:
38
+ Found N memories:
39
+ 1. [event_type] Title (95%, 3d ago, ~120tok)
40
+ Snippet line 1
41
+ Snippet line 2
42
+ 2. [event_type] Title 2 ...
43
+ """
44
+ if not text:
45
+ return []
46
+ items: List[Dict[str, Any]] = []
47
+ current: Optional[Dict[str, Any]] = None
48
+ header_re = re.compile(r"^\s*(\d+)\.\s*\[([^\]]+)\]\s*(.+?)(?:\s*\(([^)]+)\))?\s*$")
49
+ score_re = re.compile(r"(\d+(?:\.\d+)?)%")
50
+ for raw_line in text.splitlines():
51
+ m = header_re.match(raw_line)
52
+ if m:
53
+ if current is not None:
54
+ items.append(current)
55
+ meta = m.group(4) or ""
56
+ sm = score_re.search(meta)
57
+ current = {
58
+ "id": f"local_{m.group(1)}",
59
+ "type": m.group(2),
60
+ "title": m.group(3).strip(),
61
+ "snippet_lines": [],
62
+ }
63
+ if sm:
64
+ current["score"] = float(sm.group(1)) / 100.0
65
+ elif current is not None and raw_line.strip():
66
+ current["snippet_lines"].append(raw_line.strip())
67
+ if current is not None:
68
+ items.append(current)
69
+ for it in items:
70
+ it["snippet"] = " ".join(it.pop("snippet_lines"))
71
+ return items
72
+
22
73
 
23
74
  class MemoryCloudClient:
24
75
  def __init__(
25
76
  self,
26
- base_url: str,
77
+ base_url: Optional[str] = None,
27
78
  api_key: Optional[str] = None,
28
79
  timeout: float = 30.0,
29
80
  max_retries: int = 2,
@@ -39,12 +90,20 @@ class MemoryCloudClient:
39
90
  user_id: Optional[str] = None,
40
91
  agent_role: Optional[str] = None,
41
92
  mode: str = "cloud", # "cloud" | "local" | "auto"
42
- # `local` here means a self-hosted Awareness backend (docker-compose / on-prem) —
43
- # NOT the @awareness-sdk/local daemon on port 37800. The REST shape is identical to
44
- # cloud, only the host changes. Default matches `docker-compose up` (port 8000).
45
- local_url: str = "http://localhost:8000/api/v1",
93
+ # `mode="local"` routes the four daemon-supported tools (awareness_init,
94
+ # awareness_recall, awareness_record, awareness_lookup) through the local
95
+ # `@awareness-sdk/local` daemon at port 37800 via MCP JSON-RPC. Cloud-only
96
+ # endpoints (createMemory, listMemories, …) raise LOCAL_NOT_SUPPORTED.
97
+ # `mode="cloud"` (default) talks REST to the public Awareness Cloud.
98
+ # `mode="auto"` probes the daemon first and falls back to cloud.
99
+ local_url: str = DEFAULT_LOCAL_DAEMON_URL,
46
100
  ):
47
- self.base_url = self._resolve_base_url(base_url, mode, local_url)
101
+ self.mode = mode
102
+ # Cloud REST base URL — defaults to the public Awareness Cloud, NEVER to a local dev server.
103
+ self.base_url = (base_url or DEFAULT_CLOUD_BASE_URL).rstrip("/")
104
+ # Local daemon root URL (used for /healthz probe and /mcp JSON-RPC).
105
+ self.local_daemon_url = (local_url or DEFAULT_LOCAL_DAEMON_URL).rstrip("/")
106
+ self._daemon_alive: Optional[bool] = None
48
107
  self.api_key = api_key
49
108
  self.timeout = timeout
50
109
  self.max_retries = max(0, max_retries)
@@ -70,38 +129,107 @@ class MemoryCloudClient:
70
129
  if self._extraction_llm is not None:
71
130
  self._llm_type = _detect_llm_type(self._extraction_llm)
72
131
 
73
- def _resolve_base_url(self, base_url: str, mode: str, local_url: str) -> str:
74
- """Resolve the effective base URL based on mode.
132
+ # ----------------------------
133
+ # Local daemon bridge (mode="local" / "auto")
134
+ # ----------------------------
135
+ def _probe_daemon(self) -> bool:
136
+ """Best-effort daemon liveness check, cached for the lifetime of the client."""
137
+ if self._daemon_alive is not None:
138
+ return self._daemon_alive
139
+ try:
140
+ r = self.session.get(f"{self.local_daemon_url}/", timeout=1.0)
141
+ self._daemon_alive = r.status_code == 200
142
+ except Exception:
143
+ self._daemon_alive = False
144
+ return self._daemon_alive
145
+
146
+ def _should_use_daemon(self) -> bool:
147
+ if self.mode == "local":
148
+ return True
149
+ if self.mode == "auto":
150
+ return self._probe_daemon()
151
+ return False
152
+
153
+ def _require_cloud_mode(self, method: str) -> None:
154
+ if self.mode == "local":
155
+ raise MemoryCloudError(
156
+ "LOCAL_NOT_SUPPORTED",
157
+ f"{method}() requires cloud mode — the local daemon does not implement "
158
+ f"this endpoint. Use mode='cloud'/'auto', or call the daemon directly via "
159
+ f"@awareness-sdk/local.",
160
+ )
161
+
162
+ def call_local_daemon(self, tool_name: str, args: Dict[str, Any]) -> Dict[str, Any]:
163
+ """Call one of the four daemon-supported MCP tools via JSON-RPC.
75
164
 
76
- `local` mode points at a self-hosted Awareness backend (REST shape identical to
77
- cloud). For the `@awareness-sdk/local` daemon (port 37800, different REST shape),
78
- use the daemon directly or `@awareness-sdk/openclaw-memory`.
165
+ Daemon tool responses come back as MCP `content[0].text`. init/record/lookup
166
+ typically return JSON in that text; awareness_recall returns a markdown summary
167
+ which we parse via `_parse_recall_markdown` so the SDK shape stays stable.
79
168
  """
80
- if base_url and mode == "cloud":
81
- return base_url.rstrip("/")
82
- if mode == "local":
83
- return local_url.rstrip("/")
84
- if mode == "auto":
85
- # Try local first, fallback to cloud. Backend serves /health at root, but
86
- # local_url usually includes /api/v1 — strip the API prefix for the health probe.
87
- import urllib.request
88
- local_root = local_url.rstrip("/")
89
- if local_root.endswith("/api/v1"):
90
- health_url = local_root[: -len("/api/v1")] + "/health"
91
- else:
92
- health_url = local_root + "/health"
169
+ if tool_name not in DAEMON_SUPPORTED_TOOLS:
170
+ raise MemoryCloudError(
171
+ "LOCAL_NOT_SUPPORTED",
172
+ f"Tool '{tool_name}' is not supported by the local daemon. "
173
+ f"Supported: {sorted(DAEMON_SUPPORTED_TOOLS)}.",
174
+ )
175
+ body = {
176
+ "jsonrpc": "2.0",
177
+ "id": int(time.time() * 1000),
178
+ "method": "tools/call",
179
+ "params": {"name": tool_name, "arguments": args},
180
+ }
181
+ try:
182
+ response = self.session.post(
183
+ f"{self.local_daemon_url}/mcp",
184
+ json=body,
185
+ timeout=self.timeout,
186
+ headers={"Content-Type": "application/json"},
187
+ )
188
+ except requests.RequestException as exc:
189
+ raise MemoryCloudError("LOCAL_DAEMON_ERROR", f"Daemon RPC failed: {exc}") from exc
190
+ if response.status_code >= 400:
191
+ raise MemoryCloudError(
192
+ "LOCAL_DAEMON_ERROR",
193
+ f"Daemon HTTP {response.status_code}: {response.text[:200]}",
194
+ )
195
+ try:
196
+ payload = response.json()
197
+ except ValueError as exc:
198
+ raise MemoryCloudError("LOCAL_DAEMON_ERROR", f"Daemon returned non-JSON: {exc}") from exc
199
+ if isinstance(payload, dict) and payload.get("error"):
200
+ err = payload["error"]
201
+ msg = err.get("message") if isinstance(err, dict) else str(err)
202
+ raise MemoryCloudError("LOCAL_DAEMON_ERROR", msg or "Daemon RPC error")
203
+ result = (payload or {}).get("result") or {}
204
+ content = result.get("content") or []
205
+ text = ""
206
+ if content and isinstance(content, list):
207
+ first = content[0] or {}
208
+ if isinstance(first, dict):
209
+ text = first.get("text", "") or ""
210
+
211
+ # Try whole-text JSON first (init/record/lookup typically use this).
212
+ if text:
93
213
  try:
94
- urllib.request.urlopen(health_url, timeout=1)
95
- return local_root
96
- except Exception:
97
- return base_url.rstrip("/") if base_url else local_root
98
- # Default: use base_url if provided, else local_url
99
- return (base_url or local_url).rstrip("/")
214
+ parsed = json.loads(text)
215
+ if isinstance(parsed, dict):
216
+ return parsed
217
+ if isinstance(parsed, list):
218
+ return {"items": parsed, "text": text}
219
+ except (ValueError, TypeError):
220
+ pass
221
+
222
+ # awareness_recall: parse markdown summary into structured items.
223
+ if tool_name == "awareness_recall":
224
+ return {"items": _parse_recall_markdown(text), "text": text}
225
+
226
+ return {"text": text}
100
227
 
101
228
  # ----------------------------
102
229
  # Memory CRUD
103
230
  # ----------------------------
104
231
  def create_memory(self, payload: Dict[str, Any], trace_id: Optional[str] = None) -> Dict[str, Any]:
232
+ self._require_cloud_mode("create_memory")
105
233
  data, resolved_trace = self._request(
106
234
  method="POST",
107
235
  path="/memories",
@@ -117,6 +245,7 @@ class MemoryCloudClient:
117
245
  limit: int = 100,
118
246
  trace_id: Optional[str] = None,
119
247
  ) -> List[Dict[str, Any]]:
248
+ self._require_cloud_mode("list_memories")
120
249
  params: Dict[str, Any] = {"skip": max(0, skip), "limit": max(1, limit)}
121
250
  if owner_id:
122
251
  params["owner_id"] = owner_id
@@ -291,6 +420,27 @@ class MemoryCloudClient:
291
420
  if ids is not None:
292
421
  body["ids"] = ids
293
422
 
423
+ # Local daemon bridge: route through awareness_recall MCP tool.
424
+ if self._should_use_daemon():
425
+ daemon_args: Dict[str, Any] = {
426
+ "semantic_query": query,
427
+ "limit": limit,
428
+ "detail": detail or "summary",
429
+ }
430
+ if effective_keyword:
431
+ daemon_args["keyword_query"] = effective_keyword
432
+ if scope:
433
+ daemon_args["scope"] = scope
434
+ if recall_mode:
435
+ daemon_args["recall_mode"] = recall_mode
436
+ if agent_role:
437
+ daemon_args["agent_role"] = agent_role
438
+ if ids:
439
+ daemon_args["ids"] = ids
440
+ daemon_result = self.call_local_daemon("awareness_recall", daemon_args)
441
+ items = daemon_result.get("items") or daemon_result.get("results") or []
442
+ return {"results": items}
443
+
294
444
  payload, resolved_trace_id = self._request(
295
445
  method="POST",
296
446
  path=f"/memories/{memory_id}/retrieve",
@@ -462,6 +612,43 @@ class MemoryCloudClient:
462
612
  Returns:
463
613
  Dict with ingest result and/or insights submission result.
464
614
  """
615
+ # Local daemon bridge: route through awareness_record MCP tool.
616
+ if self._should_use_daemon():
617
+ args: Dict[str, Any] = {"action": "remember"}
618
+ events_count = 0
619
+ if isinstance(content, str):
620
+ args["content"] = content
621
+ events_count = 1
622
+ elif isinstance(content, list):
623
+ args["action"] = "remember_batch"
624
+ args["items"] = [
625
+ item if isinstance(item, dict) else {"content": str(item)}
626
+ for item in content
627
+ ]
628
+ events_count = len(content)
629
+ elif isinstance(content, dict):
630
+ args["content"] = content.get("content", "")
631
+ events_count = 1
632
+ if insights is not None:
633
+ args["insights"] = insights
634
+ if scope:
635
+ args["scope"] = scope
636
+ if session_id:
637
+ args["session_id"] = session_id
638
+ if source:
639
+ args["source"] = source
640
+ if user_id:
641
+ args["user_id"] = user_id
642
+ if agent_role:
643
+ args["agent_role"] = agent_role
644
+ daemon_result = self.call_local_daemon("awareness_record", args)
645
+ return {
646
+ "memory_id": memory_id,
647
+ "session_id": daemon_result.get("session_id", session_id or ""),
648
+ "events_count": events_count,
649
+ "ingest": daemon_result,
650
+ }
651
+
465
652
  source_label = self._clean_source(source or self.default_source)
466
653
  active_session = self._resolve_session(
467
654
  memory_id=memory_id,
@@ -917,6 +1104,18 @@ class MemoryCloudClient:
917
1104
  Returns:
918
1105
  {memory_id, generated_at, recent_days, open_tasks, knowledge_cards}
919
1106
  """
1107
+ # Local daemon bridge: route through awareness_init MCP tool.
1108
+ if self._should_use_daemon():
1109
+ args: Dict[str, Any] = {
1110
+ "source": self.default_source or "sdk",
1111
+ "days": days,
1112
+ "max_cards": max_cards,
1113
+ "max_tasks": max_tasks,
1114
+ }
1115
+ if user_id:
1116
+ args["user_id"] = user_id
1117
+ return self.call_local_daemon("awareness_init", args)
1118
+
920
1119
  params: Dict[str, Any] = {"days": days, "max_cards": max_cards, "max_tasks": max_tasks}
921
1120
  if user_id:
922
1121
  params["user_id"] = user_id
@@ -960,6 +1159,27 @@ class MemoryCloudClient:
960
1159
  Returns:
961
1160
  {total, cards: [{category, title, summary, tags, confidence, status}]}
962
1161
  """
1162
+ # Local daemon bridge: route through awareness_lookup type=knowledge.
1163
+ if self._should_use_daemon():
1164
+ args: Dict[str, Any] = {"type": "knowledge", "limit": limit}
1165
+ if category:
1166
+ args["category"] = category
1167
+ if status:
1168
+ args["status"] = status
1169
+ if query:
1170
+ args["query"] = query
1171
+ if user_id:
1172
+ args["user_id"] = user_id
1173
+ daemon_result = self.call_local_daemon("awareness_lookup", args)
1174
+ cards = (
1175
+ daemon_result.get("knowledge_cards")
1176
+ or daemon_result.get("cards")
1177
+ or daemon_result.get("items")
1178
+ or []
1179
+ )
1180
+ total = daemon_result.get("total")
1181
+ return {"total": total if total is not None else len(cards), "cards": cards}
1182
+
963
1183
  params: Dict[str, Any] = {"limit": limit}
964
1184
  if category:
965
1185
  params["category"] = category
@@ -1006,6 +1226,24 @@ class MemoryCloudClient:
1006
1226
  Returns:
1007
1227
  {total, in_progress, pending, tasks: [{title, priority, status, detail, context}]}
1008
1228
  """
1229
+ # Local daemon bridge: route through awareness_lookup type=tasks.
1230
+ if self._should_use_daemon():
1231
+ args: Dict[str, Any] = {"type": "tasks", "limit": limit}
1232
+ if priority:
1233
+ args["priority"] = priority
1234
+ if user_id:
1235
+ args["user_id"] = user_id
1236
+ daemon_result = self.call_local_daemon("awareness_lookup", args)
1237
+ tasks = daemon_result.get("tasks") or daemon_result.get("items") or []
1238
+ in_progress_count = sum(1 for t in tasks if (t or {}).get("status") == "in_progress")
1239
+ pending_count = sum(1 for t in tasks if (t or {}).get("status") == "pending")
1240
+ return {
1241
+ "total": len(tasks),
1242
+ "in_progress": in_progress_count,
1243
+ "pending": pending_count,
1244
+ "tasks": tasks,
1245
+ }
1246
+
1009
1247
  params: Dict[str, Any] = {"limit": limit, "status": "pending"}
1010
1248
  if priority:
1011
1249
  params["priority"] = priority
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "awareness-memory-cloud"
7
- version = "2.4.2"
7
+ version = "2.4.4"
8
8
  description = "Python SDK for Awareness Memory Cloud"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }