MemoryOS 0.2.1__py3-none-any.whl → 0.2.2__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 (74) hide show
  1. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/METADATA +2 -1
  2. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/RECORD +72 -55
  3. memos/__init__.py +1 -1
  4. memos/api/config.py +156 -65
  5. memos/api/context/context.py +147 -0
  6. memos/api/context/dependencies.py +90 -0
  7. memos/api/product_models.py +5 -1
  8. memos/api/routers/product_router.py +54 -26
  9. memos/configs/graph_db.py +49 -1
  10. memos/configs/internet_retriever.py +6 -0
  11. memos/configs/mem_os.py +5 -0
  12. memos/configs/mem_reader.py +9 -0
  13. memos/configs/mem_scheduler.py +18 -4
  14. memos/configs/mem_user.py +58 -0
  15. memos/graph_dbs/base.py +9 -1
  16. memos/graph_dbs/factory.py +2 -0
  17. memos/graph_dbs/nebular.py +1364 -0
  18. memos/graph_dbs/neo4j.py +4 -4
  19. memos/log.py +1 -1
  20. memos/mem_cube/utils.py +13 -6
  21. memos/mem_os/core.py +140 -30
  22. memos/mem_os/main.py +1 -1
  23. memos/mem_os/product.py +266 -152
  24. memos/mem_os/utils/format_utils.py +314 -67
  25. memos/mem_reader/simple_struct.py +13 -5
  26. memos/mem_scheduler/base_scheduler.py +220 -250
  27. memos/mem_scheduler/general_scheduler.py +193 -73
  28. memos/mem_scheduler/modules/base.py +5 -5
  29. memos/mem_scheduler/modules/dispatcher.py +6 -9
  30. memos/mem_scheduler/modules/misc.py +81 -16
  31. memos/mem_scheduler/modules/monitor.py +52 -41
  32. memos/mem_scheduler/modules/rabbitmq_service.py +9 -7
  33. memos/mem_scheduler/modules/retriever.py +108 -191
  34. memos/mem_scheduler/modules/scheduler_logger.py +255 -0
  35. memos/mem_scheduler/mos_for_test_scheduler.py +16 -19
  36. memos/mem_scheduler/schemas/__init__.py +0 -0
  37. memos/mem_scheduler/schemas/general_schemas.py +43 -0
  38. memos/mem_scheduler/schemas/message_schemas.py +148 -0
  39. memos/mem_scheduler/schemas/monitor_schemas.py +329 -0
  40. memos/mem_scheduler/utils/__init__.py +0 -0
  41. memos/mem_scheduler/utils/filter_utils.py +176 -0
  42. memos/mem_scheduler/utils/misc_utils.py +61 -0
  43. memos/mem_user/factory.py +94 -0
  44. memos/mem_user/mysql_persistent_user_manager.py +271 -0
  45. memos/mem_user/mysql_user_manager.py +500 -0
  46. memos/mem_user/persistent_factory.py +96 -0
  47. memos/mem_user/user_manager.py +4 -4
  48. memos/memories/activation/item.py +4 -0
  49. memos/memories/textual/base.py +1 -1
  50. memos/memories/textual/general.py +35 -91
  51. memos/memories/textual/item.py +5 -33
  52. memos/memories/textual/tree.py +13 -7
  53. memos/memories/textual/tree_text_memory/organize/conflict.py +4 -2
  54. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +47 -43
  55. memos/memories/textual/tree_text_memory/organize/reorganizer.py +8 -5
  56. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
  57. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
  58. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
  59. memos/memories/textual/tree_text_memory/retrieve/searcher.py +46 -23
  60. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +42 -15
  61. memos/memories/textual/tree_text_memory/retrieve/utils.py +11 -7
  62. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +62 -58
  63. memos/memos_tools/dinding_report_bot.py +422 -0
  64. memos/memos_tools/notification_service.py +44 -0
  65. memos/memos_tools/notification_utils.py +96 -0
  66. memos/settings.py +3 -1
  67. memos/templates/mem_reader_prompts.py +2 -1
  68. memos/templates/mem_scheduler_prompts.py +41 -7
  69. memos/templates/mos_prompts.py +87 -0
  70. memos/mem_scheduler/modules/schemas.py +0 -328
  71. memos/mem_scheduler/utils.py +0 -75
  72. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/LICENSE +0 -0
  73. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/WHEEL +0 -0
  74. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,5 @@
1
- import json
1
+ import logging
2
+ import traceback
2
3
 
3
4
  from string import Template
4
5
 
@@ -14,11 +15,16 @@ class TaskGoalParser:
14
15
  - mode == 'fine': use LLM to parse structured topic/keys/tags
