MemoryOS 0.2.2__py3-none-any.whl → 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.
Potentially problematic release.
This version of MemoryOS might be problematic. Click here for more details.
- {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/METADATA +6 -1
- {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/RECORD +61 -55
- memos/__init__.py +1 -1
- memos/api/config.py +6 -8
- memos/api/context/context.py +1 -1
- memos/api/context/dependencies.py +11 -0
- memos/configs/internet_retriever.py +13 -0
- memos/configs/mem_scheduler.py +38 -16
- memos/graph_dbs/base.py +30 -3
- memos/graph_dbs/nebular.py +442 -194
- memos/graph_dbs/neo4j.py +14 -5
- memos/log.py +5 -0
- memos/mem_os/core.py +19 -9
- memos/mem_os/main.py +1 -1
- memos/mem_os/product.py +6 -69
- memos/mem_os/utils/default_config.py +1 -1
- memos/mem_os/utils/format_utils.py +11 -47
- memos/mem_os/utils/reference_utils.py +133 -0
- memos/mem_scheduler/base_scheduler.py +58 -55
- memos/mem_scheduler/{modules → general_modules}/base.py +1 -2
- memos/mem_scheduler/{modules → general_modules}/dispatcher.py +54 -15
- memos/mem_scheduler/{modules → general_modules}/rabbitmq_service.py +4 -4
- memos/mem_scheduler/{modules → general_modules}/redis_service.py +1 -1
- memos/mem_scheduler/{modules → general_modules}/retriever.py +19 -5
- memos/mem_scheduler/{modules → general_modules}/scheduler_logger.py +10 -4
- memos/mem_scheduler/general_scheduler.py +110 -67
- memos/mem_scheduler/monitors/__init__.py +0 -0
- memos/mem_scheduler/monitors/dispatcher_monitor.py +305 -0
- memos/mem_scheduler/{modules/monitor.py → monitors/general_monitor.py} +57 -19
- memos/mem_scheduler/mos_for_test_scheduler.py +7 -1
- memos/mem_scheduler/schemas/general_schemas.py +3 -2
- memos/mem_scheduler/schemas/message_schemas.py +2 -1
- memos/mem_scheduler/schemas/monitor_schemas.py +10 -2
- memos/mem_scheduler/utils/misc_utils.py +43 -2
- memos/memories/activation/item.py +1 -1
- memos/memories/activation/kv.py +20 -8
- memos/memories/textual/base.py +1 -1
- memos/memories/textual/general.py +1 -1
- memos/memories/textual/tree_text_memory/organize/{conflict.py → handler.py} +30 -48
- memos/memories/textual/tree_text_memory/organize/manager.py +8 -96
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +2 -0
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +102 -140
- memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +229 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +9 -0
- memos/memories/textual/tree_text_memory/retrieve/recall.py +15 -8
- memos/memories/textual/tree_text_memory/retrieve/reranker.py +1 -1
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +177 -125
- memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +7 -2
- memos/memories/textual/tree_text_memory/retrieve/utils.py +1 -1
- memos/memos_tools/lockfree_dict.py +120 -0
- memos/memos_tools/thread_safe_dict.py +288 -0
- memos/templates/mem_reader_prompts.py +2 -0
- memos/templates/mem_scheduler_prompts.py +23 -10
- memos/templates/mos_prompts.py +40 -11
- memos/templates/tree_reorganize_prompts.py +24 -17
- memos/utils.py +19 -0
- memos/memories/textual/tree_text_memory/organize/redundancy.py +0 -193
- {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/LICENSE +0 -0
- {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/WHEEL +0 -0
- {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/entry_points.txt +0 -0
- /memos/mem_scheduler/{modules → general_modules}/__init__.py +0 -0
- /memos/mem_scheduler/{modules → general_modules}/misc.py +0 -0
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
|
|
6
|
-
from memos.embedders.base import BaseEmbedder
|
|
7
|
-
from memos.graph_dbs.neo4j import Neo4jGraphDB
|
|
8
|
-
from memos.llms.base import BaseLLM
|
|
9
|
-
from memos.log import get_logger
|
|
10
|
-
from memos.memories.textual.item import TextualMemoryItem, TreeNodeTextualMemoryMetadata
|
|
11
|
-
from memos.templates.tree_reorganize_prompts import (
|
|
12
|
-
REDUNDANCY_DETECTOR_PROMPT,
|
|
13
|
-
REDUNDANCY_MERGE_PROMPT,
|
|
14
|
-
REDUNDANCY_RESOLVER_PROMPT,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
logger = get_logger(__name__)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class RedundancyHandler:
|
|
22
|
-
EMBEDDING_THRESHOLD: float = 0.8 # Threshold for embedding similarity to consider redundancy
|
|
23
|
-
|
|
24
|
-
def __init__(self, graph_store: Neo4jGraphDB, llm: BaseLLM, embedder: BaseEmbedder):
|
|
25
|
-
self.graph_store = graph_store
|
|
26
|
-
self.llm = llm
|
|
27
|
-
self.embedder = embedder
|
|
28
|
-
|
|
29
|
-
def detect(
|
|
30
|
-
self, memory: TextualMemoryItem, top_k: int = 5, scope: str | None = None
|
|
31
|
-
) -> list[tuple[TextualMemoryItem, TextualMemoryItem]]:
|
|
32
|
-
"""
|
|
33
|
-
Detect redundancy by finding the most similar items in the graph database based on embedding, then use LLM to judge redundancy.
|
|
34
|
-
Args:
|
|
35
|
-
memory: The memory item (should have an embedding attribute or field).
|
|
36
|
-
top_k: Number of top similar nodes to retrieve.
|
|
37
|
-
scope: Optional memory type filter.
|
|
38
|
-
Returns:
|
|
39
|
-
List of redundancy pairs (each pair is a tuple: (memory, candidate)).
|
|
40
|
-
"""
|
|
41
|
-
# 1. Search for similar memories based on embedding
|
|
42
|
-
embedding = memory.metadata.embedding
|
|
43
|
-
embedding_candidates_info = self.graph_store.search_by_embedding(
|
|
44
|
-
embedding, top_k=top_k, scope=scope
|
|
45
|
-
)
|
|
46
|
-
# 2. Filter based on similarity threshold
|
|
47
|
-
embedding_candidates_ids = [
|
|
48
|
-
info["id"]
|
|
49
|
-
for info in embedding_candidates_info
|
|
50
|
-
if info["score"] >= self.EMBEDDING_THRESHOLD and info["id"] != memory.id
|
|
51
|
-
]
|
|
52
|
-
# 3. Judge redundancys using LLM
|
|
53
|
-
embedding_candidates = self.graph_store.get_nodes(embedding_candidates_ids)
|
|
54
|
-
redundant_pairs = []
|
|
55
|
-
for embedding_candidate in embedding_candidates:
|
|
56
|
-
embedding_candidate = TextualMemoryItem.from_dict(embedding_candidate)
|
|
57
|
-
prompt = [
|
|
58
|
-
{
|
|
59
|
-
"role": "system",
|
|
60
|
-
"content": "You are a redundancy detector for memory items.",
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"role": "user",
|
|
64
|
-
"content": REDUNDANCY_DETECTOR_PROMPT.format(
|
|
65
|
-
statement_1=memory.memory,
|
|
66
|
-
statement_2=embedding_candidate.memory,
|
|
67
|
-
),
|
|
68
|
-
},
|
|
69
|
-
]
|
|
70
|
-
result = self.llm.generate(prompt).strip()
|
|
71
|
-
if "yes" in result.lower():
|
|
72
|
-
redundant_pairs.append([memory, embedding_candidate])
|
|
73
|
-
if len(redundant_pairs):
|
|
74
|
-
redundant_text = "\n".join(
|
|
75
|
-
f'"{pair[0].memory!s}" <==REDUNDANCY==> "{pair[1].memory!s}"'
|
|
76
|
-
for pair in redundant_pairs
|
|
77
|
-
)
|
|
78
|
-
logger.warning(
|
|
79
|
-
f"Detected {len(redundant_pairs)} redundancies for memory {memory.id}\n {redundant_text}"
|
|
80
|
-
)
|
|
81
|
-
return redundant_pairs
|
|
82
|
-
|
|
83
|
-
def resolve_two_nodes(self, memory_a: TextualMemoryItem, memory_b: TextualMemoryItem) -> None:
|
|
84
|
-
"""
|
|
85
|
-
Resolve detected redundancies between two memory items using LLM fusion.
|
|
86
|
-
Args:
|
|
87
|
-
memory_a: The first redundant memory item.
|
|
88
|
-
memory_b: The second redundant memory item.
|
|
89
|
-
Returns:
|
|
90
|
-
A fused TextualMemoryItem representing the resolved memory.
|
|
91
|
-
"""
|
|
92
|
-
return # waiting for implementation
|
|
93
|
-
# ———————————— 1. LLM generate fused memory ————————————
|
|
94
|
-
metadata_for_resolve = ["key", "background", "confidence", "updated_at"]
|
|
95
|
-
metadata_1 = memory_a.metadata.model_dump_json(include=metadata_for_resolve)
|
|
96
|
-
metadata_2 = memory_b.metadata.model_dump_json(include=metadata_for_resolve)
|
|
97
|
-
prompt = [
|
|
98
|
-
{
|
|
99
|
-
"role": "system",
|
|
100
|
-
"content": "",
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
"role": "user",
|
|
104
|
-
"content": REDUNDANCY_RESOLVER_PROMPT.format(
|
|
105
|
-
statement_1=memory_a.memory,
|
|
106
|
-
metadata_1=metadata_1,
|
|
107
|
-
statement_2=memory_b.memory,
|
|
108
|
-
metadata_2=metadata_2,
|
|
109
|
-
),
|
|
110
|
-
},
|
|
111
|
-
]
|
|
112
|
-
response = self.llm.generate(prompt).strip()
|
|
113
|
-
|
|
114
|
-
# ———————————— 2. Parse the response ————————————
|
|
115
|
-
try:
|
|
116
|
-
answer = re.search(r"<answer>(.*?)</answer>", response, re.DOTALL)
|
|
117
|
-
answer = answer.group(1).strip()
|
|
118
|
-
fixed_metadata = self._merge_metadata(answer, memory_a.metadata, memory_b.metadata)
|
|
119
|
-
merged_memory = TextualMemoryItem(memory=answer, metadata=fixed_metadata)
|
|
120
|
-
logger.info(f"Resolved result: {merged_memory}")
|
|
121
|
-
self._resolve_in_graph(memory_a, memory_b, merged_memory)
|
|
122
|
-
except json.decoder.JSONDecodeError:
|
|
123
|
-
logger.error(f"Failed to parse LLM response: {response}")
|
|
124
|
-
|
|
125
|
-
def resolve_one_node(self, memory: TextualMemoryItem) -> None:
|
|
126
|
-
prompt = [
|
|
127
|
-
{
|
|
128
|
-
"role": "user",
|
|
129
|
-
"content": REDUNDANCY_MERGE_PROMPT.format(merged_text=memory.memory),
|
|
130
|
-
},
|
|
131
|
-
]
|
|
132
|
-
response = self.llm.generate(prompt)
|
|
133
|
-
memory.memory = response.strip()
|
|
134
|
-
self.graph_store.update_node(
|
|
135
|
-
memory.id,
|
|
136
|
-
{"memory": memory.memory, **memory.metadata.model_dump(exclude_none=True)},
|
|
137
|
-
)
|
|
138
|
-
logger.debug(f"Merged memory: {memory.memory}")
|
|
139
|
-
|
|
140
|
-
def _resolve_in_graph(
|
|
141
|
-
self,
|
|
142
|
-
redundant_a: TextualMemoryItem,
|
|
143
|
-
redundant_b: TextualMemoryItem,
|
|
144
|
-
merged: TextualMemoryItem,
|
|
145
|
-
):
|
|
146
|
-
edges_a = self.graph_store.get_edges(redundant_a.id, type="ANY", direction="ANY")
|
|
147
|
-
edges_b = self.graph_store.get_edges(redundant_b.id, type="ANY", direction="ANY")
|
|
148
|
-
all_edges = edges_a + edges_b
|
|
149
|
-
|
|
150
|
-
self.graph_store.add_node(
|
|
151
|
-
merged.id, merged.memory, merged.metadata.model_dump(exclude_none=True)
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
for edge in all_edges:
|
|
155
|
-
new_from = (
|
|
156
|
-
merged.id if edge["from"] in (redundant_a.id, redundant_b.id) else edge["from"]
|
|
157
|
-
)
|
|
158
|
-
new_to = merged.id if edge["to"] in (redundant_a.id, redundant_b.id) else edge["to"]
|
|
159
|
-
if new_from == new_to:
|
|
160
|
-
continue
|
|
161
|
-
# Check if the edge already exists before adding
|
|
162
|
-
if not self.graph_store.edge_exists(new_from, new_to, edge["type"], direction="ANY"):
|
|
163
|
-
self.graph_store.add_edge(new_from, new_to, edge["type"])
|
|
164
|
-
|
|
165
|
-
self.graph_store.update_node(redundant_a.id, {"status": "archived"})
|
|
166
|
-
self.graph_store.update_node(redundant_b.id, {"status": "archived"})
|
|
167
|
-
self.graph_store.add_edge(redundant_a.id, merged.id, type="MERGED_TO")
|
|
168
|
-
self.graph_store.add_edge(redundant_b.id, merged.id, type="MERGED_TO")
|
|
169
|
-
logger.debug(
|
|
170
|
-
f"Archive {redundant_a.id} and {redundant_b.id}, and inherit their edges to {merged.id}."
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
def _merge_metadata(
|
|
174
|
-
self,
|
|
175
|
-
memory: str,
|
|
176
|
-
metadata_a: TreeNodeTextualMemoryMetadata,
|
|
177
|
-
metadata_b: TreeNodeTextualMemoryMetadata,
|
|
178
|
-
) -> TreeNodeTextualMemoryMetadata:
|
|
179
|
-
metadata_1 = metadata_a.model_dump()
|
|
180
|
-
metadata_2 = metadata_b.model_dump()
|
|
181
|
-
merged_metadata = {
|
|
182
|
-
"sources": (metadata_1["sources"] or []) + (metadata_2["sources"] or []),
|
|
183
|
-
"embedding": self.embedder.embed([memory])[0],
|
|
184
|
-
"update_at": datetime.now().isoformat(),
|
|
185
|
-
"created_at": datetime.now().isoformat(),
|
|
186
|
-
}
|
|
187
|
-
for key in metadata_1:
|
|
188
|
-
if key in merged_metadata:
|
|
189
|
-
continue
|
|
190
|
-
merged_metadata[key] = (
|
|
191
|
-
metadata_1[key] if metadata_1[key] is not None else metadata_2[key]
|
|
192
|
-
)
|
|
193
|
-
return TreeNodeTextualMemoryMetadata.model_validate(merged_metadata)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|