MemoryOS 0.0.1__py3-none-any.whl → 0.1.13__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 (124) hide show
  1. memoryos-0.1.13.dist-info/METADATA +288 -0
  2. memoryos-0.1.13.dist-info/RECORD +122 -0
  3. memos/__init__.py +20 -1
  4. memos/api/start_api.py +420 -0
  5. memos/chunkers/__init__.py +4 -0
  6. memos/chunkers/base.py +24 -0
  7. memos/chunkers/factory.py +22 -0
  8. memos/chunkers/sentence_chunker.py +35 -0
  9. memos/configs/__init__.py +0 -0
  10. memos/configs/base.py +82 -0
  11. memos/configs/chunker.py +45 -0
  12. memos/configs/embedder.py +53 -0
  13. memos/configs/graph_db.py +45 -0
  14. memos/configs/internet_retriever.py +81 -0
  15. memos/configs/llm.py +71 -0
  16. memos/configs/mem_chat.py +81 -0
  17. memos/configs/mem_cube.py +89 -0
  18. memos/configs/mem_os.py +74 -0
  19. memos/configs/mem_reader.py +53 -0
  20. memos/configs/mem_scheduler.py +78 -0
  21. memos/configs/memory.py +195 -0
  22. memos/configs/parser.py +38 -0
  23. memos/configs/utils.py +8 -0
  24. memos/configs/vec_db.py +64 -0
  25. memos/deprecation.py +262 -0
  26. memos/embedders/__init__.py +0 -0
  27. memos/embedders/base.py +15 -0
  28. memos/embedders/factory.py +23 -0
  29. memos/embedders/ollama.py +74 -0
  30. memos/embedders/sentence_transformer.py +40 -0
  31. memos/exceptions.py +30 -0
  32. memos/graph_dbs/__init__.py +0 -0
  33. memos/graph_dbs/base.py +215 -0
  34. memos/graph_dbs/factory.py +21 -0
  35. memos/graph_dbs/neo4j.py +827 -0
  36. memos/hello_world.py +97 -0
  37. memos/llms/__init__.py +0 -0
  38. memos/llms/base.py +16 -0
  39. memos/llms/factory.py +25 -0
  40. memos/llms/hf.py +231 -0
  41. memos/llms/ollama.py +82 -0
  42. memos/llms/openai.py +34 -0
  43. memos/llms/utils.py +14 -0
  44. memos/log.py +78 -0
  45. memos/mem_chat/__init__.py +0 -0
  46. memos/mem_chat/base.py +30 -0
  47. memos/mem_chat/factory.py +21 -0
  48. memos/mem_chat/simple.py +200 -0
  49. memos/mem_cube/__init__.py +0 -0
  50. memos/mem_cube/base.py +29 -0
  51. memos/mem_cube/general.py +146 -0
  52. memos/mem_cube/utils.py +24 -0
  53. memos/mem_os/client.py +5 -0
  54. memos/mem_os/core.py +819 -0
  55. memos/mem_os/main.py +503 -0
  56. memos/mem_os/product.py +89 -0
  57. memos/mem_reader/__init__.py +0 -0
  58. memos/mem_reader/base.py +27 -0
  59. memos/mem_reader/factory.py +21 -0
  60. memos/mem_reader/memory.py +298 -0
  61. memos/mem_reader/simple_struct.py +241 -0
  62. memos/mem_scheduler/__init__.py +0 -0
  63. memos/mem_scheduler/base_scheduler.py +164 -0
  64. memos/mem_scheduler/general_scheduler.py +305 -0
  65. memos/mem_scheduler/modules/__init__.py +0 -0
  66. memos/mem_scheduler/modules/base.py +74 -0
  67. memos/mem_scheduler/modules/dispatcher.py +103 -0
  68. memos/mem_scheduler/modules/monitor.py +82 -0
  69. memos/mem_scheduler/modules/redis_service.py +146 -0
  70. memos/mem_scheduler/modules/retriever.py +41 -0
  71. memos/mem_scheduler/modules/schemas.py +146 -0
  72. memos/mem_scheduler/scheduler_factory.py +21 -0
  73. memos/mem_scheduler/utils.py +26 -0
  74. memos/mem_user/user_manager.py +488 -0
  75. memos/memories/__init__.py +0 -0
  76. memos/memories/activation/__init__.py +0 -0
  77. memos/memories/activation/base.py +42 -0
  78. memos/memories/activation/item.py +25 -0
  79. memos/memories/activation/kv.py +232 -0
  80. memos/memories/base.py +19 -0
  81. memos/memories/factory.py +34 -0
  82. memos/memories/parametric/__init__.py +0 -0
  83. memos/memories/parametric/base.py +19 -0
  84. memos/memories/parametric/item.py +11 -0
  85. memos/memories/parametric/lora.py +41 -0
  86. memos/memories/textual/__init__.py +0 -0
  87. memos/memories/textual/base.py +89 -0
  88. memos/memories/textual/general.py +286 -0
  89. memos/memories/textual/item.py +167 -0
  90. memos/memories/textual/naive.py +185 -0
  91. memos/memories/textual/tree.py +321 -0
  92. memos/memories/textual/tree_text_memory/__init__.py +0 -0
  93. memos/memories/textual/tree_text_memory/organize/__init__.py +0 -0
  94. memos/memories/textual/tree_text_memory/organize/manager.py +305 -0
  95. memos/memories/textual/tree_text_memory/retrieve/__init__.py +0 -0
  96. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +263 -0
  97. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +89 -0
  98. memos/memories/textual/tree_text_memory/retrieve/reasoner.py +61 -0
  99. memos/memories/textual/tree_text_memory/retrieve/recall.py +158 -0
  100. memos/memories/textual/tree_text_memory/retrieve/reranker.py +111 -0
  101. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +13 -0
  102. memos/memories/textual/tree_text_memory/retrieve/searcher.py +208 -0
  103. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +68 -0
  104. memos/memories/textual/tree_text_memory/retrieve/utils.py +48 -0
  105. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +335 -0
  106. memos/parsers/__init__.py +0 -0
  107. memos/parsers/base.py +15 -0
  108. memos/parsers/factory.py +19 -0
  109. memos/parsers/markitdown.py +22 -0
  110. memos/settings.py +8 -0
  111. memos/templates/__init__.py +0 -0
  112. memos/templates/mem_reader_prompts.py +98 -0
  113. memos/templates/mem_scheduler_prompts.py +65 -0
  114. memos/templates/mos_prompts.py +63 -0
  115. memos/types.py +55 -0
  116. memos/vec_dbs/__init__.py +0 -0
  117. memos/vec_dbs/base.py +105 -0
  118. memos/vec_dbs/factory.py +21 -0
  119. memos/vec_dbs/item.py +43 -0
  120. memos/vec_dbs/qdrant.py +292 -0
  121. memoryos-0.0.1.dist-info/METADATA +0 -53
  122. memoryos-0.0.1.dist-info/RECORD +0 -5
  123. {memoryos-0.0.1.dist-info → memoryos-0.1.13.dist-info}/LICENSE +0 -0
  124. {memoryos-0.0.1.dist-info → memoryos-0.1.13.dist-info}/WHEEL +0 -0
