MemoryOS 0.1.13__py3-none-any.whl → 0.2.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.
Potentially problematic release.
This version of MemoryOS might be problematic. Click here for more details.
- {memoryos-0.1.13.dist-info → memoryos-0.2.0.dist-info}/METADATA +14 -25
- {memoryos-0.1.13.dist-info → memoryos-0.2.0.dist-info}/RECORD +21 -15
- memos/__init__.py +1 -1
- memos/configs/llm.py +1 -0
- memos/configs/mem_reader.py +4 -0
- memos/configs/memory.py +5 -0
- memos/graph_dbs/item.py +46 -0
- memos/graph_dbs/neo4j.py +72 -5
- memos/llms/openai.py +1 -0
- memos/mem_reader/simple_struct.py +11 -6
- memos/memories/textual/item.py +3 -1
- memos/memories/textual/tree.py +5 -1
- memos/memories/textual/tree_text_memory/organize/conflict.py +196 -0
- memos/memories/textual/tree_text_memory/organize/manager.py +49 -8
- memos/memories/textual/tree_text_memory/organize/redundancy.py +212 -0
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +235 -0
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +584 -0
- memos/templates/mem_reader_prompts.py +42 -15
- memos/templates/tree_reorganize_prompts.py +168 -0
- {memoryos-0.1.13.dist-info → memoryos-0.2.0.dist-info}/LICENSE +0 -0
- {memoryos-0.1.13.dist-info → memoryos-0.2.0.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: MemoryOS
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Intelligence Begins with Memory
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: memory,llm,language model,memoryOS,agent
|
|
@@ -22,6 +22,7 @@ Requires-Dist: ollama (>=0.4.8,<0.5.0)
|
|
|
22
22
|
Requires-Dist: openai (>=1.77.0,<2.0.0)
|
|
23
23
|
Requires-Dist: qdrant-client (>=1.14.2,<2.0.0)
|
|
24
24
|
Requires-Dist: redis (>=6.2.0,<7.0.0)
|
|
25
|
+
Requires-Dist: schedule (>=1.2.2,<2.0.0)
|
|
25
26
|
Requires-Dist: sentence-transformers (>=4.1.0,<5.0.0)
|
|
26
27
|
Requires-Dist: sqlalchemy (>=2.0.41,<3.0.0)
|
|
27
28
|
Requires-Dist: tenacity (>=9.1.2,<10.0.0)
|
|
@@ -31,13 +32,11 @@ Description-Content-Type: text/markdown
|
|
|
31
32
|
|
|
32
33
|
<div align="center">
|
|
33
34
|
<a href="https://memos.openmem.net/">
|
|
34
|
-
<img src="
|
|
35
|
+
<img src="https://statics.memtensor.com.cn/memos/memos-banner.gif" alt="MemOS Banner">
|
|
35
36
|
</a>
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
38
|
<h1 align="center">
|
|
40
|
-
<img src="
|
|
39
|
+
<img src="https://statics.memtensor.com.cn/logo/memos_color_m.png" alt="MemOS Logo" width="50"/> MemOS 1.0: 星河 (Stellar) <img src="https://img.shields.io/badge/status-Preview-blue" alt="Preview Badge"/>
|
|
41
40
|
</h1>
|
|
42
41
|
|
|
43
42
|
<p>
|
|
@@ -50,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
50
49
|
<a href="https://pypi.org/project/MemoryOS">
|
|
51
50
|
<img src="https://img.shields.io/pypi/pyversions/MemoryOS.svg" alt="Supported Python versions">
|
|
52
51
|
</a>
|
|
53
|
-
<a href="https://memos.openmem.net/
|
|
52
|
+
<a href="https://memos-docs.openmem.net/home/overview/">
|
|
54
53
|
<img src="https://img.shields.io/badge/Documentation-view-blue.svg" alt="Documentation">
|
|
55
54
|
</a>
|
|
56
55
|
<a href="https://arxiv.org/abs/2507.03724">
|
|
@@ -62,7 +61,7 @@ Description-Content-Type: text/markdown
|
|
|
62
61
|
<a href="https://discord.gg/Txbx3gebZR">
|
|
63
62
|
<img src="https://img.shields.io/badge/Discord-join%20chat-7289DA.svg?logo=discord" alt="Discord">
|
|
64
63
|
</a>
|
|
65
|
-
<a href="
|
|
64
|
+
<a href="https://statics.memtensor.com.cn/memos/qr-code.png">
|
|
66
65
|
<img src="https://img.shields.io/badge/WeChat-Group-07C160.svg?logo=wechat" alt="WeChat Group">
|
|
67
66
|
</a>
|
|
68
67
|
<a href="https://opensource.org/license/apache-2-0/">
|
|
@@ -73,17 +72,14 @@ Description-Content-Type: text/markdown
|
|
|
73
72
|
|
|
74
73
|
---
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
<img src="docs/assets/sota_score.jpg" alt="SOTA SCORE">
|
|
78
|
-
</a>
|
|
79
|
-
|
|
75
|
+
<img src="https://statics.memtensor.com.cn/memos/sota_score.jpg" alt="SOTA SCORE">
|
|
80
76
|
|
|
81
77
|
**MemOS** is an operating system for Large Language Models (LLMs) that enhances them with long-term memory capabilities. It allows LLMs to store, retrieve, and manage information, enabling more context-aware, consistent, and personalized interactions.
|
|
82
78
|
|
|
83
|
-
- **Website**:
|
|
84
|
-
- **Documentation**:
|
|
85
|
-
- **API Reference**:
|
|
86
|
-
- **Source Code**:
|
|
79
|
+
- **Website**: https://memos.openmem.net/
|
|
80
|
+
- **Documentation**: https://memos-docs.openmem.net/home/overview/
|
|
81
|
+
- **API Reference**: https://memos-docs.openmem.net/docs/api/info/
|
|
82
|
+
- **Source Code**: https://github.com/MemTensor/MemOS
|
|
87
83
|
|
|
88
84
|
## 📈 Performance Benchmark
|
|
89
85
|
|
|
@@ -97,19 +93,12 @@ MemOS demonstrates significant improvements over baseline memory solutions in mu
|
|
|
97
93
|
|
|
98
94
|
> 💡 **Temporal reasoning accuracy improved by 159% compared to the OpenAI baseline.**
|
|
99
95
|
|
|
100
|
-
|
|
101
|
-
|
|
102
96
|
### Details of End-to-End Evaluation on LOCOMO
|
|
103
97
|
|
|
104
98
|
> [!NOTE]
|
|
105
99
|
> Comparison of LLM Judge Scores across five major tasks in the LOCOMO benchmark. Each bar shows the mean evaluation score judged by LLMs for a given method-task pair, with standard deviation as error bars. MemOS-0630 consistently outperforms baseline methods (LangMem, Zep, OpenAI, Mem0) across all task types, especially in multi-hop and temporal reasoning scenarios.
|
|
106
100
|
|
|
107
|
-
<
|
|
108
|
-
<img src="docs/assets/score_all_end2end.jpg" alt="END2END SCORE">
|
|
109
|
-
</a>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
<img src="https://statics.memtensor.com.cn/memos/score_all_end2end.jpg" alt="END2END SCORE">
|
|
113
102
|
|
|
114
103
|
## ✨ Key Features
|
|
115
104
|
|
|
@@ -227,7 +216,7 @@ Join our community to ask questions, share your projects, and connect with other
|
|
|
227
216
|
- **Discord**: Join our <a href="https://discord.gg/Txbx3gebZR" target="_blank">Discord Server</a>.
|
|
228
217
|
- **WeChat**: Scan the QR code to join our WeChat group.
|
|
229
218
|
|
|
230
|
-
<img src="
|
|
219
|
+
<img src="https://statics.memtensor.com.cn/memos/qr-code.png" alt="QR Code" width="600">
|
|
231
220
|
|
|
232
221
|
## 📜 Citation
|
|
233
222
|
|
|
@@ -270,7 +259,7 @@ url = {https://global-sci.com/article/91443/memory3-language-modeling-with-expli
|
|
|
270
259
|
|
|
271
260
|
## 🙌 Contributing
|
|
272
261
|
|
|
273
|
-
We welcome contributions from the community! Please read our [contribution guidelines](https://memos.openmem.net/
|
|
262
|
+
We welcome contributions from the community! Please read our [contribution guidelines](https://memos-docs.openmem.net/contribution/overview) to get started.
|
|
274
263
|
|
|
275
264
|
## 📄 License
|
|
276
265
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
memos/__init__.py,sha256=
|
|
1
|
+
memos/__init__.py,sha256=9C9wxypP0Ch_j6yjvoNgmbr8spHUAsdC1hd49UswG2U,575
|
|
2
2
|
memos/api/start_api.py,sha256=vDKS3D8AEwlDAkpWxHiblzVqcQ3cvGSnnpP4Jhjm3GE,14656
|
|
3
3
|
memos/chunkers/__init__.py,sha256=7lZOTN3e9Yp5XBsDX5wnWJ3tY126cRU9GmfevzJXAtU,67
|
|
4
4
|
memos/chunkers/base.py,sha256=z0rG5vM7FGremQdSZ_3jlTGbsDtlkWAYWdtSAGqpaR4,655
|
|
@@ -10,13 +10,13 @@ memos/configs/chunker.py,sha256=Ulci0MyhS_FkuV95l7Pr9Vei179aEFEUigwVmtmSDfI,1593
|
|
|
10
10
|
memos/configs/embedder.py,sha256=a7EbWB3tnqeK_pdK6-CneoKIwSqQg6sJrXuHX3J1PnI,1769
|
|
11
11
|
memos/configs/graph_db.py,sha256=h5wz2lJSdB1IVaidzWHXDfvL4_IAjrjYrmypu1QCCq4,1432
|
|
12
12
|
memos/configs/internet_retriever.py,sha256=7x1Sp--skifHQqWvcPFiEOkda6HCCocw6HSO5H127jk,3046
|
|
13
|
-
memos/configs/llm.py,sha256=
|
|
13
|
+
memos/configs/llm.py,sha256=iNLGnZYbeqkLpjP4L0sGzoCnH4NH8SBUzBt6bTohxZI,2446
|
|
14
14
|
memos/configs/mem_chat.py,sha256=TjEQHRG1HpLwCBo3hrn5aVK23rykNtV6Be5p4YIg7F4,2571
|
|
15
15
|
memos/configs/mem_cube.py,sha256=AvffqpFC2WmzshouxLJbabuzt0PKLWSauQMnagP_EM0,3088
|
|
16
16
|
memos/configs/mem_os.py,sha256=bUuyi6boIjjvYzrlFoCSqchbWF37IcA5BQi03feq7bg,2476
|
|
17
|
-
memos/configs/mem_reader.py,sha256=
|
|
17
|
+
memos/configs/mem_reader.py,sha256=m5pRr3DxtmOKBFdbnCki_XgG47oKfcasJNwZGXg9gN4,2022
|
|
18
18
|
memos/configs/mem_scheduler.py,sha256=bTsBfl9GoXbjkiPDCenH8K2QY4RfAJNk-XN1UBl2W4Q,2959
|
|
19
|
-
memos/configs/memory.py,sha256
|
|
19
|
+
memos/configs/memory.py,sha256=kAGmniEgBdHgAsZGXeUF7J-ekwU5H9HnxQp_3jhUtLQ,6976
|
|
20
20
|
memos/configs/parser.py,sha256=dy-QoevJbCnkJePKgpzR4oziOzYnS4jB6XH-YrpeMns,1145
|
|
21
21
|
memos/configs/utils.py,sha256=X9NQ6-xURsZROAdS3WT96phVfHcOHgDPOo2Yq68QoKM,242
|
|
22
22
|
memos/configs/vec_db.py,sha256=Gjhhda94pyTDjyJGe2Z6rVEqH4FtViiwq1-7QWhjarM,2335
|
|
@@ -30,14 +30,15 @@ memos/exceptions.py,sha256=UnBoZUYdwb1KoQPE-pXSLT4yOjkwxse9fx0rb_MhEzo,531
|
|
|
30
30
|
memos/graph_dbs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
31
|
memos/graph_dbs/base.py,sha256=dTCdoYNS6FZQukpmpqhh8c_NwZ9Myql41g1Im8aDR6E,6873
|
|
32
32
|
memos/graph_dbs/factory.py,sha256=9fS8w0GowWNMSupEyXyTRnivX-RJxTkxPy-tNHs15OM,732
|
|
33
|
-
memos/graph_dbs/
|
|
33
|
+
memos/graph_dbs/item.py,sha256=BfK17hA_hHu7I348jWugP_rb9GS5hpKfgqtYGGHBohk,1450
|
|
34
|
+
memos/graph_dbs/neo4j.py,sha256=bOTBvHGw_65dudqIpQep9D95qBPyx-NHfTybsNAdjiM,33672
|
|
34
35
|
memos/hello_world.py,sha256=RV1vXfK1_U_xAvSusqc-4A8wk3yr8WEQ9q88dmBxvnI,3057
|
|
35
36
|
memos/llms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
37
|
memos/llms/base.py,sha256=E7qlIWUi_bxRwbKw0vGG7NjHksz45D_-gcrHKT3Ak1k,438
|
|
37
38
|
memos/llms/factory.py,sha256=1JP2ZwUKN4ywaNgdZcRM6wSjahnEXqyuFiGzoxLCadY,792
|
|
38
39
|
memos/llms/hf.py,sha256=rxcR8nAvhezsIQNeOeow-xn-z2h9j1poJO2jPNcJAg8,9008
|
|
39
40
|
memos/llms/ollama.py,sha256=_nZYe0OauPWWcFFYVPSNtvyDGblrL49-EznApa-Mr5I,2565
|
|
40
|
-
memos/llms/openai.py,sha256=
|
|
41
|
+
memos/llms/openai.py,sha256=agIFI58UXz-fOEth4EBTUlQ5p6ECBg9v2gFed6OVFzw,1192
|
|
41
42
|
memos/llms/utils.py,sha256=OcbM9iSpFJpio7sTT5wvxVx-JnqjIx7eSgiRk7dt0ZI,292
|
|
42
43
|
memos/log.py,sha256=ocQi2NWlpefgxOJi9EoB0as6tXhLA4dmXuVIgZT52FE,2234
|
|
43
44
|
memos/mem_chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -56,7 +57,7 @@ memos/mem_reader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
|
56
57
|
memos/mem_reader/base.py,sha256=SSuaD3J88XbHsME1Qa-EAgZ57xApPHPHppMSeeS3JZ4,957
|
|
57
58
|
memos/mem_reader/factory.py,sha256=emKnId9BhScSkqCZyLSLWntixnU3wAMIOVdsRpHldJA,766
|
|
58
59
|
memos/mem_reader/memory.py,sha256=f3fAjrs8Jf6mBZWTgzkEZle7XjDwTJHxM1L2sOb85Tg,13288
|
|
59
|
-
memos/mem_reader/simple_struct.py,sha256=
|
|
60
|
+
memos/mem_reader/simple_struct.py,sha256=SfFxtZ_YGcDh-A4Ztx4ouEBC6m0j8mLCVF2HN4oGRy8,9739
|
|
60
61
|
memos/mem_scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
62
|
memos/mem_scheduler/base_scheduler.py,sha256=bYxLm6nUorJGm8DCiBOJ6DG8eqCK3cCIrm3cEe2be7M,6617
|
|
62
63
|
memos/mem_scheduler/general_scheduler.py,sha256=Tp9Nh8qFVR9TklmmISSoBoeHoWTqM0P977gmb-HS6rM,12895
|
|
@@ -84,12 +85,16 @@ memos/memories/parametric/lora.py,sha256=TqSI2OjmFi-XXCeM-MchSwh1sAhOwL7_JnOwSy9
|
|
|
84
85
|
memos/memories/textual/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
86
|
memos/memories/textual/base.py,sha256=3XVeuXZY3ZFzZ6zHIE3l5hx7X2PiH7orxvCwrNZ1l3U,2812
|
|
86
87
|
memos/memories/textual/general.py,sha256=7iektZA-_iR80sOlADW2EFkPT5Utbunu9hdZgT7Ocrk,11072
|
|
87
|
-
memos/memories/textual/item.py,sha256=
|
|
88
|
+
memos/memories/textual/item.py,sha256=8tSsiDztsxfyNXWB1TG2E5a4nhIQcuYMJ_BrnPl6osQ,6323
|
|
88
89
|
memos/memories/textual/naive.py,sha256=Z_gfbxI6cQGJ_raOTQic4fnpo493Xq3yEQ8qDV4xplo,6954
|
|
89
|
-
memos/memories/textual/tree.py,sha256=
|
|
90
|
+
memos/memories/textual/tree.py,sha256=xOc4yMO-tISHmndS-T3ZxGQDr3cARapiTU71PTZdJbQ,13514
|
|
90
91
|
memos/memories/textual/tree_text_memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
92
|
memos/memories/textual/tree_text_memory/organize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
92
|
-
memos/memories/textual/tree_text_memory/organize/
|
|
93
|
+
memos/memories/textual/tree_text_memory/organize/conflict.py,sha256=AIJ3pZLPkYsOZE_AqqnCykaGsCk00wLZ8dAdpaz29uE,8462
|
|
94
|
+
memos/memories/textual/tree_text_memory/organize/manager.py,sha256=ZT0VZnvR-Nd3P3f2eu6YumoauMUmp94NVP90JpI9C5w,13486
|
|
95
|
+
memos/memories/textual/tree_text_memory/organize/redundancy.py,sha256=Z88OaNs5YvFY3liXDRFqPcdiIVdTTdlJ_esx3oOiRxI,9075
|
|
96
|
+
memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py,sha256=gRG6iMT8Rm2Pk42UO1JzRaj3neIYmmOJmySi1knaGXA,8809
|
|
97
|
+
memos/memories/textual/tree_text_memory/organize/reorganizer.py,sha256=fy1P_9RWIiKBDayAfCviRZzJx48TMY09ynE0OenMX-o,22932
|
|
93
98
|
memos/memories/textual/tree_text_memory/retrieve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
99
|
memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py,sha256=asSzVcSVOiigN4-4NzFc7wzthGDK6P5sODIDF5W9egA,8467
|
|
95
100
|
memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py,sha256=pBlufTc5-mdWcyKR_R0Syev3QPMtV83mydyIw0Saz4o,3192
|
|
@@ -107,16 +112,17 @@ memos/parsers/factory.py,sha256=hPKTR0wVgMQ5Z9ZL-a9FyHWCpz9UcrG2oSEDYBAoB3g,704
|
|
|
107
112
|
memos/parsers/markitdown.py,sha256=Yh7YAv8PFsz3WgegrwtA_hqaC9W4SOMZP7IRXxcnn_w,615
|
|
108
113
|
memos/settings.py,sha256=HYNBFRW0CS2Kh4GmMRMA-Yrh3ZhJno4S4B6gx6P3T30,178
|
|
109
114
|
memos/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
110
|
-
memos/templates/mem_reader_prompts.py,sha256=
|
|
115
|
+
memos/templates/mem_reader_prompts.py,sha256=tEsrlcYifQoSTZ0JXZr8Oo_--7zo3TPKAFoaHnDHTtE,7701
|
|
111
116
|
memos/templates/mem_scheduler_prompts.py,sha256=53bcOCC32790tVJ7_s7o0jwu6hg7jUs-z8dyahlLv24,2417
|
|
112
117
|
memos/templates/mos_prompts.py,sha256=QJrMb4FwMOAVb-SFSCGTuqqzvD_YhBTpdVBvpUIftK0,3595
|
|
118
|
+
memos/templates/tree_reorganize_prompts.py,sha256=5nN5m8tlxxy46Xz4x9bGhzMPkywVFKDFqvSCjK3xnTA,6347
|
|
113
119
|
memos/types.py,sha256=N7XBYxDTdc50KEsS6YxHvYgs23ykGsZ-wNnaJBVdVi4,1791
|
|
114
120
|
memos/vec_dbs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
115
121
|
memos/vec_dbs/base.py,sha256=a_-Gsh51C1_8R35mK7DykDxQTicmdgyz4fbEw8d5Mlg,3165
|
|
116
122
|
memos/vec_dbs/factory.py,sha256=Noa4caqzPT9b59i2jzdpAHFCSHiMfDmgRox1POkRRfE,710
|
|
117
123
|
memos/vec_dbs/item.py,sha256=mLrcHF0nWtMCUjScBgaeeSqabQ3vJhKr_6wrU_g25ns,1425
|
|
118
124
|
memos/vec_dbs/qdrant.py,sha256=I_XFmz_7SgFojMTFaqkPvwQEdMBl0MsCKZ43qatwuV0,9858
|
|
119
|
-
memoryos-0.
|
|
120
|
-
memoryos-0.
|
|
121
|
-
memoryos-0.
|
|
122
|
-
memoryos-0.
|
|
125
|
+
memoryos-0.2.0.dist-info/LICENSE,sha256=FU-b6N8tVc7dzUZGyNjUIG1Ihnrh2iuBziq4a1Gl8HU,11358
|
|
126
|
+
memoryos-0.2.0.dist-info/METADATA,sha256=q0T0qM5v5Dkq9J5kKBtahtGWUHlSsu7R_dT3O_0Rb1A,12636
|
|
127
|
+
memoryos-0.2.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
128
|
+
memoryos-0.2.0.dist-info/RECORD,,
|
memos/__init__.py
CHANGED
memos/configs/llm.py
CHANGED
memos/configs/mem_reader.py
CHANGED
|
@@ -22,6 +22,10 @@ class BaseMemReaderConfig(BaseConfig):
|
|
|
22
22
|
chunker: ChunkerConfigFactory = Field(
|
|
23
23
|
..., description="Chunker configuration for the MemReader"
|
|
24
24
|
)
|
|
25
|
+
remove_prompt_example: bool = Field(
|
|
26
|
+
default=False,
|
|
27
|
+
description="whether remove example in memory extraction prompt to save token",
|
|
28
|
+
)
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
class SimpleStructMemReaderConfig(BaseMemReaderConfig):
|
memos/configs/memory.py
CHANGED
|
@@ -161,6 +161,11 @@ class TreeTextMemoryConfig(BaseTextMemoryConfig):
|
|
|
161
161
|
description="Internet retriever configuration (optional)",
|
|
162
162
|
)
|
|
163
163
|
|
|
164
|
+
reorganize: bool | None = Field(
|
|
165
|
+
False,
|
|
166
|
+
description="Optional description for this memory configuration.",
|
|
167
|
+
)
|
|
168
|
+
|
|
164
169
|
|
|
165
170
|
# ─── 3. Global Memory Config Factory ──────────────────────────────────────────
|
|
166
171
|
|
memos/graph_dbs/item.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
6
|
+
|
|
7
|
+
from memos.memories.textual.item import TextualMemoryItem
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GraphDBNode(TextualMemoryItem):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GraphDBEdge(BaseModel):
|
|
15
|
+
"""Represents an edge in a graph database (corresponds to Neo4j relationship)."""
|
|
16
|
+
|
|
17
|
+
id: str = Field(
|
|
18
|
+
default_factory=lambda: str(uuid.uuid4()), description="Unique identifier for the edge"
|
|
19
|
+
)
|
|
20
|
+
source: str = Field(..., description="Source node ID")
|
|
21
|
+
target: str = Field(..., description="Target node ID")
|
|
22
|
+
type: Literal["RELATED", "PARENT"] = Field(
|
|
23
|
+
..., description="Relationship type (must be one of 'RELATED', 'PARENT')"
|
|
24
|
+
)
|
|
25
|
+
properties: dict[str, Any] | None = Field(
|
|
26
|
+
default=None, description="Additional properties for the edge"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
model_config = ConfigDict(extra="forbid")
|
|
30
|
+
|
|
31
|
+
@field_validator("id")
|
|
32
|
+
@classmethod
|
|
33
|
+
def validate_id(cls, v):
|
|
34
|
+
"""Validate that ID is a valid UUID."""
|
|
35
|
+
if not isinstance(v, str) or not uuid.UUID(v, version=4):
|
|
36
|
+
raise ValueError("ID must be a valid UUID string")
|
|
37
|
+
return v
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_dict(cls, data: dict[str, Any]) -> "GraphDBEdge":
|
|
41
|
+
"""Create GraphDBEdge from dictionary."""
|
|
42
|
+
return cls(**data)
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> dict[str, Any]:
|
|
45
|
+
"""Convert to dictionary format."""
|
|
46
|
+
return self.model_dump(exclude_none=True)
|
memos/graph_dbs/neo4j.py
CHANGED
|
@@ -92,6 +92,16 @@ class Neo4jGraphDB(BaseGraphDB):
|
|
|
92
92
|
result = session.run(query, memory_type=memory_type)
|
|
93
93
|
return result.single()["count"]
|
|
94
94
|
|
|
95
|
+
def count_nodes(self, scope: str) -> int:
|
|
96
|
+
query = """
|
|
97
|
+
MATCH (n:Memory)
|
|
98
|
+
WHERE n.memory_type = $scope
|
|
99
|
+
RETURN count(n) AS count
|
|
100
|
+
"""
|
|
101
|
+
with self.driver.session(database=self.db_name) as session:
|
|
102
|
+
result = session.run(query, {"scope": scope}).single()
|
|
103
|
+
return result["count"]
|
|
104
|
+
|
|
95
105
|
def remove_oldest_memory(self, memory_type: str, keep_latest: int) -> None:
|
|
96
106
|
"""
|
|
97
107
|
Remove all WorkingMemory nodes except the latest `keep_latest` entries.
|
|
@@ -336,6 +346,49 @@ class Neo4jGraphDB(BaseGraphDB):
|
|
|
336
346
|
"""
|
|
337
347
|
raise NotImplementedError
|
|
338
348
|
|
|
349
|
+
def get_neighbors_by_tag(
|
|
350
|
+
self,
|
|
351
|
+
tags: list[str],
|
|
352
|
+
exclude_ids: list[str],
|
|
353
|
+
top_k: int = 5,
|
|
354
|
+
min_overlap: int = 1,
|
|
355
|
+
) -> list[dict[str, Any]]:
|
|
356
|
+
"""
|
|
357
|
+
Find top-K neighbor nodes with maximum tag overlap.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
tags: The list of tags to match.
|
|
361
|
+
exclude_ids: Node IDs to exclude (e.g., local cluster).
|
|
362
|
+
top_k: Max number of neighbors to return.
|
|
363
|
+
min_overlap: Minimum number of overlapping tags required.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
List of dicts with node details and overlap count.
|
|
367
|
+
"""
|
|
368
|
+
query = """
|
|
369
|
+
MATCH (n:Memory)
|
|
370
|
+
WHERE NOT n.id IN $exclude_ids
|
|
371
|
+
AND n.status = 'activated'
|
|
372
|
+
AND n.type <> 'reasoning'
|
|
373
|
+
AND n.memory_type <> 'WorkingMemory'
|
|
374
|
+
WITH n, [tag IN n.tags WHERE tag IN $tags] AS overlap_tags
|
|
375
|
+
WHERE size(overlap_tags) >= $min_overlap
|
|
376
|
+
RETURN n, size(overlap_tags) AS overlap_count
|
|
377
|
+
ORDER BY overlap_count DESC
|
|
378
|
+
LIMIT $top_k
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
params = {
|
|
382
|
+
"tags": tags,
|
|
383
|
+
"exclude_ids": exclude_ids,
|
|
384
|
+
"min_overlap": min_overlap,
|
|
385
|
+
"top_k": top_k,
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
with self.driver.session(database=self.db_name) as session:
|
|
389
|
+
result = session.run(query, params)
|
|
390
|
+
return [_parse_node(dict(record["n"])) for record in result]
|
|
391
|
+
|
|
339
392
|
def get_children_with_embeddings(self, id: str) -> list[str]:
|
|
340
393
|
query = """
|
|
341
394
|
MATCH (p:Memory)-[:PARENT]->(c:Memory)
|
|
@@ -386,14 +439,10 @@ class Neo4jGraphDB(BaseGraphDB):
|
|
|
386
439
|
record = session.run(query, {"center_id": center_id}).single()
|
|
387
440
|
|
|
388
441
|
if not record:
|
|
389
|
-
logger.warning(
|
|
390
|
-
f"No active node found for center_id={center_id} with status={center_status}"
|
|
391
|
-
)
|
|
392
442
|
return {"core_node": None, "neighbors": [], "edges": []}
|
|
393
443
|
|
|
394
444
|
centers = record["centers"]
|
|
395
445
|
if not centers or centers[0] is None:
|
|
396
|
-
logger.warning(f"Center node not found or inactive for id={center_id}")
|
|
397
446
|
return {"core_node": None, "neighbors": [], "edges": []}
|
|
398
447
|
|
|
399
448
|
core_node = _parse_node(dict(centers[0]))
|
|
@@ -730,6 +779,24 @@ class Neo4jGraphDB(BaseGraphDB):
|
|
|
730
779
|
results = session.run(query, {"scope": scope})
|
|
731
780
|
return [_parse_node(dict(record["n"])) for record in results]
|
|
732
781
|
|
|
782
|
+
def get_structure_optimization_candidates(self, scope: str) -> list[dict]:
|
|
783
|
+
"""
|
|
784
|
+
Find nodes that are likely candidates for structure optimization:
|
|
785
|
+
- Isolated nodes, nodes with empty background, or nodes with exactly one child.
|
|
786
|
+
- Plus: the child of any parent node that has exactly one child.
|
|
787
|
+
"""
|
|
788
|
+
query = """
|
|
789
|
+
MATCH (n:Memory)
|
|
790
|
+
WHERE n.memory_type = $scope
|
|
791
|
+
AND n.status = 'activated'
|
|
792
|
+
AND NOT ( (n)-[:PARENT]->() OR ()-[:PARENT]->(n) )
|
|
793
|
+
RETURN n.id AS id, n AS node
|
|
794
|
+
"""
|
|
795
|
+
|
|
796
|
+
with self.driver.session(database=self.db_name) as session:
|
|
797
|
+
results = session.run(query, {"scope": scope})
|
|
798
|
+
return [_parse_node({"id": record["id"], **dict(record["node"])}) for record in results]
|
|
799
|
+
|
|
733
800
|
def drop_database(self) -> None:
|
|
734
801
|
"""
|
|
735
802
|
Permanently delete the entire database this instance is using.
|
|
@@ -744,7 +811,7 @@ class Neo4jGraphDB(BaseGraphDB):
|
|
|
744
811
|
|
|
745
812
|
def _ensure_database_exists(self):
|
|
746
813
|
with self.driver.session(database="system") as session:
|
|
747
|
-
session.run(f"CREATE DATABASE
|
|
814
|
+
session.run(f"CREATE DATABASE $db_name IF NOT EXISTS", db_name=self.db_name)
|
|
748
815
|
|
|
749
816
|
# Wait until the database is available
|
|
750
817
|
for _ in range(10):
|
memos/llms/openai.py
CHANGED
|
@@ -22,6 +22,7 @@ class OpenAILLM(BaseLLM):
|
|
|
22
22
|
response = self.client.chat.completions.create(
|
|
23
23
|
model=self.config.model_name_or_path,
|
|
24
24
|
messages=messages,
|
|
25
|
+
extra_body=self.config.extra_body,
|
|
25
26
|
temperature=self.config.temperature,
|
|
26
27
|
max_tokens=self.config.max_tokens,
|
|
27
28
|
top_p=self.config.top_p,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import concurrent.futures
|
|
2
2
|
import copy
|
|
3
3
|
import json
|
|
4
|
-
|
|
4
|
+
import re
|
|
5
5
|
from abc import ABC
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
@@ -17,6 +17,7 @@ from memos.parsers.factory import ParserFactory
|
|
|
17
17
|
from memos.templates.mem_reader_prompts import (
|
|
18
18
|
SIMPLE_STRUCT_DOC_READER_PROMPT,
|
|
19
19
|
SIMPLE_STRUCT_MEM_READER_PROMPT,
|
|
20
|
+
SIMPLE_STRUCT_MEM_READER_EXAMPLE,
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
|
|
@@ -39,11 +40,11 @@ class SimpleStructMemReader(BaseMemReader, ABC):
|
|
|
39
40
|
self.chunker = ChunkerFactory.from_config(config.chunker)
|
|
40
41
|
|
|
41
42
|
def _process_chat_data(self, scene_data_info, info):
|
|
42
|
-
prompt = (
|
|
43
|
-
|
|
44
|
-
.replace("${user_b}", "assistant")
|
|
45
|
-
.replace("${conversation}", "\n".join(scene_data_info))
|
|
43
|
+
prompt = SIMPLE_STRUCT_MEM_READER_PROMPT.replace(
|
|
44
|
+
"${conversation}", "\n".join(scene_data_info)
|
|
46
45
|
)
|
|
46
|
+
if self.config.remove_prompt_example:
|
|
47
|
+
prompt = prompt.replace(SIMPLE_STRUCT_MEM_READER_EXAMPLE, "")
|
|
47
48
|
|
|
48
49
|
messages = [{"role": "user", "content": prompt}]
|
|
49
50
|
|
|
@@ -228,7 +229,11 @@ class SimpleStructMemReader(BaseMemReader, ABC):
|
|
|
228
229
|
|
|
229
230
|
def parse_json_result(self, response_text):
|
|
230
231
|
try:
|
|
231
|
-
|
|
232
|
+
json_start = response_text.find("{")
|
|
233
|
+
response_text = response_text[json_start:]
|
|
234
|
+
response_text = response_text.replace("```", "").strip()
|
|
235
|
+
if response_text[-1] != "}":
|
|
236
|
+
response_text += "}"
|
|
232
237
|
response_json = json.loads(response_text)
|
|
233
238
|
return response_json
|
|
234
239
|
except json.JSONDecodeError as e:
|
memos/memories/textual/item.py
CHANGED
|
@@ -27,7 +27,9 @@ class TextualMemoryMetadata(BaseModel):
|
|
|
27
27
|
default="activated",
|
|
28
28
|
description="The status of the memory, e.g., 'activated', 'archived', 'deleted'.",
|
|
29
29
|
)
|
|
30
|
-
type: Literal["procedure", "fact", "event", "opinion", "topic"] | None = Field(
|
|
30
|
+
type: Literal["procedure", "fact", "event", "opinion", "topic", "reasoning"] | None = Field(
|
|
31
|
+
default=None
|
|
32
|
+
)
|
|
31
33
|
memory_time: str | None = Field(
|
|
32
34
|
default=None,
|
|
33
35
|
description='The time the memory occurred or refers to. Must be in standard `YYYY-MM-DD` format. Relative expressions such as "yesterday" or "tomorrow" are not allowed.',
|
memos/memories/textual/tree.py
CHANGED
|
@@ -35,7 +35,11 @@ class TreeTextMemory(BaseTextMemory):
|
|
|
35
35
|
self.dispatcher_llm: OpenAILLM | OllamaLLM = LLMFactory.from_config(config.dispatcher_llm)
|
|
36
36
|
self.embedder: OllamaEmbedder = EmbedderFactory.from_config(config.embedder)
|
|
37
37
|
self.graph_store: Neo4jGraphDB = GraphStoreFactory.from_config(config.graph_db)
|
|
38
|
-
self.
|
|
38
|
+
self.is_reorganize = config.reorganize
|
|
39
|
+
|
|
40
|
+
self.memory_manager: MemoryManager = MemoryManager(
|
|
41
|
+
self.graph_store, self.embedder, self.extractor_llm, is_reorganize=self.is_reorganize
|
|
42
|
+
)
|
|
39
43
|
|
|
40
44
|
# Create internet retriever if configured
|
|
41
45
|
self.internet_retriever = None
|