15
16
  """
16
17
 
17
- def __init__(self, llm=BaseLLM, mode: str = "fast"):
18
+ def __init__(self, llm=BaseLLM):
18
19
  self.llm = llm
19
- self.mode = mode
20
20
 
21
- def parse(self, task_description: str, context: str = "") -> ParsedTaskGoal:
21
+ def parse(
22
+ self,
23
+ task_description: str,
24
+ context: str = "",
25
+ conversation: list[dict] | None = None,
26
+ mode: str = "fast",
27
+ ) -> ParsedTaskGoal:
22
28
  """
23
29
  Parse user input into structured semantic layers.
24
30
  Returns:
@@ -26,42 +32,63 @@ class TaskGoalParser:
26
32
  - mode == 'fast': use jieba to split words only
27
33
  - mode == 'fine': use LLM to parse structured topic/keys/tags
28
34
  """
29
- if self.mode == "fast":
35
+ if mode == "fast":
30
36
  return self._parse_fast(task_description)
31
- elif self.mode == "fine":
37
+ elif mode == "fine":
32
38
  if not self.llm:
33
39
  raise ValueError("LLM not provided for slow mode.")
34
- return self._parse_fine(task_description, context)
40
+ return self._parse_fine(task_description, context, conversation)
35
41
  else:
36
- raise ValueError(f"Unknown mode: {self.mode}")
42
+ raise ValueError(f"Unknown mode: {mode}")
37
43
 
38
44
  def _parse_fast(self, task_description: str, limit_num: int = 5) -> ParsedTaskGoal:
39
45
  """
40
46
  Fast mode: simple jieba word split.
41
47
  """
42
48
  return ParsedTaskGoal(
43
- memories=[task_description], keys=[task_description], tags=[], goal_type="default"
49
+ memories=[task_description],
50
+ keys=[task_description],
51
+ tags=[],
52
+ goal_type="default",
53
+ rephrased_query=task_description,
54
+ internet_search=False,
44
55
  )
45
56
 
46
- def _parse_fine(self, query: str, context: str = "") -> ParsedTaskGoal:
57
+ def _parse_fine(
58
+ self, query: str, context: str = "", conversation: list[dict] | None = None
59
+ ) -> ParsedTaskGoal:
47
60
  """
48
61
  Slow mode: LLM structured parse.
49
62
  """
50
- prompt = Template(TASK_PARSE_PROMPT).substitute(task=query.strip(), context=context)
51
- response = self.llm.generate(messages=[{"role": "user", "content": prompt}])
52
- return self._parse_response(response)
63
+ try:
64
+ if conversation:
65
+ conversation_prompt = "\n".join(
66
+ [f"{each['role']}: {each['content']}" for each in conversation]
67
+ )
68
+ else:
69
+ conversation_prompt = ""
70
+ prompt = Template(TASK_PARSE_PROMPT).substitute(
71
+ task=query.strip(), context=context, conversation=conversation_prompt
72
+ )
73
+ response = self.llm.generate(messages=[{"role": "user", "content": prompt}])
74
+ return self._parse_response(response)
75
+ except Exception:
76
+ logging.warning(f"Fail to fine-parse query {query}: {traceback.format_exc()}")
77
+ return self._parse_fast(query)
53
78
 
54
79
  def _parse_response(self, response: str) -> ParsedTaskGoal:
55
80
  """
56
81
  Parse LLM JSON output safely.
57
82
  """
58
83
  try:
59
- response = response.replace("```", "").replace("json", "")
60
- response_json = json.loads(response.strip())
84
+ response = response.replace("```", "").replace("json", "").strip()
85
+ response_json = eval(response)
61
86
  return ParsedTaskGoal(
62
87
  memories=response_json.get("memories", []),
63
88
  keys=response_json.get("keys", []),
64
89
  tags=response_json.get("tags", []),
90
+ rephrased_query=response_json.get("rephrased_instruction", None),
91
+ internet_search=response_json.get("internet_search", False),
65
92
  goal_type=response_json.get("goal_type", "default"),
66
93
  )
67
94
  except Exception as e:
@@ -1,19 +1,21 @@
1
1
  # Prompt for task parsing
2
2
  TASK_PARSE_PROMPT = """
3
- You are a task parsing expert. Given a user's task instruction, extract the following structured information:
4
-
5
- Given a user task instruction and optional related memory context,
6
- extract the following structured information:
3
+ You are a task parsing expert. Given a user task instruction, optional former conversation and optional related memory context,extract the following structured information:
7
4
  1. Keys: the high-level keywords directly relevant to the user’s task.
8
5
  2. Tags: thematic tags to help categorize and retrieve related memories.
9
6
  3. Goal Type: retrieval | qa | generation
