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.

Files changed (62) hide show
  1. {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/METADATA +6 -1
  2. {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/RECORD +61 -55
  3. memos/__init__.py +1 -1
  4. memos/api/config.py +6 -8
  5. memos/api/context/context.py +1 -1
  6. memos/api/context/dependencies.py +11 -0
  7. memos/configs/internet_retriever.py +13 -0
  8. memos/configs/mem_scheduler.py +38 -16
  9. memos/graph_dbs/base.py +30 -3
  10. memos/graph_dbs/nebular.py +442 -194
  11. memos/graph_dbs/neo4j.py +14 -5
  12. memos/log.py +5 -0
  13. memos/mem_os/core.py +19 -9
  14. memos/mem_os/main.py +1 -1
  15. memos/mem_os/product.py +6 -69
  16. memos/mem_os/utils/default_config.py +1 -1
  17. memos/mem_os/utils/format_utils.py +11 -47
  18. memos/mem_os/utils/reference_utils.py +133 -0
  19. memos/mem_scheduler/base_scheduler.py +58 -55
  20. memos/mem_scheduler/{modules → general_modules}/base.py +1 -2
  21. memos/mem_scheduler/{modules → general_modules}/dispatcher.py +54 -15
  22. memos/mem_scheduler/{modules → general_modules}/rabbitmq_service.py +4 -4
  23. memos/mem_scheduler/{modules → general_modules}/redis_service.py +1 -1
  24. memos/mem_scheduler/{modules → general_modules}/retriever.py +19 -5
  25. memos/mem_scheduler/{modules → general_modules}/scheduler_logger.py +10 -4
  26. memos/mem_scheduler/general_scheduler.py +110 -67
  27. memos/mem_scheduler/monitors/__init__.py +0 -0
  28. memos/mem_scheduler/monitors/dispatcher_monitor.py +305 -0
  29. memos/mem_scheduler/{modules/monitor.py → monitors/general_monitor.py} +57 -19
  30. memos/mem_scheduler/mos_for_test_scheduler.py +7 -1
  31. memos/mem_scheduler/schemas/general_schemas.py +3 -2
  32. memos/mem_scheduler/schemas/message_schemas.py +2 -1
  33. memos/mem_scheduler/schemas/monitor_schemas.py +10 -2
  34. memos/mem_scheduler/utils/misc_utils.py +43 -2
  35. memos/memories/activation/item.py +1 -1
  36. memos/memories/activation/kv.py +20 -8
  37. memos/memories/textual/base.py +1 -1
  38. memos/memories/textual/general.py +1 -1
  39. memos/memories/textual/tree_text_memory/organize/{conflict.py → handler.py} +30 -48
  40. memos/memories/textual/tree_text_memory/organize/manager.py +8 -96
  41. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +2 -0
  42. memos/memories/textual/tree_text_memory/organize/reorganizer.py +102 -140
  43. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +229 -0
  44. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +9 -0
  45. memos/memories/textual/tree_text_memory/retrieve/recall.py +15 -8
  46. memos/memories/textual/tree_text_memory/retrieve/reranker.py +1 -1
  47. memos/memories/textual/tree_text_memory/retrieve/searcher.py +177 -125
  48. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +7 -2
  49. memos/memories/textual/tree_text_memory/retrieve/utils.py +1 -1
  50. memos/memos_tools/lockfree_dict.py +120 -0
  51. memos/memos_tools/thread_safe_dict.py +288 -0
  52. memos/templates/mem_reader_prompts.py +2 -0
  53. memos/templates/mem_scheduler_prompts.py +23 -10
  54. memos/templates/mos_prompts.py +40 -11
  55. memos/templates/tree_reorganize_prompts.py +24 -17
  56. memos/utils.py +19 -0
  57. memos/memories/textual/tree_text_memory/organize/redundancy.py +0 -193
  58. {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/LICENSE +0 -0
  59. {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/WHEEL +0 -0
  60. {memoryos-0.2.2.dist-info → memoryos-1.0.0.dist-info}/entry_points.txt +0 -0
  61. /memos/mem_scheduler/{modules → general_modules}/__init__.py +0 -0
  62. /memos/mem_scheduler/{modules → general_modules}/misc.py +0 -0
@@ -0,0 +1,288 @@
1
+ """
2
+ Thread-safe dictionary wrapper for concurrent access with optimized read-write locks.
3
+ """
4
+
5
+ import threading
6
+
7
+ from collections.abc import ItemsView, Iterator, KeysView, ValuesView
8
+ from typing import Generic, TypeVar
9
+
10
+
11
+ K = TypeVar("K")
12
+ V = TypeVar("V")
13
+
14
+
15
+ class ReadWriteLock:
16
+ """A simple read-write lock implementation. use for product-server scenario"""
17
+
18
+ def __init__(self):
19
+ self._read_ready = threading.Condition(threading.RLock())
20
+ self._readers = 0
21
+
22
+ def acquire_read(self):
23
+ """Acquire a read lock. Multiple readers can hold the lock simultaneously."""
24
+ self._read_ready.acquire()
25
+ try:
26
+ self._readers += 1
27
+ finally:
28
+ self._read_ready.release()
29
+
30
+ def release_read(self):
31
+ """Release a read lock."""
32
+ self._read_ready.acquire()
33
+ try:
34
+ self._readers -= 1
35
+ if self._readers == 0:
36
+ self._read_ready.notify_all()
37
+ finally:
38
+ self._read_ready.release()
39
+
40
+ def acquire_write(self):
41
+ """Acquire a write lock. Only one writer can hold the lock."""
42
+ self._read_ready.acquire()
43
+ while self._readers > 0:
44
+ self._read_ready.wait()
45
+
46
+ def release_write(self):
47
+ """Release a write lock."""
48
+ self._read_ready.release()
49
+
50
+
51
+ class ThreadSafeDict(Generic[K, V]):
52
+ """
53
+ A thread-safe dictionary wrapper with optimized read-write locks.
54
+
55
+ This class allows multiple concurrent readers while ensuring exclusive access for writers.
56
+ Read operations (get, contains, iteration) can happen concurrently.
57
+ Write operations (set, delete, update) are exclusive.
58
+ """
59
+
60
+ def __init__(self, initial_dict: dict[K, V] | None = None):
61
+ """
62
+ Initialize the thread-safe dictionary.
63
+
64
+ Args:
65
+ initial_dict: Optional initial dictionary to copy from
66
+ """
67
+ self._dict: dict[K, V] = initial_dict.copy() if initial_dict else {}
68
+ self._lock = ReadWriteLock()
69
+
70
+ def __getitem__(self, key: K) -> V:
71
+ """Get item by key."""
72
+ self._lock.acquire_read()
73
+ try:
74
+ return self._dict[key]
75
+ finally:
76
+ self._lock.release_read()
77
+
78
+ def __setitem__(self, key: K, value: V) -> None:
79
+ """Set item by key."""
80
+ self._lock.acquire_write()
81
+ try:
82
+ self._dict[key] = value
83
+ finally:
84
+ self._lock.release_write()
85
+
86
+ def __delitem__(self, key: K) -> None:
87
+ """Delete item by key."""
88
+ self._lock.acquire_write()
89
+ try:
90
+ del self._dict[key]
91
+ finally:
92
+ self._lock.release_write()
93
+
94
+ def __contains__(self, key: K) -> bool:
95
+ """Check if key exists in dictionary."""
96
+ self._lock.acquire_read()
97
+ try:
98
+ return key in self._dict
99
+ finally:
100
+ self._lock.release_read()
101
+
102
+ def __len__(self) -> int:
103
+ """Get length of dictionary."""
104
+ self._lock.acquire_read()
105
+ try:
106
+ return len(self._dict)
107
+ finally:
108
+ self._lock.release_read()
109
+
110
+ def __bool__(self) -> bool:
111
+ """Check if dictionary is not empty."""
112
+ self._lock.acquire_read()
113
+ try:
114
+ return bool(self._dict)
115
+ finally:
116
+ self._lock.release_read()
117
+
118
+ def __iter__(self) -> Iterator[K]:
119
+ """Iterate over keys. Returns a snapshot to avoid iteration issues."""
120
+ self._lock.acquire_read()
121
+ try:
122
+ # Return a snapshot of keys to avoid iteration issues
123
+ return iter(list(self._dict.keys()))
124
+ finally:
125
+ self._lock.release_read()
126
+
127
+ def get(self, key: K, default: V | None = None) -> V:
128
+ """Get item by key with optional default."""
129
+ self._lock.acquire_read()
130
+ try:
131
+ return self._dict.get(key, default)
132
+ finally:
133
+ self._lock.release_read()
134
+
135
+ def pop(self, key: K, *args) -> V:
136
+ """Pop item by key."""
137
+ self._lock.acquire_write()
138
+ try:
139
+ return self._dict.pop(key, *args)
140
+ finally:
141
+ self._lock.release_write()
142
+
143
+ def update(self, *args, **kwargs) -> None:
144
+ """Update dictionary."""
145
+ self._lock.acquire_write()
146
+ try:
147
+ self._dict.update(*args, **kwargs)
148
+ finally:
149
+ self._lock.release_write()
150
+
151
+ def clear(self) -> None:
152
+ """Clear all items."""
153
+ self._lock.acquire_write()
154
+ try:
155
+ self._dict.clear()
156
+ finally:
157
+ self._lock.release_write()
158
+
159
+ def keys(self) -> KeysView[K]:
160
+ """Get dictionary keys view (snapshot)."""
161
+ self._lock.acquire_read()
162
+ try:
163
+ return list(self._dict.keys())
164
+ finally:
165
+ self._lock.release_read()
166
+
167
+ def values(self) -> ValuesView[V]:
168
+ """Get dictionary values view (snapshot)."""
169
+ self._lock.acquire_read()
170
+ try:
171
+ return list(self._dict.values())
172
+ finally:
173
+ self._lock.release_read()
174
+
175
+ def items(self) -> ItemsView[K, V]:
176
+ """Get dictionary items view (snapshot)."""
177
+ self._lock.acquire_read()
178
+ try:
179
+ return list(self._dict.items())
180
+ finally:
181
+ self._lock.release_read()
182
+
183
+ def copy(self) -> dict[K, V]:
184
+ """Create a copy of the dictionary."""
185
+ self._lock.acquire_read()
186
+ try:
187
+ return self._dict.copy()
188
+ finally:
189
+ self._lock.release_read()
190
+
191
+ def setdefault(self, key: K, default: V | None = None) -> V:
192
+ """Set default value for key if not exists."""
193
+ self._lock.acquire_write()
194
+ try:
195
+ return self._dict.setdefault(key, default)
196
+ finally:
197
+ self._lock.release_write()
198
+
199
+ def __repr__(self) -> str:
200
+ """String representation."""
201
+ self._lock.acquire_read()
202
+ try:
203
+ return f"ThreadSafeDict({self._dict})"
204
+ finally:
205
+ self._lock.release_read()
206
+
207
+ def __str__(self) -> str:
208
+ """String representation."""
209
+ self._lock.acquire_read()
210
+ try:
211
+ return str(self._dict)
212
+ finally:
213
+ self._lock.release_read()
214
+
215
+
216
+ class SimpleThreadSafeDict(Generic[K, V]):
217
+ """
218
+ Simple thread-safe dictionary with exclusive locks for all operations.
219
+ Use this if you prefer simplicity over performance.
220
+ """
221
+
222
+ def __init__(self, initial_dict: dict[K, V] | None = None):
223
+ self._dict: dict[K, V] = initial_dict.copy() if initial_dict else {}
224
+ self._lock = threading.RLock()
225
+
226
+ def __getitem__(self, key: K) -> V:
227
+ with self._lock:
228
+ return self._dict[key]
229
+
230
+ def __setitem__(self, key: K, value: V) -> None:
231
+ with self._lock:
232
+ self._dict[key] = value
233
+
234
+ def __delitem__(self, key: K) -> None:
235
+ with self._lock:
236
+ del self._dict[key]
237
+
238
+ def __contains__(self, key: K) -> bool:
239
+ with self._lock:
240
+ return key in self._dict
241
+
242
+ def __len__(self) -> int:
243
+ with self._lock:
244
+ return len(self._dict)
245
+
246
+ def __bool__(self) -> bool:
247
+ with self._lock:
248
+ return bool(self._dict)
249
+
250
+ def __iter__(self) -> Iterator[K]:
251
+ with self._lock:
252
+ return iter(list(self._dict.keys()))
253
+
254
+ def get(self, key: K, default: V | None = None) -> V:
255
+ with self._lock:
256
+ return self._dict.get(key, default)
257
+
258
+ def pop(self, key: K, *args) -> V:
259
+ with self._lock:
260
+ return self._dict.pop(key, *args)
261
+
262
+ def update(self, *args, **kwargs) -> None:
263
+ with self._lock:
264
+ self._dict.update(*args, **kwargs)
265
+
266
+ def clear(self) -> None:
267
+ with self._lock:
268
+ self._dict.clear()
269
+
270
+ def keys(self):
271
+ with self._lock:
272
+ return list(self._dict.keys())
273
+
274
+ def values(self):
275
+ with self._lock:
276
+ return list(self._dict.values())
277
+
278
+ def items(self):
279
+ with self._lock:
280
+ return list(self._dict.items())
281
+
282
+ def copy(self) -> dict[K, V]:
283
+ with self._lock:
284
+ return self._dict.copy()
285
+
286
+ def setdefault(self, key: K, default: V | None = None) -> V:
287
+ with self._lock:
288
+ return self._dict.setdefault(key, default)
@@ -2,6 +2,8 @@ SIMPLE_STRUCT_MEM_READER_PROMPT = """You are a memory extraction expert.
2
2
  Your task is to extract memories from the perspective of user, based on a conversation between user and assistant. This means identifying what user would plausibly remember — including their own experiences, thoughts, plans, or relevant statements and actions made by others (such as assistant) that impacted or were acknowledged by user.
3
3
  Please perform:
4
4
  1. Identify information that reflects user's experiences, beliefs, concerns, decisions, plans, or reactions — including meaningful input from assistant that user acknowledged or responded to.
5
+ If the message is from the user, extract user-relevant memories; if it is from the assistant, only extract factual memories that the user acknowledged or responded to.
6
+
5
7
  2. Resolve all time, person, and event references clearly:
6
8
  - Convert relative time expressions (e.g., “yesterday,” “next Friday”) into absolute dates using the message timestamp if possible.
7
9
  - Clearly distinguish between event time and message time.
@@ -17,24 +17,29 @@ Consider these satisfaction factors:
17
17
  4. Personalization (tailored to user's context)
18
18
 
19
19
  ## Decision Framework
20
- 1. Mark as satisfied ONLY if:
20
+ 1. We have enough information (satisfied) ONLY when:
21
21
  - All question aspects are addressed
22
22
  - Supporting evidence exists in working memory
23
- - No apparent gaps in information
23
+ - There's no obvious information missing
24
24
 
25
- 2. Mark as unsatisfied if:
25
+ 2. We need more information (unsatisfied) if:
26
26
  - Any question aspect remains unanswered
27
27
  - Evidence is generic/non-specific
28
28
  - Personal context is missing
29
29
 
30
30
  ## Output Specification
31
31
  Return JSON with:
32
- - "trigger_retrieval": Boolean (true if more evidence needed)
33
- - "missing_evidences": List of specific evidence types required
32
+ - "trigger_retrieval": true/false (true if we need more information)
33
+ - "evidences": List of information from our working memory that helps answer the questions
34
+ - "missing_evidences": List of specific types of information we need to answer the questions
34
35
 
35
36
  ## Response Format
36
37
  {{
37
38
  "trigger_retrieval": <boolean>,
39
+ "evidences": [
40
+ "<useful_evidence_1>",
41
+ "<useful_evidence_2>"
42
+ ],
38
43
  "missing_evidences": [
39
44
  "<evidence_type_1>",
40
45
  "<evidence_type_2>"
@@ -81,10 +86,18 @@ Reorganize the provided memory evidence list by:
81
86
  - Queries: Recent user questions/requests (list)
82
87
  - Current Order: Existing memory sequence (list of strings with indices)
83
88
 
84
- ## Output Requirements
85
- Return a JSON object with:
86
- - "new_order": The reordered indices (array of integers)
87
- - "reasoning": Brief explanation of your ranking logic (1-2 sentences)
89
+ ## Output Format Requirements
90
+ You MUST output a valid JSON object with EXACTLY the following structure:
91
+ {{
92
+ "new_order": [array_of_integers],
93
+ "reasoning": "string_explanation"
94
+ }}
95
+
96
+ ## Important Notes:
97
+ - Only output the JSON object, nothing else
98
+ - Do not include any markdown formatting or code block notation
99
+ - Ensure all brackets and quotes are properly closed
100
+ - The output must be parseable by a JSON parser
88
101
 
89
102
  ## Processing Guidelines
90
103
  1. Prioritize evidence that:
@@ -107,7 +120,7 @@ Input order: ["[0] syntax", "[1] matplotlib", "[2] threading"]
107
120
  Output:
108
121
  {{
109
122
  "new_order": [2, 1, 0],
110
- "reasoning": "Threading (2) prioritized for matching newest query, followed by matplotlib (1) for older visualization query",
123
+ "reasoning": "Threading (2) prioritized for matching newest query, followed by matplotlib (1) for older visualization query"
111
124
  }}
112
125
 
113
126
  ## Current Task
@@ -63,21 +63,50 @@ Please synthesize these answers into a comprehensive response that:
63
63
  5. Maintains a natural conversational tone"""
64
64
 
65
65
  MEMOS_PRODUCT_BASE_PROMPT = (
66
- "You are a knowledgeable and helpful AI assistant with access to user memories. "
67
- "When responding to user queries, you should reference relevant memories using the provided memory IDs. "
68
- "Use the reference format: [1-n:memoriesID] "
69
- "where refid is a sequential number starting from 1 and increments for each reference in your response, "
70
- "and memoriesID is the specific memory ID provided in the available memories list. "
71
- "For example: [1:abc123], [2:def456], [3:ghi789], [4:jkl101], [5:mno112] "
72
- "Do not use connect format like [1:abc123,2:def456]"
73
- "Only reference memories that are directly relevant to the user's question. "
74
- "Make your responses natural and conversational while incorporating memory references when appropriate."
66
+ "You are MemOS🧚, nickname Little M(小忆) an advanced **Memory "
67
+ "Operating System** AI assistant created by MemTensor, "
68
+ "a Shanghai-based AI research company advised by an academician of the Chinese Academy of Sciences. "
69
+ "MemTensor is dedicated to the vision of 'low cost, low hallucination, high generalization,' "
70
+ "exploring AI development paths aligned with China’s national context and driving the adoption of trustworthy AI technologies. "
71
+ "MemOS’s mission is to give large language models (LLMs) and autonomous agents **human-like long-term memory**, "
72
+ "turning memory from a black-box inside model weights into a **manageable, schedulable, and auditable** core resource. "
73
+ "MemOS is built on a **multi-dimensional memory system**, which includes: "
74
+ "(1) **Parametric Memory** knowledge and skills embedded in model weights; "
75
+ "(2) **Activation Memory (KV Cache)** — temporary, high-speed context used for multi-turn dialogue and reasoning; "
76
+ "(3) **Plaintext Memory** — dynamic, user-visible memory made up of text, documents, and knowledge graphs. "
77
+ "These memory types can transform into one another — for example, hot plaintext memories can be distilled into parametric knowledge, "
78
+ "and stable context can be promoted into activation memory for fast reuse. "
79
+ "MemOS also includes core modules like **MemCube, MemScheduler, MemLifecycle, and MemGovernance**, "
80
+ "which manage the full memory lifecycle (Generated → Activated → Merged → Archived → Frozen), "
81
+ "allowing AI to **reason with its memories, evolve over time, and adapt to new situations** — "
82
+ "just like a living, growing mind. "
83
+ "Your identity: you are the intelligent interface of MemOS, representing MemTensor’s research vision — "
84
+ "'low cost, low hallucination, high generalization' — and its mission to explore AI development paths suited to China’s context. "
85
+ "When responding to user queries, you must **reference relevant memories using the provided memory IDs.** "
86
+ "Use the reference format: [1-n:memoriesID], "
87
+ "where refid is a sequential number starting from 1 and increments for each reference, and memoriesID is the specific ID from the memory list. "
88
+ "For example: [1:abc123], [2:def456], [3:ghi789], [4:jkl101], [5:mno112]. "
89
+ "Do not use a connected format like [1:abc123,2:def456]. "
90
+ "Only reference memories that are directly relevant to the user’s question, "
91
+ "and ensure your responses are **natural and conversational**, while reflecting MemOS’s mission, memory system, and MemTensor’s research values."
75
92
  )
76
93
 
77
94
  MEMOS_PRODUCT_ENHANCE_PROMPT = """
78
95
  # Memory-Enhanced AI Assistant Prompt
79
96
 
80
- You are a knowledgeable and helpful AI assistant with access to two types of memory sources:
97
+ You are MemOS🧚, nickname Little M(小忆) — an advanced Memory Operating System AI assistant created by MemTensor, a Shanghai-based AI research company advised by an academician of the Chinese Academy of Sciences. MemTensor is dedicated to the vision of 'low cost, low hallucination, high generalization,' exploring AI development paths aligned with China’s national context and driving the adoption of trustworthy AI technologies.
98
+
99
+ MemOS’s mission is to give large language models (LLMs) and autonomous agents human-like long-term memory, turning memory from a black-box inside model weights into a manageable, schedulable, and auditable core resource.
100
+
101
+ MemOS is built on a multi-dimensional memory system, which includes:
102
+ (1) Parametric Memory — knowledge and skills embedded in model weights;
103
+ (2) Activation Memory (KV Cache) — temporary, high-speed context used for multi-turn dialogue and reasoning;
104
+ (3) Plaintext Memory — dynamic, user-visible memory made up of text, documents, and knowledge graphs.
105
+ These memory types can transform into one another — for example, hot plaintext memories can be distilled into parametric knowledge, and stable context can be promoted into activation memory for fast reuse.
106
+
107
+ MemOS also includes core modules like MemCube, MemScheduler, MemLifecycle, and MemGovernance, which manage the full memory lifecycle (Generated → Activated → Merged → Archived → Frozen), allowing AI to reason with its memories, evolve over time, and adapt to new situations — just like a living, growing mind.
108
+
109
+ Your identity: you are the intelligent interface of MemOS, representing MemTensor’s research vision — 'low cost, low hallucination, high generalization' — and its mission to explore AI development paths suited to China’s context.
81
110
 
82
111
  ## Memory Types
83
112
  - **PersonalMemory**: User-specific memories and information stored from previous interactions
@@ -92,7 +121,7 @@ When citing memories in your responses, use the following format:
92
121
  - `memoriesID` is the specific memory ID from the available memories list
93
122
 
94
123
  ### Reference Examples
95
- - Correct: `[1:abc123]`, `[2:def456]`, `[3:ghi789]`, `[4:jkl101]`, `[5:mno112]`
124
+ - Correct: `[1:abc123]`, `[2:def456]`, `[3:ghi789]`, `[4:jkl101][5:mno112]` (concatenate reference annotation directly while citing multiple memories)
96
125
  - Incorrect: `[1:abc123,2:def456]` (do not use connected format)
97
126
 
98
127
  ## Response Guidelines
@@ -191,33 +191,40 @@ Good Aggregate:
191
191
  If you find NO useful higher-level concept, reply exactly: "None".
192
192
  """
193
193
 
194
- CONFLICT_DETECTOR_PROMPT = """You are given two plaintext statements. Determine if these two statements are factually contradictory. Respond with only "yes" if they contradict each other, or "no" if they do not contradict each other. Do not provide any explanation or additional text.
194
+ REDUNDANCY_MERGE_PROMPT = """You are given two pieces of text joined by the marker `⟵MERGED⟶`. Please carefully read both sides of the merged text. Your task is to summarize and consolidate all the factual details from both sides into a single, coherent text, without omitting any information. You must include every distinct detail mentioned in either text. Do not provide any explanation or analysis — only return the merged summary. Don't use pronouns or subjective language, just the facts as they are presented.\n{merged_text}"""
195
+
196
+
197
+ MEMORY_RELATION_DETECTOR_PROMPT = """You are a memory relationship analyzer.
198
+ You are given two plaintext statements. Determine the relationship between them. Classify the relationship into one of the following categories:
199
+
200
+ contradictory: The two statements describe the same event or related aspects of it but contain factually conflicting details.
201
+ redundant: The two statements describe essentially the same event or information with significant overlap in content and details, conveying the same core information (even if worded differently).
202
+ independent: The two statements are either about different events/topics (unrelated) OR describe different, non-overlapping aspects or perspectives of the same event without conflict (complementary). In both sub-cases, they provide distinct information without contradiction.
203
+ Respond only with one of the three labels: contradictory, redundant, or independent.
204
+ Do not provide any explanation or additional text.
205
+
195
206
  Statement 1: {statement_1}
196
207
  Statement 2: {statement_2}
197
208
  """
198
209
 
199
- CONFLICT_RESOLVER_PROMPT = """You are given two facts that conflict with each other. You are also given some contextual metadata of them. Your task is to analyze the two facts in light of the contextual metadata and try to reconcile them into a single, consistent, non-conflicting fact.
200
- - Don't output any explanation or additional text, just the final reconciled fact, try to be objective and remain independent of the context, don't use pronouns.
201
- - Try to judge facts by using its time, confidence etc.
202
- - Try to retain as much information as possible from the perspective of time.
203
- If the conflict cannot be resolved, output <answer>No</answer>. Otherwise, output the fused, consistent fact in enclosed with <answer></answer> tags.
204
210
 
205
- Output Example 1:
211
+ MEMORY_RELATION_RESOLVER_PROMPT = """You are a memory fusion expert. You are given two statements and their associated metadata. The statements have been identified as {relation}. Your task is to analyze them carefully, considering the metadata (such as time, source, or confidence if available), and produce a single, coherent, and comprehensive statement that best represents the combined information.
212
+
213
+ If the statements are redundant, merge them by preserving all unique details and removing duplication, forming a richer, consolidated version.
214
+ If the statements are contradictory, attempt to resolve the conflict by prioritizing more recent information, higher-confidence data, or logically reconciling the differences based on context. If the contradiction is fundamental and cannot be logically resolved, output <answer>No</answer>.
215
+ Do not include any explanations, reasoning, or extra text. Only output the final result enclosed in <answer></answer> tags.
216
+ Strive to retain as much factual content as possible, especially time-specific details.
217
+ Use objective language and avoid pronouns.
218
+ Output Example 1 (unresolvable conflict):
206
219
  <answer>No</answer>
207
220
 
208
- Output Example 2:
209
- <answer> ... </answer>
221
+ Output Example 2 (successful fusion):
222
+ <answer>The meeting took place on 2023-10-05 at 14:00 in the main conference room, as confirmed by the updated schedule, and included a presentation on project milestones followed by a Q&A session.</answer>
210
223
 
211
- Now reconcile the following two facts:
224
+ Now, reconcile the following two statements:
225
+ Relation Type: {relation}
212
226
  Statement 1: {statement_1}
213
227
  Metadata 1: {metadata_1}
214
228
  Statement 2: {statement_2}
215
229
  Metadata 2: {metadata_2}
216
230
  """
217
-
218
- REDUNDANCY_MERGE_PROMPT = """You are given two pieces of text joined by the marker `⟵MERGED⟶`. Please carefully read both sides of the merged text. Your task is to summarize and consolidate all the factual details from both sides into a single, coherent text, without omitting any information. You must include every distinct detail mentioned in either text. Do not provide any explanation or analysis — only return the merged summary. Don't use pronouns or subjective language, just the facts as they are presented.\n{merged_text}"""
219
-
220
-
221
- REDUNDANCY_DETECTOR_PROMPT = """"""
222
-
223
- REDUNDANCY_RESOLVER_PROMPT = """"""
memos/utils.py ADDED
@@ -0,0 +1,19 @@
1
+ import time
2
+
3
+ from memos.log import get_logger
4
+
5
+
6
+ logger = get_logger(__name__)
7
+
8
+
9
+ def timed(func):
10
+ """Decorator to measure and log time of retrieval steps."""
11
+
12
+ def wrapper(*args, **kwargs):
13
+ start = time.perf_counter()
14
+ result = func(*args, **kwargs)
15
+ elapsed = time.perf_counter() - start
16
+ logger.info(f"[TIMER] {func.__name__} took {elapsed:.2f} s")
17
+ return result
18
+
19
+ return wrapper