MemoryOS 1.0.0__py3-none-any.whl → 1.1.1__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 (94) hide show
  1. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/METADATA +8 -2
  2. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/RECORD +92 -69
  3. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/WHEEL +1 -1
  4. memos/__init__.py +1 -1
  5. memos/api/client.py +109 -0
  6. memos/api/config.py +35 -8
  7. memos/api/context/dependencies.py +15 -66
  8. memos/api/middleware/request_context.py +63 -0
  9. memos/api/product_api.py +5 -2
  10. memos/api/product_models.py +107 -16
  11. memos/api/routers/product_router.py +62 -19
  12. memos/api/start_api.py +13 -0
  13. memos/configs/graph_db.py +4 -0
  14. memos/configs/mem_scheduler.py +38 -3
  15. memos/configs/memory.py +13 -0
  16. memos/configs/reranker.py +18 -0
  17. memos/context/context.py +255 -0
  18. memos/embedders/factory.py +2 -0
  19. memos/graph_dbs/base.py +4 -2
  20. memos/graph_dbs/nebular.py +368 -223
  21. memos/graph_dbs/neo4j.py +49 -13
  22. memos/graph_dbs/neo4j_community.py +13 -3
  23. memos/llms/factory.py +2 -0
  24. memos/llms/openai.py +74 -2
  25. memos/llms/vllm.py +2 -0
  26. memos/log.py +128 -4
  27. memos/mem_cube/general.py +3 -1
  28. memos/mem_os/core.py +89 -23
  29. memos/mem_os/main.py +3 -6
  30. memos/mem_os/product.py +418 -154
  31. memos/mem_os/utils/reference_utils.py +20 -0
  32. memos/mem_reader/factory.py +2 -0
  33. memos/mem_reader/simple_struct.py +204 -82
  34. memos/mem_scheduler/analyzer/__init__.py +0 -0
  35. memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +569 -0
  36. memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
  37. memos/mem_scheduler/base_scheduler.py +126 -56
  38. memos/mem_scheduler/general_modules/dispatcher.py +2 -2
  39. memos/mem_scheduler/general_modules/misc.py +99 -1
  40. memos/mem_scheduler/general_modules/scheduler_logger.py +17 -11
  41. memos/mem_scheduler/general_scheduler.py +40 -88
  42. memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
  43. memos/mem_scheduler/memory_manage_modules/memory_filter.py +308 -0
  44. memos/mem_scheduler/{general_modules → memory_manage_modules}/retriever.py +34 -7
  45. memos/mem_scheduler/monitors/dispatcher_monitor.py +9 -8
  46. memos/mem_scheduler/monitors/general_monitor.py +119 -39
  47. memos/mem_scheduler/optimized_scheduler.py +124 -0
  48. memos/mem_scheduler/orm_modules/__init__.py +0 -0
  49. memos/mem_scheduler/orm_modules/base_model.py +635 -0
  50. memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
  51. memos/mem_scheduler/scheduler_factory.py +2 -0
  52. memos/mem_scheduler/schemas/monitor_schemas.py +96 -29
  53. memos/mem_scheduler/utils/config_utils.py +100 -0
  54. memos/mem_scheduler/utils/db_utils.py +33 -0
  55. memos/mem_scheduler/utils/filter_utils.py +1 -1
  56. memos/mem_scheduler/webservice_modules/__init__.py +0 -0
  57. memos/mem_user/mysql_user_manager.py +4 -2
  58. memos/memories/activation/kv.py +2 -1
  59. memos/memories/textual/item.py +96 -17
  60. memos/memories/textual/naive.py +1 -1
  61. memos/memories/textual/tree.py +57 -3
  62. memos/memories/textual/tree_text_memory/organize/handler.py +4 -2
  63. memos/memories/textual/tree_text_memory/organize/manager.py +28 -14
  64. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +1 -2
  65. memos/memories/textual/tree_text_memory/organize/reorganizer.py +75 -23
  66. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +10 -6
  67. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -2
  68. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
  69. memos/memories/textual/tree_text_memory/retrieve/recall.py +119 -21
  70. memos/memories/textual/tree_text_memory/retrieve/searcher.py +172 -44
  71. memos/memories/textual/tree_text_memory/retrieve/utils.py +6 -4
  72. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +5 -4
  73. memos/memos_tools/notification_utils.py +46 -0
  74. memos/memos_tools/singleton.py +174 -0
  75. memos/memos_tools/thread_safe_dict.py +22 -0
  76. memos/memos_tools/thread_safe_dict_segment.py +382 -0
  77. memos/parsers/factory.py +2 -0
  78. memos/reranker/__init__.py +4 -0
  79. memos/reranker/base.py +24 -0
  80. memos/reranker/concat.py +59 -0
  81. memos/reranker/cosine_local.py +96 -0
  82. memos/reranker/factory.py +48 -0
  83. memos/reranker/http_bge.py +312 -0
  84. memos/reranker/noop.py +16 -0
  85. memos/templates/mem_reader_prompts.py +289 -40
  86. memos/templates/mem_scheduler_prompts.py +242 -0
  87. memos/templates/mos_prompts.py +133 -60
  88. memos/types.py +4 -1
  89. memos/api/context/context.py +0 -147
  90. memos/mem_scheduler/mos_for_test_scheduler.py +0 -146
  91. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/entry_points.txt +0 -0
  92. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info/licenses}/LICENSE +0 -0
  93. /memos/mem_scheduler/{general_modules → webservice_modules}/rabbitmq_service.py +0 -0
  94. /memos/mem_scheduler/{general_modules → webservice_modules}/redis_service.py +0 -0