10
- 4. Memories: Provide 2–5 short semantic expansions or rephrasings of the task instruction.
11
- These are used for improved embedding search coverage.
12
- Each should be clear, concise, and meaningful for retrieval.
7
+ 4. Rephrased instruction: Give a rephrased task instruction based on the former conversation to make it less confusing to look alone. If you think the task instruction is easy enough to understand, or there is no former conversation, set "rephrased_instruction" to an empty string.
8
+ 5. Need for internet search: If you think you need to search the internet to finish the rephrased/original user task instruction, set "internet_search" to True. Otherwise, set it to False.
9
+ 6. Memories: Provide 2–5 short semantic expansions or rephrasings of the rephrased/original user task instruction. These are used for improved embedding search coverage. Each should be clear, concise, and meaningful for retrieval.
13
10
 
14
11
  Task description:
15
12
  \"\"\"$task\"\"\"
16
13
 
14
+ Former conversation (if any):
15
+ \"\"\"
16
+ $conversation
17
+ \"\"\"
18
+
17
19
  Context (if any):
18
20
  \"\"\"$context\"\"\"
19
21
 
@@ -22,6 +24,8 @@ Return strictly in this JSON format:
22
24
  "keys": [...],
23
25
  "tags": [...],
24
26
  "goal_type": "retrieval | qa | generation",
27
+ "rephrased_instruction": "...", # return an empty string if the original instruction is easy enough to understand
28
+ "internet_search": True/False,
25
29
  "memories": ["...", "...", ...]
26
30
  }
27
31
  """
@@ -3,13 +3,15 @@
3
3
  import json
4
4
  import uuid
5
5
 
6
+ from concurrent.futures import ThreadPoolExecutor, as_completed
6
7
  from datetime import datetime
7
8
 
8
9
  import requests
9
10
 
10
11
  from memos.embedders.factory import OllamaEmbedder
11
12
  from memos.log import get_logger
12
- from memos.memories.textual.item import TextualMemoryItem, TreeNodeTextualMemoryMetadata
13
+ from memos.mem_reader.base import BaseMemReader
14
+ from memos.memories.textual.item import TextualMemoryItem
13
15
 
14
16
 
15
17
  logger = get_logger(__name__)
@@ -93,8 +95,8 @@ class XinyuSearchAPI:
93
95
  "online_search": {
94
96
  "max_entries": max_results,
95
97
  "cache_switch": False,
96
- "baidu_field": {"switch": True, "mode": "relevance", "type": "page"},
97
- "bing_field": {"switch": False, "mode": "relevance", "type": "page_web"},
98
+ "baidu_field": {"switch": False, "mode": "relevance", "type": "page"},
99
+ "bing_field": {"switch": True, "mode": "relevance", "type": "page"},
98
100
  "sogou_field": {"switch": False, "mode": "relevance", "type": "page"},
99
101
  },
100
102
  "request_id": "memos" + str(uuid.uuid4()),
@@ -112,6 +114,7 @@ class XinyuSearchRetriever:
112
114
  access_key: str,
113
115
  search_engine_id: str,
114
116
  embedder: OllamaEmbedder,
117
+ reader: BaseMemReader,
115
118
  max_results: int = 20,
116
119
  ):
117
120
  """
@@ -121,12 +124,14 @@ class XinyuSearchRetriever:
121
124
  access_key: Xinyu API access key
122
125
  embedder: Embedder instance for generating embeddings
123
126
  max_results: Maximum number of results to retrieve
127
+ reader: MemReader Moduel to deal with internet contents
124
128
  """
125
129
  self.xinyu_api = XinyuSearchAPI(access_key, search_engine_id, max_results=max_results)
126
130
  self.embedder = embedder
131
+ self.reader = reader
127
132
 
128
133
  def retrieve_from_internet(
129
- self, query: str, top_k: int = 10, parsed_goal=None
134
+ self, query: str, top_k: int = 10, parsed_goal=None, info=None
130
135
  ) -> list[TextualMemoryItem]:
131
136
  """
132
137
  Retrieve information from Xinyu search and convert to TextualMemoryItem format
@@ -135,7 +140,7 @@ class XinyuSearchRetriever:
135
140
  query: Search query
136
141
  top_k: Number of results to return
137
142
  parsed_goal: Parsed task goal (optional)
138
-
143
+ info (dict): Leave a record of memory consumption.
139
144
  Returns:
140
145
  List of TextualMemoryItem
