MemoryOS 2.0.3__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.
- memoryos-2.0.3.dist-info/METADATA +418 -0
- memoryos-2.0.3.dist-info/RECORD +315 -0
- memoryos-2.0.3.dist-info/WHEEL +4 -0
- memoryos-2.0.3.dist-info/entry_points.txt +3 -0
- memoryos-2.0.3.dist-info/licenses/LICENSE +201 -0
- memos/__init__.py +20 -0
- memos/api/client.py +571 -0
- memos/api/config.py +1018 -0
- memos/api/context/dependencies.py +50 -0
- memos/api/exceptions.py +53 -0
- memos/api/handlers/__init__.py +62 -0
- memos/api/handlers/add_handler.py +158 -0
- memos/api/handlers/base_handler.py +194 -0
- memos/api/handlers/chat_handler.py +1401 -0
- memos/api/handlers/component_init.py +388 -0
- memos/api/handlers/config_builders.py +190 -0
- memos/api/handlers/feedback_handler.py +93 -0
- memos/api/handlers/formatters_handler.py +237 -0
- memos/api/handlers/memory_handler.py +316 -0
- memos/api/handlers/scheduler_handler.py +497 -0
- memos/api/handlers/search_handler.py +222 -0
- memos/api/handlers/suggestion_handler.py +117 -0
- memos/api/mcp_serve.py +614 -0
- memos/api/middleware/request_context.py +101 -0
- memos/api/product_api.py +38 -0
- memos/api/product_models.py +1206 -0
- memos/api/routers/__init__.py +1 -0
- memos/api/routers/product_router.py +477 -0
- memos/api/routers/server_router.py +394 -0
- memos/api/server_api.py +44 -0
- memos/api/start_api.py +433 -0
- memos/chunkers/__init__.py +4 -0
- memos/chunkers/base.py +24 -0
- memos/chunkers/charactertext_chunker.py +41 -0
- memos/chunkers/factory.py +24 -0
- memos/chunkers/markdown_chunker.py +62 -0
- memos/chunkers/sentence_chunker.py +54 -0
- memos/chunkers/simple_chunker.py +50 -0
- memos/cli.py +113 -0
- memos/configs/__init__.py +0 -0
- memos/configs/base.py +82 -0
- memos/configs/chunker.py +59 -0
- memos/configs/embedder.py +88 -0
- memos/configs/graph_db.py +236 -0
- memos/configs/internet_retriever.py +100 -0
- memos/configs/llm.py +151 -0
- memos/configs/mem_agent.py +54 -0
- memos/configs/mem_chat.py +81 -0
- memos/configs/mem_cube.py +105 -0
- memos/configs/mem_os.py +83 -0
- memos/configs/mem_reader.py +91 -0
- memos/configs/mem_scheduler.py +385 -0
- memos/configs/mem_user.py +70 -0
- memos/configs/memory.py +324 -0
- memos/configs/parser.py +38 -0
- memos/configs/reranker.py +18 -0
- memos/configs/utils.py +8 -0
- memos/configs/vec_db.py +80 -0
- memos/context/context.py +355 -0
- memos/dependency.py +52 -0
- memos/deprecation.py +262 -0
- memos/embedders/__init__.py +0 -0
- memos/embedders/ark.py +95 -0
- memos/embedders/base.py +106 -0
- memos/embedders/factory.py +29 -0
- memos/embedders/ollama.py +77 -0
- memos/embedders/sentence_transformer.py +49 -0
- memos/embedders/universal_api.py +51 -0
- memos/exceptions.py +30 -0
- memos/graph_dbs/__init__.py +0 -0
- memos/graph_dbs/base.py +274 -0
- memos/graph_dbs/factory.py +27 -0
- memos/graph_dbs/item.py +46 -0
- memos/graph_dbs/nebular.py +1794 -0
- memos/graph_dbs/neo4j.py +1942 -0
- memos/graph_dbs/neo4j_community.py +1058 -0
- memos/graph_dbs/polardb.py +5446 -0
- memos/hello_world.py +97 -0
- memos/llms/__init__.py +0 -0
- memos/llms/base.py +25 -0
- memos/llms/deepseek.py +13 -0
- memos/llms/factory.py +38 -0
- memos/llms/hf.py +443 -0
- memos/llms/hf_singleton.py +114 -0
- memos/llms/ollama.py +135 -0
- memos/llms/openai.py +222 -0
- memos/llms/openai_new.py +198 -0
- memos/llms/qwen.py +13 -0
- memos/llms/utils.py +14 -0
- memos/llms/vllm.py +218 -0
- memos/log.py +237 -0
- memos/mem_agent/base.py +19 -0
- memos/mem_agent/deepsearch_agent.py +391 -0
- memos/mem_agent/factory.py +36 -0
- memos/mem_chat/__init__.py +0 -0
- memos/mem_chat/base.py +30 -0
- memos/mem_chat/factory.py +21 -0
- memos/mem_chat/simple.py +200 -0
- memos/mem_cube/__init__.py +0 -0
- memos/mem_cube/base.py +30 -0
- memos/mem_cube/general.py +240 -0
- memos/mem_cube/navie.py +172 -0
- memos/mem_cube/utils.py +169 -0
- memos/mem_feedback/base.py +15 -0
- memos/mem_feedback/feedback.py +1192 -0
- memos/mem_feedback/simple_feedback.py +40 -0
- memos/mem_feedback/utils.py +230 -0
- memos/mem_os/client.py +5 -0
- memos/mem_os/core.py +1203 -0
- memos/mem_os/main.py +582 -0
- memos/mem_os/product.py +1608 -0
- memos/mem_os/product_server.py +455 -0
- memos/mem_os/utils/default_config.py +359 -0
- memos/mem_os/utils/format_utils.py +1403 -0
- memos/mem_os/utils/reference_utils.py +162 -0
- memos/mem_reader/__init__.py +0 -0
- memos/mem_reader/base.py +47 -0
- memos/mem_reader/factory.py +53 -0
- memos/mem_reader/memory.py +298 -0
- memos/mem_reader/multi_modal_struct.py +965 -0
- memos/mem_reader/read_multi_modal/__init__.py +43 -0
- memos/mem_reader/read_multi_modal/assistant_parser.py +311 -0
- memos/mem_reader/read_multi_modal/base.py +273 -0
- memos/mem_reader/read_multi_modal/file_content_parser.py +826 -0
- memos/mem_reader/read_multi_modal/image_parser.py +359 -0
- memos/mem_reader/read_multi_modal/multi_modal_parser.py +252 -0
- memos/mem_reader/read_multi_modal/string_parser.py +139 -0
- memos/mem_reader/read_multi_modal/system_parser.py +327 -0
- memos/mem_reader/read_multi_modal/text_content_parser.py +131 -0
- memos/mem_reader/read_multi_modal/tool_parser.py +210 -0
- memos/mem_reader/read_multi_modal/user_parser.py +218 -0
- memos/mem_reader/read_multi_modal/utils.py +358 -0
- memos/mem_reader/simple_struct.py +912 -0
- memos/mem_reader/strategy_struct.py +163 -0
- memos/mem_reader/utils.py +157 -0
- memos/mem_scheduler/__init__.py +0 -0
- memos/mem_scheduler/analyzer/__init__.py +0 -0
- memos/mem_scheduler/analyzer/api_analyzer.py +714 -0
- memos/mem_scheduler/analyzer/eval_analyzer.py +219 -0
- memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +571 -0
- memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
- memos/mem_scheduler/base_scheduler.py +1319 -0
- memos/mem_scheduler/general_modules/__init__.py +0 -0
- memos/mem_scheduler/general_modules/api_misc.py +137 -0
- memos/mem_scheduler/general_modules/base.py +80 -0
- memos/mem_scheduler/general_modules/init_components_for_scheduler.py +425 -0
- memos/mem_scheduler/general_modules/misc.py +313 -0
- memos/mem_scheduler/general_modules/scheduler_logger.py +389 -0
- memos/mem_scheduler/general_modules/task_threads.py +315 -0
- memos/mem_scheduler/general_scheduler.py +1495 -0
- memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
- memos/mem_scheduler/memory_manage_modules/memory_filter.py +306 -0
- memos/mem_scheduler/memory_manage_modules/retriever.py +547 -0
- memos/mem_scheduler/monitors/__init__.py +0 -0
- memos/mem_scheduler/monitors/dispatcher_monitor.py +366 -0
- memos/mem_scheduler/monitors/general_monitor.py +394 -0
- memos/mem_scheduler/monitors/task_schedule_monitor.py +254 -0
- memos/mem_scheduler/optimized_scheduler.py +410 -0
- memos/mem_scheduler/orm_modules/__init__.py +0 -0
- memos/mem_scheduler/orm_modules/api_redis_model.py +518 -0
- memos/mem_scheduler/orm_modules/base_model.py +729 -0
- memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
- memos/mem_scheduler/orm_modules/redis_model.py +699 -0
- memos/mem_scheduler/scheduler_factory.py +23 -0
- memos/mem_scheduler/schemas/__init__.py +0 -0
- memos/mem_scheduler/schemas/analyzer_schemas.py +52 -0
- memos/mem_scheduler/schemas/api_schemas.py +233 -0
- memos/mem_scheduler/schemas/general_schemas.py +55 -0
- memos/mem_scheduler/schemas/message_schemas.py +173 -0
- memos/mem_scheduler/schemas/monitor_schemas.py +406 -0
- memos/mem_scheduler/schemas/task_schemas.py +132 -0
- memos/mem_scheduler/task_schedule_modules/__init__.py +0 -0
- memos/mem_scheduler/task_schedule_modules/dispatcher.py +740 -0
- memos/mem_scheduler/task_schedule_modules/local_queue.py +247 -0
- memos/mem_scheduler/task_schedule_modules/orchestrator.py +74 -0
- memos/mem_scheduler/task_schedule_modules/redis_queue.py +1385 -0
- memos/mem_scheduler/task_schedule_modules/task_queue.py +162 -0
- memos/mem_scheduler/utils/__init__.py +0 -0
- memos/mem_scheduler/utils/api_utils.py +77 -0
- memos/mem_scheduler/utils/config_utils.py +100 -0
- memos/mem_scheduler/utils/db_utils.py +50 -0
- memos/mem_scheduler/utils/filter_utils.py +176 -0
- memos/mem_scheduler/utils/metrics.py +125 -0
- memos/mem_scheduler/utils/misc_utils.py +290 -0
- memos/mem_scheduler/utils/monitor_event_utils.py +67 -0
- memos/mem_scheduler/utils/status_tracker.py +229 -0
- memos/mem_scheduler/webservice_modules/__init__.py +0 -0
- memos/mem_scheduler/webservice_modules/rabbitmq_service.py +485 -0
- memos/mem_scheduler/webservice_modules/redis_service.py +380 -0
- memos/mem_user/factory.py +94 -0
- memos/mem_user/mysql_persistent_user_manager.py +271 -0
- memos/mem_user/mysql_user_manager.py +502 -0
- memos/mem_user/persistent_factory.py +98 -0
- memos/mem_user/persistent_user_manager.py +260 -0
- memos/mem_user/redis_persistent_user_manager.py +225 -0
- memos/mem_user/user_manager.py +488 -0
- memos/memories/__init__.py +0 -0
- memos/memories/activation/__init__.py +0 -0
- memos/memories/activation/base.py +42 -0
- memos/memories/activation/item.py +56 -0
- memos/memories/activation/kv.py +292 -0
- memos/memories/activation/vllmkv.py +219 -0
- memos/memories/base.py +19 -0
- memos/memories/factory.py +42 -0
- memos/memories/parametric/__init__.py +0 -0
- memos/memories/parametric/base.py +19 -0
- memos/memories/parametric/item.py +11 -0
- memos/memories/parametric/lora.py +41 -0
- memos/memories/textual/__init__.py +0 -0
- memos/memories/textual/base.py +92 -0
- memos/memories/textual/general.py +236 -0
- memos/memories/textual/item.py +304 -0
- memos/memories/textual/naive.py +187 -0
- memos/memories/textual/prefer_text_memory/__init__.py +0 -0
- memos/memories/textual/prefer_text_memory/adder.py +504 -0
- memos/memories/textual/prefer_text_memory/config.py +106 -0
- memos/memories/textual/prefer_text_memory/extractor.py +221 -0
- memos/memories/textual/prefer_text_memory/factory.py +85 -0
- memos/memories/textual/prefer_text_memory/retrievers.py +177 -0
- memos/memories/textual/prefer_text_memory/spliter.py +132 -0
- memos/memories/textual/prefer_text_memory/utils.py +93 -0
- memos/memories/textual/preference.py +344 -0
- memos/memories/textual/simple_preference.py +161 -0
- memos/memories/textual/simple_tree.py +69 -0
- memos/memories/textual/tree.py +459 -0
- memos/memories/textual/tree_text_memory/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/organize/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/organize/handler.py +184 -0
- memos/memories/textual/tree_text_memory/organize/manager.py +518 -0
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +238 -0
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +622 -0
- memos/memories/textual/tree_text_memory/retrieve/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/retrieve/advanced_searcher.py +364 -0
- memos/memories/textual/tree_text_memory/retrieve/bm25_util.py +186 -0
- memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +419 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +270 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +102 -0
- memos/memories/textual/tree_text_memory/retrieve/reasoner.py +61 -0
- memos/memories/textual/tree_text_memory/retrieve/recall.py +497 -0
- memos/memories/textual/tree_text_memory/retrieve/reranker.py +111 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +16 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieve_utils.py +472 -0
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +848 -0
- memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +135 -0
- memos/memories/textual/tree_text_memory/retrieve/utils.py +54 -0
- memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +387 -0
- memos/memos_tools/dinding_report_bot.py +453 -0
- memos/memos_tools/lockfree_dict.py +120 -0
- memos/memos_tools/notification_service.py +44 -0
- memos/memos_tools/notification_utils.py +142 -0
- memos/memos_tools/singleton.py +174 -0
- memos/memos_tools/thread_safe_dict.py +310 -0
- memos/memos_tools/thread_safe_dict_segment.py +382 -0
- memos/multi_mem_cube/__init__.py +0 -0
- memos/multi_mem_cube/composite_cube.py +86 -0
- memos/multi_mem_cube/single_cube.py +874 -0
- memos/multi_mem_cube/views.py +54 -0
- memos/parsers/__init__.py +0 -0
- memos/parsers/base.py +15 -0
- memos/parsers/factory.py +21 -0
- memos/parsers/markitdown.py +28 -0
- memos/reranker/__init__.py +4 -0
- memos/reranker/base.py +25 -0
- memos/reranker/concat.py +103 -0
- memos/reranker/cosine_local.py +102 -0
- memos/reranker/factory.py +72 -0
- memos/reranker/http_bge.py +324 -0
- memos/reranker/http_bge_strategy.py +327 -0
- memos/reranker/noop.py +19 -0
- memos/reranker/strategies/__init__.py +4 -0
- memos/reranker/strategies/base.py +61 -0
- memos/reranker/strategies/concat_background.py +94 -0
- memos/reranker/strategies/concat_docsource.py +110 -0
- memos/reranker/strategies/dialogue_common.py +109 -0
- memos/reranker/strategies/factory.py +31 -0
- memos/reranker/strategies/single_turn.py +107 -0
- memos/reranker/strategies/singleturn_outmem.py +98 -0
- memos/settings.py +10 -0
- memos/templates/__init__.py +0 -0
- memos/templates/advanced_search_prompts.py +211 -0
- memos/templates/cloud_service_prompt.py +107 -0
- memos/templates/instruction_completion.py +66 -0
- memos/templates/mem_agent_prompts.py +85 -0
- memos/templates/mem_feedback_prompts.py +822 -0
- memos/templates/mem_reader_prompts.py +1096 -0
- memos/templates/mem_reader_strategy_prompts.py +238 -0
- memos/templates/mem_scheduler_prompts.py +626 -0
- memos/templates/mem_search_prompts.py +93 -0
- memos/templates/mos_prompts.py +403 -0
- memos/templates/prefer_complete_prompt.py +735 -0
- memos/templates/tool_mem_prompts.py +139 -0
- memos/templates/tree_reorganize_prompts.py +230 -0
- memos/types/__init__.py +34 -0
- memos/types/general_types.py +151 -0
- memos/types/openai_chat_completion_types/__init__.py +15 -0
- memos/types/openai_chat_completion_types/chat_completion_assistant_message_param.py +56 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_image_param.py +27 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_input_audio_param.py +23 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_param.py +43 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_refusal_param.py +16 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_text_param.py +16 -0
- memos/types/openai_chat_completion_types/chat_completion_message_custom_tool_call_param.py +27 -0
- memos/types/openai_chat_completion_types/chat_completion_message_function_tool_call_param.py +32 -0
- memos/types/openai_chat_completion_types/chat_completion_message_param.py +18 -0
- memos/types/openai_chat_completion_types/chat_completion_message_tool_call_union_param.py +15 -0
- memos/types/openai_chat_completion_types/chat_completion_system_message_param.py +36 -0
- memos/types/openai_chat_completion_types/chat_completion_tool_message_param.py +30 -0
- memos/types/openai_chat_completion_types/chat_completion_user_message_param.py +34 -0
- memos/utils.py +123 -0
- memos/vec_dbs/__init__.py +0 -0
- memos/vec_dbs/base.py +117 -0
- memos/vec_dbs/factory.py +23 -0
- memos/vec_dbs/item.py +50 -0
- memos/vec_dbs/milvus.py +654 -0
- memos/vec_dbs/qdrant.py +355 -0
memos/mem_cube/base.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
self.pref_mem: BaseTextMemory
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def load(self, dir: str) -> None:
|
|
26
|
+
"""Load memories from a directory."""
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def dump(self, dir: str) -> None:
|
|
30
|
+
"""Dump memories to a directory."""
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from memos.configs.mem_cube import GeneralMemCubeConfig
|
|
7
|
+
from memos.configs.utils import get_json_file_model_schema
|
|
8
|
+
from memos.exceptions import ConfigurationError, MemCubeError
|
|
9
|
+
from memos.log import get_logger
|
|
10
|
+
from memos.mem_cube.base import BaseMemCube
|
|
11
|
+
from memos.mem_cube.utils import download_repo, merge_config_with_default
|
|
12
|
+
from memos.memories.activation.base import BaseActMemory
|
|
13
|
+
from memos.memories.factory import MemoryFactory
|
|
14
|
+
from memos.memories.parametric.base import BaseParaMemory
|
|
15
|
+
from memos.memories.textual.base import BaseTextMemory
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logger = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GeneralMemCube(BaseMemCube):
|
|
22
|
+
"""MemCube is a box for loading and dumping three types of memories."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, config: GeneralMemCubeConfig):
|
|
25
|
+
"""Initialize the MemCube with a configuration."""
|
|
26
|
+
self.config = config
|
|
27
|
+
time_start = time.time()
|
|
28
|
+
self._text_mem: BaseTextMemory | None = (
|
|
29
|
+
MemoryFactory.from_config(config.text_mem)
|
|
30
|
+
if config.text_mem.backend != "uninitialized"
|
|
31
|
+
else None
|
|
32
|
+
)
|
|
33
|
+
logger.info(f"init_text_mem in {time.time() - time_start} seconds")
|
|
34
|
+
self._act_mem: BaseActMemory | None = (
|
|
35
|
+
MemoryFactory.from_config(config.act_mem)
|
|
36
|
+
if config.act_mem.backend != "uninitialized"
|
|
37
|
+
else None
|
|
38
|
+
)
|
|
39
|
+
self._para_mem: BaseParaMemory | None = (
|
|
40
|
+
MemoryFactory.from_config(config.para_mem)
|
|
41
|
+
if config.para_mem.backend != "uninitialized"
|
|
42
|
+
else None
|
|
43
|
+
)
|
|
44
|
+
self._pref_mem: BaseTextMemory | None = (
|
|
45
|
+
MemoryFactory.from_config(config.pref_mem)
|
|
46
|
+
if config.pref_mem.backend != "uninitialized"
|
|
47
|
+
else None
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def load(
|
|
51
|
+
self,
|
|
52
|
+
dir: str,
|
|
53
|
+
memory_types: list[Literal["text_mem", "act_mem", "para_mem", "pref_mem"]] | None = None,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Load memories.
|
|
56
|
+
Args:
|
|
57
|
+
dir (str): The directory containing the memory files.
|
|
58
|
+
memory_types (list[str], optional): List of memory types to load.
|
|
59
|
+
If None, loads all available memory types.
|
|
60
|
+
Options: ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
61
|
+
"""
|
|
62
|
+
loaded_schema = get_json_file_model_schema(os.path.join(dir, self.config.config_filename))
|
|
63
|
+
if loaded_schema != self.config.model_schema:
|
|
64
|
+
raise ConfigurationError(
|
|
65
|
+
f"Configuration schema mismatch. Expected {self.config.model_schema}, "
|
|
66
|
+
f"but found {loaded_schema}."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# If no specific memory types specified, load all
|
|
70
|
+
if memory_types is None:
|
|
71
|
+
memory_types = ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
72
|
+
|
|
73
|
+
# Load specified memory types
|
|
74
|
+
if "text_mem" in memory_types and self.text_mem:
|
|
75
|
+
self.text_mem.load(dir)
|
|
76
|
+
logger.debug(f"Loaded text_mem from {dir}")
|
|
77
|
+
|
|
78
|
+
if "act_mem" in memory_types and self.act_mem:
|
|
79
|
+
self.act_mem.load(dir)
|
|
80
|
+
logger.info(f"Loaded act_mem from {dir}")
|
|
81
|
+
|
|
82
|
+
if "para_mem" in memory_types and self.para_mem:
|
|
83
|
+
self.para_mem.load(dir)
|
|
84
|
+
logger.info(f"Loaded para_mem from {dir}")
|
|
85
|
+
|
|
86
|
+
if "pref_mem" in memory_types and self.pref_mem:
|
|
87
|
+
self.pref_mem.load(dir)
|
|
88
|
+
logger.info(f"Loaded pref_mem from {dir}")
|
|
89
|
+
|
|
90
|
+
logger.info(f"MemCube loaded successfully from {dir} (types: {memory_types})")
|
|
91
|
+
|
|
92
|
+
def dump(
|
|
93
|
+
self,
|
|
94
|
+
dir: str,
|
|
95
|
+
memory_types: list[Literal["text_mem", "act_mem", "para_mem", "pref_mem"]] | None = None,
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Dump memories.
|
|
98
|
+
Args:
|
|
99
|
+
dir (str): The directory where the memory files will be saved.
|
|
100
|
+
memory_types (list[str], optional): List of memory types to dump.
|
|
101
|
+
If None, dumps all available memory types.
|
|
102
|
+
Options: ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
103
|
+
"""
|
|
104
|
+
if os.path.exists(dir) and os.listdir(dir):
|
|
105
|
+
raise MemCubeError(
|
|
106
|
+
f"Directory {dir} is not empty. Please provide an empty directory for dumping."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Always dump config
|
|
110
|
+
self.config.to_json_file(os.path.join(dir, self.config.config_filename))
|
|
111
|
+
|
|
112
|
+
# If no specific memory types specified, dump all
|
|
113
|
+
if memory_types is None:
|
|
114
|
+
memory_types = ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
115
|
+
|
|
116
|
+
# Dump specified memory types
|
|
117
|
+
if "text_mem" in memory_types and self.text_mem:
|
|
118
|
+
self.text_mem.dump(dir)
|
|
119
|
+
logger.info(f"Dumped text_mem to {dir}")
|
|
120
|
+
|
|
121
|
+
if "act_mem" in memory_types and self.act_mem:
|
|
122
|
+
self.act_mem.dump(dir)
|
|
123
|
+
logger.info(f"Dumped act_mem to {dir}")
|
|
124
|
+
|
|
125
|
+
if "para_mem" in memory_types and self.para_mem:
|
|
126
|
+
self.para_mem.dump(dir)
|
|
127
|
+
logger.info(f"Dumped para_mem to {dir}")
|
|
128
|
+
|
|
129
|
+
if "pref_mem" in memory_types and self.pref_mem:
|
|
130
|
+
self.pref_mem.dump(dir)
|
|
131
|
+
logger.info(f"Dumped pref_mem to {dir}")
|
|
132
|
+
|
|
133
|
+
logger.info(f"MemCube dumped successfully to {dir} (types: {memory_types})")
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def init_from_dir(
|
|
137
|
+
dir: str,
|
|
138
|
+
memory_types: list[Literal["text_mem", "act_mem", "para_mem", "pref_mem"]] | None = None,
|
|
139
|
+
default_config: GeneralMemCubeConfig | None = None,
|
|
140
|
+
) -> "GeneralMemCube":
|
|
141
|
+
"""Create a MemCube instance from a MemCube directory.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
dir (str): The directory containing the memory files.
|
|
145
|
+
memory_types (list[str], optional): List of memory types to load.
|
|
146
|
+
If None, loads all available memory types.
|
|
147
|
+
default_config (GeneralMemCubeConfig, optional): Default configuration to merge with existing config.
|
|
148
|
+
If provided, will merge general settings while preserving critical user-specific fields.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
MemCube: An instance of MemCube loaded with memories from the specified directory.
|
|
152
|
+
"""
|
|
153
|
+
config_path = os.path.join(dir, "config.json")
|
|
154
|
+
config = GeneralMemCubeConfig.from_json_file(config_path)
|
|
155
|
+
|
|
156
|
+
# Merge with default config if provided
|
|
157
|
+
if default_config is not None:
|
|
158
|
+
config = merge_config_with_default(config, default_config)
|
|
159
|
+
logger.info(f"Applied default config to cube {config.cube_id}")
|
|
160
|
+
mem_cube = GeneralMemCube(config)
|
|
161
|
+
mem_cube.load(dir, memory_types)
|
|
162
|
+
return mem_cube
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def init_from_remote_repo(
|
|
166
|
+
cube_id: str,
|
|
167
|
+
base_url: str = "https://huggingface.co/datasets",
|
|
168
|
+
memory_types: list[Literal["text_mem", "act_mem", "para_mem", "pref_mem"]] | None = None,
|
|
169
|
+
default_config: GeneralMemCubeConfig | None = None,
|
|
170
|
+
) -> "GeneralMemCube":
|
|
171
|
+
"""Create a MemCube instance from a remote repository.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
cube_id (str): The repository name.
|
|
175
|
+
base_url (str): The base URL of the remote repository.
|
|
176
|
+
memory_types (list[str], optional): List of memory types to load.
|
|
177
|
+
If None, loads all available memory types.
|
|
178
|
+
default_config (GeneralMemCubeConfig, optional): Default configuration to merge with existing config.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
MemCube: An instance of MemCube loaded with memories from the specified remote repository.
|
|
182
|
+
"""
|
|
183
|
+
dir = download_repo(cube_id, base_url)
|
|
184
|
+
return GeneralMemCube.init_from_dir(dir, memory_types, default_config)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def text_mem(self) -> "BaseTextMemory | None":
|
|
188
|
+
"""Get the textual memory."""
|
|
189
|
+
if self._text_mem is None:
|
|
190
|
+
logger.warning("Textual memory is not initialized. Returning None.")
|
|
191
|
+
return self._text_mem
|
|
192
|
+
|
|
193
|
+
@text_mem.setter
|
|
194
|
+
def text_mem(self, value: BaseTextMemory) -> None:
|
|
195
|
+
"""Set the textual memory."""
|
|
196
|
+
if not isinstance(value, BaseTextMemory):
|
|
197
|
+
raise TypeError(f"Expected BaseTextMemory, got {type(value).__name__}")
|
|
198
|
+
self._text_mem = value
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def act_mem(self) -> "BaseActMemory | None":
|
|
202
|
+
"""Get the activation memory."""
|
|
203
|
+
if self._act_mem is None:
|
|
204
|
+
logger.warning("Activation memory is not initialized. Returning None.")
|
|
205
|
+
return self._act_mem
|
|
206
|
+
|
|
207
|
+
@act_mem.setter
|
|
208
|
+
def act_mem(self, value: BaseActMemory) -> None:
|
|
209
|
+
"""Set the activation memory."""
|
|
210
|
+
if not isinstance(value, BaseActMemory):
|
|
211
|
+
raise TypeError(f"Expected BaseActMemory, got {type(value).__name__}")
|
|
212
|
+
self._act_mem = value
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def para_mem(self) -> "BaseParaMemory | None":
|
|
216
|
+
"""Get the parametric memory."""
|
|
217
|
+
if self._para_mem is None:
|
|
218
|
+
logger.warning("Parametric memory is not initialized. Returning None.")
|
|
219
|
+
return self._para_mem
|
|
220
|
+
|
|
221
|
+
@para_mem.setter
|
|
222
|
+
def para_mem(self, value: BaseParaMemory) -> None:
|
|
223
|
+
"""Set the parametric memory."""
|
|
224
|
+
if not isinstance(value, BaseParaMemory):
|
|
225
|
+
raise TypeError(f"Expected BaseParaMemory, got {type(value).__name__}")
|
|
226
|
+
self._para_mem = value
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def pref_mem(self) -> "BaseTextMemory | None":
|
|
230
|
+
"""Get the preference memory."""
|
|
231
|
+
if self._pref_mem is None:
|
|
232
|
+
logger.warning("Preference memory is not initialized. Returning None.")
|
|
233
|
+
return self._pref_mem
|
|
234
|
+
|
|
235
|
+
@pref_mem.setter
|
|
236
|
+
def pref_mem(self, value: BaseTextMemory) -> None:
|
|
237
|
+
"""Set the preference memory."""
|
|
238
|
+
if not isinstance(value, BaseTextMemory):
|
|
239
|
+
raise TypeError(f"Expected BaseTextMemory, got {type(value).__name__}")
|
|
240
|
+
self._pref_mem = value
|
memos/mem_cube/navie.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from memos.configs.utils import get_json_file_model_schema
|
|
6
|
+
from memos.exceptions import ConfigurationError, MemCubeError
|
|
7
|
+
from memos.log import get_logger
|
|
8
|
+
from memos.mem_cube.base import BaseMemCube
|
|
9
|
+
from memos.memories.activation.base import BaseActMemory
|
|
10
|
+
from memos.memories.parametric.base import BaseParaMemory
|
|
11
|
+
from memos.memories.textual.base import BaseTextMemory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NaiveMemCube(BaseMemCube):
|
|
18
|
+
"""MemCube is a box for loading and dumping three types of memories."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
text_mem: BaseTextMemory | None = None,
|
|
23
|
+
pref_mem: BaseTextMemory | None = None,
|
|
24
|
+
act_mem: BaseActMemory | None = None,
|
|
25
|
+
para_mem: BaseParaMemory | None = None,
|
|
26
|
+
):
|
|
27
|
+
"""Initialize the MemCube with memory instances."""
|
|
28
|
+
self._text_mem: BaseTextMemory = text_mem
|
|
29
|
+
self._act_mem: BaseActMemory | None = act_mem
|
|
30
|
+
self._para_mem: BaseParaMemory | None = para_mem
|
|
31
|
+
self._pref_mem: BaseTextMemory | None = pref_mem
|
|
32
|
+
|
|
33
|
+
def load(
|
|
34
|
+
self,
|
|
35
|
+
dir: str,
|
|
36
|
+
memory_types: list[Literal["text_mem", "act_mem", "para_mem", "pref_mem"]] | None = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Load memories.
|
|
39
|
+
Args:
|
|
40
|
+
dir (str): The directory containing the memory files.
|
|
41
|
+
memory_types (list[str], optional): List of memory types to load.
|
|
42
|
+
If None, loads all available memory types.
|
|
43
|
+
Options: ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
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
|
+
|
|
52
|
+
# If no specific memory types specified, load all
|
|
53
|
+
if memory_types is None:
|
|
54
|
+
memory_types = ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
55
|
+
|
|
56
|
+
# Load specified memory types
|
|
57
|
+
if "text_mem" in memory_types and self.text_mem:
|
|
58
|
+
self.text_mem.load(dir)
|
|
59
|
+
logger.debug(f"Loaded text_mem from {dir}")
|
|
60
|
+
|
|
61
|
+
if "act_mem" in memory_types and self.act_mem:
|
|
62
|
+
self.act_mem.load(dir)
|
|
63
|
+
logger.info(f"Loaded act_mem from {dir}")
|
|
64
|
+
|
|
65
|
+
if "para_mem" in memory_types and self.para_mem:
|
|
66
|
+
self.para_mem.load(dir)
|
|
67
|
+
logger.info(f"Loaded para_mem from {dir}")
|
|
68
|
+
|
|
69
|
+
if "pref_mem" in memory_types and self.pref_mem:
|
|
70
|
+
self.pref_mem.load(dir)
|
|
71
|
+
logger.info(f"Loaded pref_mem from {dir}")
|
|
72
|
+
|
|
73
|
+
logger.info(f"MemCube loaded successfully from {dir} (types: {memory_types})")
|
|
74
|
+
|
|
75
|
+
def dump(
|
|
76
|
+
self,
|
|
77
|
+
dir: str,
|
|
78
|
+
memory_types: list[Literal["text_mem", "act_mem", "para_mem", "pref_mem"]] | None = None,
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Dump memories.
|
|
81
|
+
Args:
|
|
82
|
+
dir (str): The directory where the memory files will be saved.
|
|
83
|
+
memory_types (list[str], optional): List of memory types to dump.
|
|
84
|
+
If None, dumps all available memory types.
|
|
85
|
+
Options: ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
86
|
+
"""
|
|
87
|
+
if os.path.exists(dir) and os.listdir(dir):
|
|
88
|
+
raise MemCubeError(
|
|
89
|
+
f"Directory {dir} is not empty. Please provide an empty directory for dumping."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Always dump config
|
|
93
|
+
self.config.to_json_file(os.path.join(dir, self.config.config_filename))
|
|
94
|
+
|
|
95
|
+
# If no specific memory types specified, dump all
|
|
96
|
+
if memory_types is None:
|
|
97
|
+
memory_types = ["text_mem", "act_mem", "para_mem", "pref_mem"]
|
|
98
|
+
|
|
99
|
+
# Dump specified memory types
|
|
100
|
+
if "text_mem" in memory_types and self.text_mem:
|
|
101
|
+
self.text_mem.dump(dir)
|
|
102
|
+
logger.info(f"Dumped text_mem to {dir}")
|
|
103
|
+
|
|
104
|
+
if "act_mem" in memory_types and self.act_mem:
|
|
105
|
+
self.act_mem.dump(dir)
|
|
106
|
+
logger.info(f"Dumped act_mem to {dir}")
|
|
107
|
+
|
|
108
|
+
if "para_mem" in memory_types and self.para_mem:
|
|
109
|
+
self.para_mem.dump(dir)
|
|
110
|
+
logger.info(f"Dumped para_mem to {dir}")
|
|
111
|
+
|
|
112
|
+
if "pref_mem" in memory_types and self.pref_mem:
|
|
113
|
+
self.pref_mem.dump(dir)
|
|
114
|
+
logger.info(f"Dumped pref_mem to {dir}")
|
|
115
|
+
|
|
116
|
+
logger.info(f"MemCube dumped successfully to {dir} (types: {memory_types})")
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def text_mem(self) -> "BaseTextMemory | None":
|
|
120
|
+
"""Get the textual memory."""
|
|
121
|
+
if self._text_mem is None:
|
|
122
|
+
logger.warning("Textual memory is not initialized. Returning None.")
|
|
123
|
+
return self._text_mem
|
|
124
|
+
|
|
125
|
+
@text_mem.setter
|
|
126
|
+
def text_mem(self, value: BaseTextMemory) -> None:
|
|
127
|
+
"""Set the textual memory."""
|
|
128
|
+
if not isinstance(value, BaseTextMemory):
|
|
129
|
+
raise TypeError(f"Expected BaseTextMemory, got {type(value).__name__}")
|
|
130
|
+
self._text_mem = value
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def act_mem(self) -> "BaseActMemory | None":
|
|
134
|
+
"""Get the activation memory."""
|
|
135
|
+
if self._act_mem is None:
|
|
136
|
+
logger.warning("Activation memory is not initialized. Returning None.")
|
|
137
|
+
return self._act_mem
|
|
138
|
+
|
|
139
|
+
@act_mem.setter
|
|
140
|
+
def act_mem(self, value: BaseActMemory) -> None:
|
|
141
|
+
"""Set the activation memory."""
|
|
142
|
+
if not isinstance(value, BaseActMemory):
|
|
143
|
+
raise TypeError(f"Expected BaseActMemory, got {type(value).__name__}")
|
|
144
|
+
self._act_mem = value
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def para_mem(self) -> "BaseParaMemory | None":
|
|
148
|
+
"""Get the parametric memory."""
|
|
149
|
+
if self._para_mem is None:
|
|
150
|
+
logger.warning("Parametric memory is not initialized. Returning None.")
|
|
151
|
+
return self._para_mem
|
|
152
|
+
|
|
153
|
+
@para_mem.setter
|
|
154
|
+
def para_mem(self, value: BaseParaMemory) -> None:
|
|
155
|
+
"""Set the parametric memory."""
|
|
156
|
+
if not isinstance(value, BaseParaMemory):
|
|
157
|
+
raise TypeError(f"Expected BaseParaMemory, got {type(value).__name__}")
|
|
158
|
+
self._para_mem = value
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def pref_mem(self) -> "BaseTextMemory | None":
|
|
162
|
+
"""Get the preference memory."""
|
|
163
|
+
if self._pref_mem is None:
|
|
164
|
+
logger.warning("Preference memory is not initialized. Returning None.")
|
|
165
|
+
return self._pref_mem
|
|
166
|
+
|
|
167
|
+
@pref_mem.setter
|
|
168
|
+
def pref_mem(self, value: BaseTextMemory) -> None:
|
|
169
|
+
"""Set the preference memory."""
|
|
170
|
+
if not isinstance(value, BaseTextMemory):
|
|
171
|
+
raise TypeError(f"Expected BaseTextMemory, got {type(value).__name__}")
|
|
172
|
+
self._pref_mem = value
|
memos/mem_cube/utils.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import logging
|
|
3
|
+
import subprocess
|
|
4
|
+
import tempfile
|
|
5
|
+
|
|
6
|
+
from memos.configs.mem_cube import GeneralMemCubeConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def download_repo(repo: str, base_url: str, dir: str | None = None) -> str:
|
|
13
|
+
"""Download a repository from a remote source.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
repo (str): The repository name.
|
|
17
|
+
base_url (str): The base URL of the remote repository.
|
|
18
|
+
dir (str, optional): The directory where the repository will be downloaded. If None, a temporary directory will be created.
|
|
19
|
+
If a directory is provided, it will be used instead of creating a temporary one.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: The local directory where the repository is downloaded.
|
|
23
|
+
"""
|
|
24
|
+
if dir is None:
|
|
25
|
+
dir = tempfile.mkdtemp()
|
|
26
|
+
repo_url = f"{base_url}/{repo}"
|
|
27
|
+
|
|
28
|
+
# Clone the repo
|
|
29
|
+
subprocess.run(["git", "clone", repo_url, dir], check=True)
|
|
30
|
+
|
|
31
|
+
return dir
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def merge_config_with_default(
|
|
35
|
+
existing_config: GeneralMemCubeConfig, default_config: GeneralMemCubeConfig
|
|
36
|
+
) -> GeneralMemCubeConfig:
|
|
37
|
+
"""
|
|
38
|
+
Merge existing cube config with default config, preserving critical fields.
|
|
39
|
+
|
|
40
|
+
This method updates general configuration fields (like API keys, model parameters)
|
|
41
|
+
while preserving critical user-specific fields (like user_id, cube_id, graph_db settings).
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
existing_config (GeneralMemCubeConfig): The existing cube configuration loaded from file
|
|
45
|
+
default_config (GeneralMemCubeConfig): The default configuration to merge from
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
GeneralMemCubeConfig: Merged configuration
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# Convert configs to dictionaries
|
|
52
|
+
existing_dict = existing_config.model_dump(mode="json")
|
|
53
|
+
default_dict = default_config.model_dump(mode="json")
|
|
54
|
+
|
|
55
|
+
logger.info(
|
|
56
|
+
f"Starting config merge for user {existing_config.user_id}, cube {existing_config.cube_id}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Define fields that should be preserved from existing config
|
|
60
|
+
preserve_fields = {"user_id", "cube_id", "config_filename", "model_schema"}
|
|
61
|
+
|
|
62
|
+
# Preserve graph_db from existing config if it exists, but merge some fields
|
|
63
|
+
preserved_graph_db = None
|
|
64
|
+
if "text_mem" in existing_dict and "text_mem" in default_dict:
|
|
65
|
+
existing_text_config = existing_dict["text_mem"].get("config", {})
|
|
66
|
+
default_text_config = default_dict["text_mem"].get("config", {})
|
|
67
|
+
|
|
68
|
+
if "graph_db" in existing_text_config and "graph_db" in default_text_config:
|
|
69
|
+
existing_graph_config = existing_text_config["graph_db"]["config"]
|
|
70
|
+
default_graph_config = default_text_config["graph_db"]["config"]
|
|
71
|
+
existing_backend = existing_text_config["graph_db"]["backend"]
|
|
72
|
+
default_backend = default_text_config["graph_db"]["backend"]
|
|
73
|
+
|
|
74
|
+
# Detect backend change
|
|
75
|
+
backend_changed = existing_backend != default_backend
|
|
76
|
+
|
|
77
|
+
if backend_changed:
|
|
78
|
+
logger.info(
|
|
79
|
+
f"Detected graph_db backend change: {existing_backend} -> {default_backend}. "
|
|
80
|
+
f"Migrating configuration..."
|
|
81
|
+
)
|
|
82
|
+
# Start with default config as base when backend changes
|
|
83
|
+
merged_graph_config = copy.deepcopy(default_graph_config)
|
|
84
|
+
|
|
85
|
+
# Preserve user-specific fields if they exist in both configs
|
|
86
|
+
preserve_graph_fields = {
|
|
87
|
+
"auto_create",
|
|
88
|
+
"user_name",
|
|
89
|
+
"use_multi_db",
|
|
90
|
+
}
|
|
91
|
+
for field in preserve_graph_fields:
|
|
92
|
+
if field in existing_graph_config:
|
|
93
|
+
merged_graph_config[field] = existing_graph_config[field]
|
|
94
|
+
logger.debug(
|
|
95
|
+
f"Preserved graph_db field '{field}': {existing_graph_config[field]}"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Clean up backend-specific fields that don't exist in the new backend
|
|
99
|
+
# This approach is generic: remove any field from merged config that's not in default config
|
|
100
|
+
# and not in the preserve list
|
|
101
|
+
fields_to_remove = []
|
|
102
|
+
for field in list(merged_graph_config.keys()):
|
|
103
|
+
if field not in default_graph_config and field not in preserve_graph_fields:
|
|
104
|
+
fields_to_remove.append(field)
|
|
105
|
+
|
|
106
|
+
for field in fields_to_remove:
|
|
107
|
+
removed_value = merged_graph_config.pop(field)
|
|
108
|
+
logger.info(
|
|
109
|
+
f"Removed {existing_backend}-specific field '{field}' (value: {removed_value}) "
|
|
110
|
+
f"during migration to {default_backend}"
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
# Same backend: merge configs while preserving user-specific fields
|
|
114
|
+
logger.debug(f"Same graph_db backend ({default_backend}), merging configurations")
|
|
115
|
+
preserve_graph_fields = {
|
|
116
|
+
"auto_create",
|
|
117
|
+
"user_name",
|
|
118
|
+
"use_multi_db",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Start with existing config as base
|
|
122
|
+
merged_graph_config = copy.deepcopy(existing_graph_config)
|
|
123
|
+
|
|
124
|
+
# Update with default config except preserved fields
|
|
125
|
+
for key, value in default_graph_config.items():
|
|
126
|
+
if key not in preserve_graph_fields:
|
|
127
|
+
merged_graph_config[key] = value
|
|
128
|
+
logger.debug(
|
|
129
|
+
f"Updated graph_db field '{key}': {existing_graph_config.get(key)} -> {value}"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Handle use_multi_db transition
|
|
133
|
+
if not default_graph_config.get("use_multi_db", True) and merged_graph_config.get(
|
|
134
|
+
"use_multi_db", True
|
|
135
|
+
):
|
|
136
|
+
merged_graph_config["use_multi_db"] = False
|
|
137
|
+
# For Neo4j: db_name becomes user_name in single-db mode
|
|
138
|
+
if "neo4j" in default_backend and "db_name" in merged_graph_config:
|
|
139
|
+
merged_graph_config["user_name"] = merged_graph_config.get("db_name")
|
|
140
|
+
merged_graph_config["db_name"] = default_graph_config.get("db_name")
|
|
141
|
+
logger.info("Transitioned to single-db mode (use_multi_db=False)")
|
|
142
|
+
|
|
143
|
+
preserved_graph_db = {
|
|
144
|
+
"backend": default_backend,
|
|
145
|
+
"config": merged_graph_config,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Use default config as base
|
|
149
|
+
merged_dict = copy.deepcopy(default_dict)
|
|
150
|
+
|
|
151
|
+
# Restore preserved fields from existing config
|
|
152
|
+
for field in preserve_fields:
|
|
153
|
+
if field in existing_dict:
|
|
154
|
+
merged_dict[field] = existing_dict[field]
|
|
155
|
+
logger.debug(f"Preserved field '{field}': {existing_dict[field]}")
|
|
156
|
+
|
|
157
|
+
# Restore graph_db if it was preserved
|
|
158
|
+
if preserved_graph_db and "text_mem" in merged_dict:
|
|
159
|
+
merged_dict["text_mem"]["config"]["graph_db"] = preserved_graph_db
|
|
160
|
+
logger.debug(f"Preserved graph_db with merged config: {preserved_graph_db}")
|
|
161
|
+
|
|
162
|
+
# Create new config from merged dictionary
|
|
163
|
+
merged_config = GeneralMemCubeConfig.model_validate(merged_dict)
|
|
164
|
+
|
|
165
|
+
logger.info(
|
|
166
|
+
f"Successfully merged cube config for user {merged_config.user_id}, cube {merged_config.cube_id}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return merged_config
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from memos.configs.memory import MemFeedbackConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseMemFeedback(ABC):
|
|
7
|
+
"""MemFeedback interface class for reading information."""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def __init__(self, config: MemFeedbackConfig):
|
|
11
|
+
"""Initialize the MemFeedback with the given configuration."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def process_feedback(self, data: dict) -> None:
|
|
15
|
+
"""Process user's feedback"""
|