memos/mem_os/core.py CHANGED
@@ -24,7 +24,7 @@ from memos.mem_user.user_manager import UserManager, UserRole
24
24
  from memos.memories.activation.item import ActivationMemoryItem
25
25
  from memos.memories.parametric.item import ParametricMemoryItem
26
26
  from memos.memories.textual.item import TextualMemoryItem, TextualMemoryMetadata
27
- from memos.memos_tools.thread_safe_dict import ThreadSafeDict
27
+ from memos.memos_tools.thread_safe_dict_segment import OptimizedThreadSafeDict
28
28
  from memos.templates.mos_prompts import QUERY_REWRITING_PROMPT
29
29
  from memos.types import ChatHistory, MessageList, MOSSearchResult
30
30
 
@@ -47,8 +47,8 @@ class MOSCore:
47
47
  self.mem_reader = MemReaderFactory.from_config(config.mem_reader)
48
48
  self.chat_history_manager: dict[str, ChatHistory] = {}
49
49
  # use thread safe dict for multi-user product-server scenario
50
- self.mem_cubes: ThreadSafeDict[str, GeneralMemCube] = (
51
- ThreadSafeDict() if user_manager is not None else {}
50
+ self.mem_cubes: OptimizedThreadSafeDict[str, GeneralMemCube] = (
51
+ OptimizedThreadSafeDict() if user_manager is not None else {}
52
52
  )
53
53
  self._register_chat_history()
54
54
 
@@ -125,12 +125,16 @@ class MOSCore:
125
125
  "missing required 'llm' attribute"
126
126
  )
127
127
  self._mem_scheduler.initialize_modules(
128
- chat_llm=self.chat_llm, process_llm=self.chat_llm
128
+ chat_llm=self.chat_llm,
129
+ process_llm=self.chat_llm,
130
+ db_engine=self.user_manager.engine,
129
131
  )
130
132
  else:
131
133
  # Configure scheduler general_modules
132
134
  self._mem_scheduler.initialize_modules(
133
- chat_llm=self.chat_llm, process_llm=self.mem_reader.llm
135
+ chat_llm=self.chat_llm,
136
+ process_llm=self.mem_reader.llm,
137
+ db_engine=self.user_manager.engine,
134
138
  )
135
139
  self._mem_scheduler.start()
136
140
  return self._mem_scheduler
@@ -182,13 +186,13 @@ class MOSCore:
182
186
  logger.info(f"close reorganizer for {mem_cube.text_mem.config.cube_id}")
183
187
  mem_cube.text_mem.memory_manager.wait_reorganizer()
184
188
 
185
- def _register_chat_history(self, user_id: str | None = None) -> None:
189
+ def _register_chat_history(
190
+ self, user_id: str | None = None, session_id: str | None = None
191
+ ) -> None:
186
192
  """Initialize chat history with user ID."""
187
- if user_id is None:
188
- user_id = self.user_id
189
193
  self.chat_history_manager[user_id] = ChatHistory(
190
- user_id=user_id,
191
- session_id=self.session_id,
194
+ user_id=user_id if user_id is not None else self.user_id,
195
+ session_id=session_id if session_id is not None else self.session_id,
192
196
  created_at=datetime.utcnow(),
193
197
  total_messages=0,
194
198
  chat_history=[],
@@ -352,6 +356,7 @@ class MOSCore:
352
356
  self,
353
357
  memories: list[TextualMemoryItem] | list[str] | None = None,
354
358
  base_prompt: str | None = None,
359
+ **kwargs,
355
360
  ) -> str:
356
361
  """Build system prompt with optional memories context."""
357
362
  if base_prompt is None:
@@ -482,14 +487,14 @@ class MOSCore:
482
487
  self.mem_cubes[mem_cube_id] = mem_cube_name_or_path
483
488
  logger.info(f"register new cube {mem_cube_id} for user {target_user_id}")
484
489
  elif os.path.exists(mem_cube_name_or_path):
485
- self.mem_cubes[mem_cube_id] = GeneralMemCube.init_from_dir(mem_cube_name_or_path)
490
+ mem_cube_obj = GeneralMemCube.init_from_dir(mem_cube_name_or_path)
491
+ self.mem_cubes[mem_cube_id] = mem_cube_obj
486
492
  else:
487
493
  logger.warning(
488
494
  f"MemCube {mem_cube_name_or_path} does not exist, try to init from remote repo."
489
495
  )
490
- self.mem_cubes[mem_cube_id] = GeneralMemCube.init_from_remote_repo(
491
- mem_cube_name_or_path
492
- )
496
+ mem_cube_obj = GeneralMemCube.init_from_remote_repo(mem_cube_name_or_path)
497
+ self.mem_cubes[mem_cube_id] = mem_cube_obj
493
498
  # Check if cube already exists in database
494
499
  existing_cube = self.user_manager.get_cube(mem_cube_id)
495
500
 
@@ -545,6 +550,9 @@ class MOSCore:
545
550
  top_k: int | None = None,
546
551
  mode: Literal["fast", "fine"] = "fast",
547
552
  internet_search: bool = False,
553
+ moscube: bool = False,
554
+ session_id: str | None = None,
555
+ **kwargs,
548
556
  ) -> MOSSearchResult:
549
557
  """
550
558
  Search for textual memories across all registered MemCubes.
@@ -559,7 +567,9 @@ class MOSCore:
559
567
  Returns:
560
568
  MemoryResult: A dictionary containing the search results.
561
569
  """
570
+ target_session_id = session_id if session_id is not None else self.session_id
562
571
  target_user_id = user_id if user_id is not None else self.user_id
572
+
563
573
  self._validate_user_exists(target_user_id)
564
574
  # Get all cubes accessible by the target user
565
575
  accessible_cubes = self.user_manager.get_user_cubes(target_user_id)
@@ -572,6 +582,11 @@ class MOSCore:
572
582
  self._register_chat_history(target_user_id)
573
583
  chat_history = self.chat_history_manager[target_user_id]
574
584
 