141
146
  """
@@ -143,63 +148,25 @@ class XinyuSearchRetriever:
143
148
  search_results = self.xinyu_api.search(query, max_results=top_k)
144
149
 
145
150
  # Convert to TextualMemoryItem format
146
- memory_items = []
147
-
148
- for _, result in enumerate(search_results):
149
- # Extract basic information from Xinyu response format
150
- title = result.get("title", "")
151
- content = result.get("content", "")
152
- summary = result.get("summary", "")
153
- url = result.get("url", "")
154
- publish_time = result.get("publish_time", "")
155
- if publish_time:
151
+ memory_items: list[TextualMemoryItem] = []
152
+
153
+ with ThreadPoolExecutor(max_workers=8) as executor:
154
+ futures = [
155
+ executor.submit(self._process_result, result, query, parsed_goal, info)
156
+ for result in search_results
157
+ ]
158
+ for future in as_completed(futures):
156
159
  try:
157
- publish_time = datetime.strptime(publish_time, "%Y-%m-%d %H:%M:%S").strftime(
158
- "%Y-%m-%d"
159
- )
160
+ memory_items.extend(future.result())
160
161
  except Exception as e:
161
- logger.error(f"xinyu search error: {e}")
162
- publish_time = datetime.now().strftime("%Y-%m-%d")
163
- else:
164
- publish_time = datetime.now().strftime("%Y-%m-%d")
165
- source = result.get("source", "")
166
- site = result.get("site", "")
167
- if site:
168
- site = site.split("|")[0]
169
-
170
- # Combine memory content
171
- memory_content = (
172
- f"Title: {title}\nSummary: {summary}\nContent: {content[:200]}...\nSource: {url}"
173
- )
162
+ logger.error(f"Error processing search result: {e}")
174
163
 
175
- # Create metadata
176
- metadata = TreeNodeTextualMemoryMetadata(
177
- user_id=None,
178
- session_id=None,
179
- status="activated",
180
- type="fact", # Search results are usually factual information
181
- memory_time=publish_time,
182
- source="web",
183
- confidence=85.0, # Confidence level for search information
184
- entities=self._extract_entities(title, content, summary),
185
- tags=self._extract_tags(title, content, summary, parsed_goal),
186
- visibility="public",
187
- memory_type="LongTermMemory", # Search results as working memory
188
- key=title,
189
- sources=[url] if url else [],
190
- embedding=self.embedder.embed([memory_content])[0],
191
- created_at=datetime.now().isoformat(),
192
- usage=[],
193
- background=f"Xinyu search result from {site or source}",
194
- )
195
- # Create TextualMemoryItem
196
- memory_item = TextualMemoryItem(
197
- id=str(uuid.uuid4()), memory=memory_content, metadata=metadata
198
- )
164
+ unique_memory_items = {}
165
+ for item in memory_items:
166
+ if item.memory not in unique_memory_items:
167
+ unique_memory_items[item.memory] = item
199
168
 
200
- memory_items.append(memory_item)
201
-
202
- return memory_items
169
+ return list(unique_memory_items.values())
203
170
 
204
171
  def _extract_entities(self, title: str, content: str, summary: str) -> list[str]:
205
172
  """
@@ -333,3 +300,40 @@ class XinyuSearchRetriever:
333
300
  tags.extend(parsed_goal.tags)
334
301
 
335
302
  return list(set(tags))[:15] # Limit to 15 tags
303
+
304
+ def _process_result(
305
+ self, result: dict, query: str, parsed_goal: str, info: None
306
+ ) -> list[TextualMemoryItem]:
307
+ if not info:
308
+ info = {"user_id": "", "session_id": ""}
309
+ title = result.get("title", "")
310
+ content = result.get("content", "")
311
+ summary = result.get("summary", "")
312
+ url = result.get("url", "")
313
+ publish_time = result.get("publish_time", "")
314
+ if publish_time:
315
+ try:
316
+ publish_time = datetime.strptime(publish_time, "%Y-%m-%d %H:%M:%S").strftime(
317
+ "%Y-%m-%d"
318
+ )
319
+ except Exception as e:
320
+ logger.error(f"xinyu search error: {e}")
321
+ publish_time = datetime.now().strftime("%Y-%m-%d")
322
+ else:
323
+ publish_time = datetime.now().strftime("%Y-%m-%d")
324
+
325
+ read_items = self.reader.get_memory([content], type="doc", info=info)
326
+
327
+ memory_items = []
328
+ for read_item_i in read_items[0]:
329
+ read_item_i.memory = (
330
+ f"Title: {title}\nNewsTime: {publish_time}\nSummary: {summary}\n"
331
+ f"Content: {read_item_i.memory}"
332
+ )
333
+ read_item_i.metadata.source = "web"
334
+ read_item_i.metadata.memory_type = "OuterMemory"
335
+ read_item_i.metadata.sources = [url] if url else []
336
+ read_item_i.metadata.visibility = "public"
337
+
338
+ memory_items.append(read_item_i)
339
+ return memory_items