@@ -0,0 +1,200 @@
1
+ import os
2
+
3
+ from typing import Literal
4
+
5
+ from memos.configs.mem_chat import SimpleMemChatConfig
6
+ from memos.llms.factory import LLMFactory
7
+ from memos.log import get_logger
8
+ from memos.mem_chat.base import BaseMemChat
9
+ from memos.mem_cube.base import BaseMemCube
10
+ from memos.memories.activation.kv import move_dynamic_cache_htod
11
+ from memos.memories.textual.item import TextualMemoryItem
12
+ from memos.types import ChatHistory, MessageList
13
+
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ class SimpleMemChat(BaseMemChat):
19
+ """Simple MemChat class."""
20
+
21
+ def __init__(self, config: SimpleMemChatConfig):
22
+ """Initialize the MemChat with the given configuration."""
23
+ self.config = config
24
+ self.chat_llm = LLMFactory.from_config(config.chat_llm)
25
+ self._mem_cube = None
26
+
27
+ @property
28
+ def mem_cube(self) -> BaseMemCube:
29
+ """The memory cube associated with this MemChat."""
30
+ return self._mem_cube
31
+
32
+ @mem_cube.setter
33
+ def mem_cube(self, value: BaseMemCube) -> None:
34
+ """The memory cube associated with this MemChat."""
35
+ self._mem_cube = value
36
+
37
+ def run(self) -> None:
38
+ """Run the MemChat."""
39
+
40
+ # Start MemChat
41
+
42
+ print(
43
+ "\n📢 [System] " + "Simple MemChat is running.\n"
44
+ "Commands: 'bye' to quit, 'clear' to clear chat history, 'mem' to show all memories, 'export' to export chat history\n",
45
+ )
46
+
47
+ messages = []
48
+ while True:
49
+ # Get user input
50
+
51
+ user_input = input("👤 [You] ").strip()
52
+ print()
53
+
54
+ if user_input.lower() == "bye":
55
+ break
56
+ elif user_input.lower() == "clear":
57
+ messages = []
58
+ print("📢 [System] Chat history cleared.")
59
+ continue
60
+ elif user_input.lower() == "mem":
61
+ if self.config.enable_textual_memory:
62
+ all_memories = self.mem_cube.text_mem.get_all()
63
+ print(f"🧠 [Memory] \n{self._str_memories(all_memories)}\n")
64
+ else:
65
+ print("📢 [System] Textual memory is not enabled.\n")
66
+ continue
67
+ elif user_input.lower() == "export":
68
+ if messages:
69
+ filepath = self._export_chat_history(messages)
70
+ print(f"📢 [System] Chat history exported to: {filepath}\n")
71
+ else:
72
+ print("📢 [System] No chat history to export.\n")
73
+ continue
74
+ elif user_input == "":
75
+ continue
76
+
77
+ # Get memories
78
+
79
+ if self.config.enable_textual_memory:
80
+ memories = self.mem_cube.text_mem.search(user_input, top_k=self.config.top_k)
81
+ print(
82
+ f"🧠 [Memory] Searched memories:\n{self._str_memories(memories, mode='concise')}\n"
83
+ )
84
+ system_prompt = self._build_system_prompt(memories)
85
+ else:
86
+ system_prompt = self._build_system_prompt()
87
+ current_messages = [
88
+ {"role": "system", "content": system_prompt},
89
+ *messages,
90
+ {"role": "user", "content": user_input},
91
+ ]
92
+
93
+ if self.config.enable_activation_memory:
94
+ past_key_values = None
95
+ loaded_kv_cache_item = next(
96
+ iter(self.mem_cube.act_mem.kv_cache_memories.values()), None
97
+ )
98
+ if loaded_kv_cache_item is not None:
99
+ # If has loaded kv cache, we move it to device before inferring.
100
+ # Currently, we move only single kv cache item
101
+ past_key_values = loaded_kv_cache_item
102
+ past_key_values.kv_cache = move_dynamic_cache_htod(
103
+ past_key_values.kv_cache, self.chat_llm.model.device
104
+ )
105
+
106
+ # Generate response
107
+ response = self.chat_llm.generate(
108
+ current_messages,
109
+ past_key_values=past_key_values.kv_cache if past_key_values else None,
110
+ )
111
+ else:
112
+ # Generate response without activation memory
113
+ response = self.chat_llm.generate(current_messages)
114
+
115
+ print(f"🤖 [Assistant] {response}\n")
116
+ messages.append({"role": "user", "content": user_input})
117
+ messages.append({"role": "assistant", "content": response})
118
+ messages = messages[
119
+ -self.config.max_turns_window :
120
+ ] # Keep only recent messages to avoid context overflow
121
+
122
+ # Extract memories
123
+
124
+ if self.config.enable_textual_memory:
125
+ new_memories = self.mem_cube.text_mem.extract(messages[-2:])
126
+ for memory in new_memories:
127
+ memory.metadata.user_id = self.config.user_id
128
+ memory.metadata.session_id = self.config.session_id
129
+ memory.metadata.status = "activated"
130
+ self.mem_cube.text_mem.add(new_memories)
131
+ print(
132
+ f"🧠 [Memory] Stored {len(new_memories)} new memory(ies):\n"
133
+ f"{self._str_memories(new_memories, 'concise')}\n"
134
+ )
135
+
136
+ # Stop MemChat
137
+
138
+ print("📢 [System] MemChat has stopped.")
139
+
140
+ def _build_system_prompt(self, memories: list | None = None) -> str:
141
+ """Build system prompt with optional memories context."""
142
+ base_prompt = (
143
+ "You are a knowledgeable and helpful AI assistant. "
144
+ "You have access to conversation memories that help you provide more personalized responses. "
145
+ "Use the memories to understand the user's context, preferences, and past interactions. "
146
+ "If memories are provided, reference them naturally when relevant, but don't explicitly mention having memories."
147
+ )
148
+
149
+ if memories:
150
+ memory_context = "\n\n## Memories:\n"
151
+ for i, memory in enumerate(memories, 1):
152
+ memory_context += f"{i}. ({memory.metadata.memory_time}) {memory.memory}\n"
153
+ return base_prompt + memory_context
154
+
155
+ return base_prompt
156
+
157
+ def _str_memories(
158
+ self, memories: list[TextualMemoryItem], mode: Literal["concise", "full"] = "full"
159
+ ) -> str:
160
+ """Format memories for display."""
161
+ if not memories:
162
+ return "No memories."
163
+ if mode == "concise":
164
+ return "\n".join(f"{i + 1}. {memory.memory}" for i, memory in enumerate(memories))
165
+ elif mode == "full":
166
+ return "\n".join(f"{i + 1}. {memory}" for i, memory in enumerate(memories))
167
+
168
+ def _export_chat_history(self, messages: MessageList, output_dir: str = "chat_exports") -> str:
169
+ """Export chat history to JSON file.
170
+
171
+ Args:
172
+ messages: List of chat messages
173
+ output_dir: Directory to save the export file
174
+
175
+ Returns:
176
+ Path to the exported JSON file
177
+ """
178
+ # Create output directory if it doesn't exist
179
+ os.makedirs(output_dir, exist_ok=True)
180
+
181
+ # Generate filename with user_id and timestamp
182
+ timestamp = self.config.created_at.strftime("%Y%m%d_%H%M%S")
183
+ filename = f"{self.config.user_id}_{timestamp}_chat_history.json"
184
+ filepath = os.path.join(output_dir, filename)
185
+
186
+ # Prepare export data
187
+ export_data = ChatHistory(
188
+ user_id=self.config.user_id,
189
+ session_id=self.config.session_id,
190
+ created_at=self.config.created_at,
191
+ total_messages=len(messages),
192
+ chat_history=messages,
193
+ )
194
+
195
+ # Write to JSON file
196
+ with open(filepath, "w", encoding="utf-8") as f:
197
+ f.write(export_data.model_dump_json(indent=4, exclude_none=True, warnings="none"))
198
+
199
+ logger.info(f"Chat history exported to {filepath}")
200
+ return filepath
File without changes
memos/mem_cube/base.py ADDED
@@ -0,0 +1,29 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import TYPE_CHECKING
3
+
4
+ from memos.configs.mem_cube import BaseMemCubeConfig
5
+
6
+
7
+ if TYPE_CHECKING:
8
+ from memos.memories.activation.base import BaseActMemory
9
+ from memos.memories.parametric.base import BaseParaMemory
10
+ from memos.memories.textual.base import BaseTextMemory
11
+
12
+
13
+ class BaseMemCube(ABC):
14
+ """Base class for all MemCube implementations."""
15
+
16
+ @abstractmethod
17
+ def __init__(self, config: BaseMemCubeConfig):
18
+ """Initialize the MemCube with the given configuration."""
19
+ self.text_mem: BaseTextMemory
20
+ self.act_mem: BaseActMemory
21
+ self.para_mem: BaseParaMemory
22
+
23
+ @abstractmethod
24
+ def load(self, dir: str) -> None:
25
+ """Load memories from a directory."""
26
+
27
+ @abstractmethod
28
+ def dump(self, dir: str) -> None:
29
+ """Dump memories to a directory."""
@@ -0,0 +1,146 @@
1
+ import os
2
+
3
+ from memos.configs.mem_cube import GeneralMemCubeConfig
4
+ from memos.configs.utils import get_json_file_model_schema
5
+ from memos.exceptions import ConfigurationError, MemCubeError
6
+ from memos.log import get_logger
7
+ from memos.mem_cube.base import BaseMemCube
8
+ from memos.mem_cube.utils import download_repo
9
+ from memos.memories.activation.base import BaseActMemory
10
+ from memos.memories.factory import MemoryFactory
11
+ from memos.memories.parametric.base import BaseParaMemory
12
+ from memos.memories.textual.base import BaseTextMemory
13
+
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ class GeneralMemCube(BaseMemCube):
19
+ """MemCube is a box for loading and dumping three types of memories."""
20
+
21
+ def __init__(self, config: GeneralMemCubeConfig):
22
+ """Initialize the MemCube with a configuration."""
23
+ self.config = config
24
+ self._text_mem: BaseTextMemory | None = (
25
+ MemoryFactory.from_config(config.text_mem)
26
+ if config.text_mem.backend != "uninitialized"
27
+ else None
28
+ )
29
+ self._act_mem: BaseActMemory | None = (
30
+ MemoryFactory.from_config(config.act_mem)
31
+ if config.act_mem.backend != "uninitialized"
32
+ else None
33
+ )
34
+ self._para_mem: BaseParaMemory | None = (
35
+ MemoryFactory.from_config(config.para_mem)
36
+ if config.para_mem.backend != "uninitialized"
37
+ else None
38
+ )
39
+
40
+ def load(self, dir: str) -> None:
41
+ """Load memories.
42
+ Args:
43
+ dir (str): The directory containing the memory files.
44
+ """
45
+ loaded_schema = get_json_file_model_schema(os.path.join(dir, self.config.config_filename))
46
+ if loaded_schema != self.config.model_schema:
47
+ raise ConfigurationError(
48
+ f"Configuration schema mismatch. Expected {self.config.model_schema}, "
49
+ f"but found {loaded_schema}."
50
+ )
51
+ self.text_mem.load(dir) if self.text_mem else None
52
+ self.act_mem.load(dir) if self.act_mem else None
53
+ self.para_mem.load(dir) if self.para_mem else None
54
+
55
+ logger.info(f"MemCube loaded successfully from {dir}")
56
+
57
+ def dump(self, dir: str) -> None:
58
+ """Dump memories.
59
+ Args:
60
+ dir (str): The directory where the memory files will be saved.
61
+ """
62
+ if os.path.exists(dir) and os.listdir(dir):
63
+ raise MemCubeError(
64
+ f"Directory {dir} is not empty. Please provide an empty directory for dumping."
65
+ )
66
+
67
+ self.config.to_json_file(os.path.join(dir, self.config.config_filename))
68
+ self.text_mem.dump(dir) if self.text_mem else None
69
+ self.act_mem.dump(dir) if self.act_mem else None
70
+ self.para_mem.dump(dir) if self.para_mem else None
71
+
72
+ logger.info(f"MemCube dumped successfully to {dir}")
73
+
74
+ @staticmethod
75
+ def init_from_dir(dir: str) -> "GeneralMemCube":
76
+ """Create a MemCube instance from a MemCube directory.
77
+
78
+ Args:
79
+ dir (str): The directory containing the memory files.
80
+
81
+ Returns:
82
+ MemCube: An instance of MemCube loaded with memories from the specified directory.
83
+ """
84
+ config_path = os.path.join(dir, "config.json")
85
+ config = GeneralMemCubeConfig.from_json_file(config_path)
86
+ mem_cube = GeneralMemCube(config)
87
+ mem_cube.load(dir)
88
+ return mem_cube
89
+
90
+ @staticmethod
91
+ def init_from_remote_repo(
92
+ cube_id: str, base_url: str = "https://huggingface.co/datasets"
93
+ ) -> "GeneralMemCube":
94
+ """Create a MemCube instance from a remote repository.
95
+
96
+ Args:
97
+ repo (str): The repository name.
98
+ base_url (str): The base URL of the remote repository.
99
+
100
+ Returns:
101
+ MemCube: An instance of MemCube loaded with memories from the specified remote repository.
102
+ """
103
+ dir = download_repo(cube_id, base_url)
104
+ return GeneralMemCube.init_from_dir(dir)
105
+
106
+ @property
107
+ def text_mem(self) -> "BaseTextMemory | None":
108
+ """Get the textual memory."""
109
+ if self._text_mem is None:
110
+ logger.warning("Textual memory is not initialized. Returning None.")
111
+ return self._text_mem
112
+
113
+ @text_mem.setter
114
+ def text_mem(self, value: BaseTextMemory) -> None:
115
+ """Set the textual memory."""
116
+ if not isinstance(value, BaseTextMemory):
117
+ raise TypeError(f"Expected BaseTextMemory, got {type(value).__name__}")
118
+ self._text_mem = value
119
+
120
+ @property
121
+ def act_mem(self) -> "BaseActMemory | None":
122
+ """Get the activation memory."""
123
+ if self._act_mem is None:
124
+ logger.warning("Activation memory is not initialized. Returning None.")
125
+ return self._act_mem
126
+
127
+ @act_mem.setter
128
+ def act_mem(self, value: BaseActMemory) -> None:
129
+ """Set the activation memory."""
130
+ if not isinstance(value, BaseActMemory):
131
+ raise TypeError(f"Expected BaseActMemory, got {type(value).__name__}")
132
+ self._act_mem = value
133
+
134
+ @property
135
+ def para_mem(self) -> "BaseParaMemory | None":
136
+ """Get the parametric memory."""
137
+ if self._para_mem is None:
138
+ logger.warning("Parametric memory is not initialized. Returning None.")
139
+ return self._para_mem
140
+
141
+ @para_mem.setter
142
+ def para_mem(self, value: BaseParaMemory) -> None:
143
+ """Set the parametric memory."""
144
+ if not isinstance(value, BaseParaMemory):
145
+ raise TypeError(f"Expected BaseParaMemory, got {type(value).__name__}")
146
+ self._para_mem = value
@@ -0,0 +1,24 @@
1
+ import subprocess
2
+ import tempfile
3
+
4
+
5
+ def download_repo(repo: str, base_url: str, dir: str | None = None) -> str:
6
+ """Download a repository from a remote source.
7
+
8
+ Args:
9
+ repo (str): The repository name.
10
+ base_url (str): The base URL of the remote repository.
11
+ dir (str, optional): The directory where the repository will be downloaded. If None, a temporary directory will be created.
12
+ If a directory is provided, it will be used instead of creating a temporary one.
13
+
14
+ Returns:
15
+ str: The local directory where the repository is downloaded.
16
+ """
17
+ if dir is None:
18
+ dir = tempfile.mkdtemp()
19
+ repo_url = f"{base_url}/{repo}"
20
+
21
+ # Clone the repo
22
+ subprocess.run(["git", "clone", repo_url, dir], check=True)
23
+
24
+ return dir
memos/mem_os/client.py ADDED
@@ -0,0 +1,5 @@
1
+ # TODO: @Li Ji
2
+
3
+
4
+ class ClientMOS:
5
+ pass