585
+ # Create search filter if session_id is provided
586
+ search_filter = None
587
+ if session_id is not None:
588
+ search_filter = {"session_id": session_id}
589
+
575
590
  result: MOSSearchResult = {
576
591
  "text_mem": [],
577
592
  "act_mem": [],
@@ -581,9 +596,13 @@ class MOSCore:
581
596
  install_cube_ids = user_cube_ids
582
597
  # create exist dict in mem_cubes and avoid one search slow
583
598
  tmp_mem_cubes = {}
599
+ time_start_cube_get = time.time()
584
600
  for mem_cube_id in install_cube_ids:
585
601
  if mem_cube_id in self.mem_cubes:
586
602
  tmp_mem_cubes[mem_cube_id] = self.mem_cubes.get(mem_cube_id)
603
+ logger.info(
604
+ f"time search: transform cube time user_id: {target_user_id} time is: {time.time() - time_start_cube_get}"
605
+ )
587
606
 
588
607
  for mem_cube_id, mem_cube in tmp_mem_cubes.items():
589
608
  if (
@@ -599,9 +618,11 @@ class MOSCore:
599
618
  manual_close_internet=not internet_search,
600
619
  info={
601
620
  "user_id": target_user_id,
602
- "session_id": self.session_id,
621
+ "session_id": target_session_id,
603
622
  "chat_history": chat_history.chat_history,
604
623
  },
624
+ moscube=moscube,
625
+ search_filter=search_filter,
605
626
  )
606
627
  result["text_mem"].append({"cube_id": mem_cube_id, "memories": memories})
607
628
  logger.info(
@@ -620,6 +641,8 @@ class MOSCore:
620
641
  doc_path: str | None = None,
621
642
  mem_cube_id: str | None = None,
622
643
  user_id: str | None = None,
644
+ session_id: str | None = None,
645
+ **kwargs,
623
646
  ) -> None:
624
647
  """
625
648
  Add textual memories to a MemCube.
@@ -632,11 +655,16 @@ class MOSCore:
632
655
  If None, the default MemCube for the user is used.
633
656
  user_id (str, optional): The identifier of the user to add the memories to.
634
657
  If None, the default user is used.
658
+ session_id (str, optional): session_id
635
659
  """
636
660
  # user input messages
637
661
  assert (messages is not None) or (memory_content is not None) or (doc_path is not None), (
638
662
  "messages_or_doc_path or memory_content or doc_path must be provided."
639
663
  )
664
+ # TODO: asure that session_id is a valid string
665
+ time_start = time.time()
666
+
667
+ target_session_id = session_id if session_id else self.session_id
640
668
  target_user_id = user_id if user_id is not None else self.user_id
641
669
  if mem_cube_id is None:
642
670
  # Try to find a default cube for the user
@@ -648,18 +676,29 @@ class MOSCore:
648
676
  mem_cube_id = accessible_cubes[0].cube_id # TODO not only first
649
677
  else:
650
678
  self._validate_cube_access(target_user_id, mem_cube_id)
679
+ logger.info(
680
+ f"time add: get mem_cube_id time user_id: {target_user_id} time is: {time.time() - time_start}"
681
+ )
651
682
 
683
+ time_start_0 = time.time()
652
684
  if mem_cube_id not in self.mem_cubes:
653
685
  raise ValueError(f"MemCube '{mem_cube_id}' is not loaded. Please register.")
686
+ logger.info(
687
+ f"time add: get mem_cube_id check in mem_cubes time user_id: {target_user_id} time is: {time.time() - time_start_0}"
688
+ )
689
+ time_start_1 = time.time()
654
690
  if (
655
691
  (messages is not None)
656
692
  and self.config.enable_textual_memory
657
693
  and self.mem_cubes[mem_cube_id].text_mem
658
694
  ):
695
+ logger.info(
696
+ f"time add: messages is not None and enable_textual_memory and text_mem is not None time user_id: {target_user_id} time is: {time.time() - time_start_1}"
697
+ )
659
698
  if self.mem_cubes[mem_cube_id].config.text_mem.backend != "tree_text":
660
699
  add_memory = []
661
700
  metadata = TextualMemoryMetadata(
662
- user_id=self.user_id, session_id=self.session_id, source="conversation"
701
+ user_id=target_user_id, session_id=target_session_id, source="conversation"
663
702
  )
664
703
  for message in messages:
665
704
  add_memory.append(
@@ -668,12 +707,15 @@ class MOSCore:
668
707
  self.mem_cubes[mem_cube_id].text_mem.add(add_memory)
669
708
  else:
670
709
  messages_list = [messages]
710
+ time_start_2 = time.time()
671
711
  memories = self.mem_reader.get_memory(
672
712
  messages_list,
673
713
  type="chat",
674
- info={"user_id": target_user_id, "session_id": self.session_id},
714
+ info={"user_id": target_user_id, "session_id": target_session_id},
715
+ )
716
+ logger.info(
717
+ f"time add: get mem_reader time user_id: {target_user_id} time is: {time.time() - time_start_2}"
675
718
  )
676
-
677
719
  mem_ids = []
678
720
  for mem in memories:
679
721
  mem_id_list: list[str] = self.mem_cubes[mem_cube_id].text_mem.add(mem)
@@ -703,7 +745,7 @@ class MOSCore:
703
745
  ):
704
746
  if self.mem_cubes[mem_cube_id].config.text_mem.backend != "tree_text":
705
747
  metadata = TextualMemoryMetadata(
706
- user_id=self.user_id, session_id=self.session_id, source="conversation"
748
+ user_id=target_user_id, session_id=target_session_id, source="conversation"
707
749
  )
708
750
  self.mem_cubes[mem_cube_id].text_mem.add(
709
751
  [TextualMemoryItem(memory=memory_content, metadata=metadata)]
@@ -715,7 +757,7 @@ class MOSCore:
715
757
  memories = self.mem_reader.get_memory(
716
758
  messages_list,
717
759
  type="chat",
718
- info={"user_id": target_user_id, "session_id": self.session_id},
760
+ info={"user_id": target_user_id, "session_id": target_session_id},
719
761
  )
720
762
 
721
763
  mem_ids = []
@@ -749,7 +791,7 @@ class MOSCore:
749
791
  doc_memories = self.mem_reader.get_memory(
750
792
  documents,
751
793
  type="doc",
752
- info={"user_id": target_user_id, "session_id": self.session_id},
794
+ info={"user_id": target_user_id, "session_id": target_session_id},
753
795
  )
754
796
 
755
797
  mem_ids = []
@@ -956,9 +998,33 @@ class MOSCore:
956
998
  self.mem_cubes[mem_cube_id].dump(dump_dir)
957
999
  logger.info(f"MemCube {mem_cube_id} dumped to {dump_dir}")
958
1000
 
1001
+ def load(
1002
+ self,
1003
+ load_dir: str,
1004
+ user_id: str | None = None,
1005
+ mem_cube_id: str | None = None,
1006
+ memory_types: list[Literal["text_mem", "act_mem", "para_mem"]] | None = None,
1007
+ ) -> None:
1008
+ """Dump the MemCube to a dictionary.
1009
+ Args:
1010
+ load_dir (str): The directory to load the MemCube from.
1011
+ user_id (str, optional): The identifier of the user to load the MemCube from.
1012
+ If None, the default user is used.
1013
+ mem_cube_id (str, optional): The identifier of the MemCube to load.
1014
+ If None, the default MemCube for the user is used.
1015
+ """
1016
+ target_user_id = user_id if user_id is not None else self.user_id
1017
+ accessible_cubes = self.user_manager.get_user_cubes(target_user_id)
1018
+ if not mem_cube_id:
1019
+ mem_cube_id = accessible_cubes[0].cube_id
1020
+ if mem_cube_id not in self.mem_cubes:
1021
+ raise ValueError(f"MemCube with ID {mem_cube_id} does not exist. please regiester")
1022
+ self.mem_cubes[mem_cube_id].load(load_dir, memory_types=memory_types)
1023
+ logger.info(f"MemCube {mem_cube_id} loaded from {load_dir}")
1024
+
959
1025
  def get_user_info(self) -> dict[str, Any]:
960
1026
  """Get current user information including accessible cubes.
961
-
1027
+ TODO: maybe input user_id
962
1028
  Returns:
963
1029
  dict: User information and accessible cubes.
964
1030
  """
@@ -971,7 +1037,7 @@ class MOSCore:
971
1037
  return {
972
1038
  "user_id": user.user_id,
973
1039
  "user_name": user.user_name,
974
- "role": user.role.value,
1040
+ "role": user.role.value if hasattr(user.role, "value") else user.role,
975
1041
  "created_at": user.created_at.isoformat(),
976
1042
  "accessible_cubes": [
977
1043
  {
memos/mem_os/main.py CHANGED
@@ -5,6 +5,7 @@ import os
5
5
  from typing import Any
6
6
 
7
7
  from memos.configs.mem_os import MOSConfig
8
+ from memos.context.context import ContextThreadPoolExecutor
8
9
  from memos.llms.factory import LLMFactory
9
10
  from memos.log import get_logger
10
11
  from memos.mem_os.core import MOSCore
@@ -487,9 +488,7 @@ class MOS(MOSCore):
487
488
 
488
489
  # Generate answers in parallel while maintaining order
489
490
  sub_answers = [None] * len(sub_questions)
490
- with concurrent.futures.ThreadPoolExecutor(
491
- max_workers=min(len(sub_questions), 10)
492
- ) as executor:
491
+ with ContextThreadPoolExecutor(max_workers=min(len(sub_questions), 10)) as executor:
493
492
  # Submit all answer generation tasks
494
493
  future_to_index = {
495
494
  executor.submit(generate_answer_for_question, i, question): i
@@ -552,9 +551,7 @@ class MOS(MOSCore):
552
551
 
553
552
  # Search in parallel while maintaining order
554
553
  all_memories = []
555
- with concurrent.futures.ThreadPoolExecutor(
556
- max_workers=min(len(sub_questions), 10)
557
- ) as executor:
554
+ with ContextThreadPoolExecutor(max_workers=min(len(sub_questions), 10)) as executor:
558
555
  # Submit all search tasks and keep track of their order
559
556
  future_to_index = {
560
557
  executor.submit(search_single_question, question): i