MemoryOS 0.2.1__tar.gz → 1.0.0__tar.gz
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.
- {memoryos-0.2.1 → memoryos-1.0.0}/PKG-INFO +7 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/README.md +5 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/pyproject.toml +2 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/__init__.py +1 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/config.py +158 -69
- memoryos-1.0.0/src/memos/api/context/context.py +147 -0
- memoryos-1.0.0/src/memos/api/context/dependencies.py +101 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/product_models.py +5 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/routers/product_router.py +54 -26
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/graph_db.py +49 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/internet_retriever.py +19 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/mem_os.py +5 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/mem_reader.py +9 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/mem_scheduler.py +54 -18
- memoryos-1.0.0/src/memos/configs/mem_user.py +58 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/graph_dbs/base.py +38 -3
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/graph_dbs/factory.py +2 -0
- memoryos-1.0.0/src/memos/graph_dbs/nebular.py +1612 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/graph_dbs/neo4j.py +18 -9
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/log.py +6 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_cube/utils.py +13 -6
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_os/core.py +157 -37
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_os/main.py +2 -2
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_os/product.py +252 -201
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_os/utils/default_config.py +1 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_os/utils/format_utils.py +281 -70
- memoryos-1.0.0/src/memos/mem_os/utils/reference_utils.py +133 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_reader/simple_struct.py +13 -5
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_scheduler/base_scheduler.py +239 -266
- {memoryos-0.2.1/src/memos/mem_scheduler/modules → memoryos-1.0.0/src/memos/mem_scheduler/general_modules}/base.py +4 -5
- {memoryos-0.2.1/src/memos/mem_scheduler/modules → memoryos-1.0.0/src/memos/mem_scheduler/general_modules}/dispatcher.py +57 -21
- memoryos-1.0.0/src/memos/mem_scheduler/general_modules/misc.py +104 -0
- {memoryos-0.2.1/src/memos/mem_scheduler/modules → memoryos-1.0.0/src/memos/mem_scheduler/general_modules}/rabbitmq_service.py +12 -10
- {memoryos-0.2.1/src/memos/mem_scheduler/modules → memoryos-1.0.0/src/memos/mem_scheduler/general_modules}/redis_service.py +1 -1
- memoryos-1.0.0/src/memos/mem_scheduler/general_modules/retriever.py +199 -0
- memoryos-1.0.0/src/memos/mem_scheduler/general_modules/scheduler_logger.py +261 -0
- memoryos-1.0.0/src/memos/mem_scheduler/general_scheduler.py +349 -0
- memoryos-1.0.0/src/memos/mem_scheduler/monitors/dispatcher_monitor.py +305 -0
- memoryos-0.2.1/src/memos/mem_scheduler/modules/monitor.py → memoryos-1.0.0/src/memos/mem_scheduler/monitors/general_monitor.py +106 -57
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_scheduler/mos_for_test_scheduler.py +23 -20
- memoryos-1.0.0/src/memos/mem_scheduler/schemas/general_schemas.py +44 -0
- memoryos-1.0.0/src/memos/mem_scheduler/schemas/message_schemas.py +149 -0
- memoryos-1.0.0/src/memos/mem_scheduler/schemas/monitor_schemas.py +337 -0
- memoryos-1.0.0/src/memos/mem_scheduler/utils/filter_utils.py +176 -0
- memoryos-1.0.0/src/memos/mem_scheduler/utils/misc_utils.py +102 -0
- memoryos-1.0.0/src/memos/mem_user/factory.py +94 -0
- memoryos-1.0.0/src/memos/mem_user/mysql_persistent_user_manager.py +271 -0
- memoryos-1.0.0/src/memos/mem_user/mysql_user_manager.py +500 -0
- memoryos-1.0.0/src/memos/mem_user/persistent_factory.py +96 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_user/user_manager.py +4 -4
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/activation/item.py +5 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/activation/kv.py +20 -8
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/base.py +2 -2
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/general.py +36 -92
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/item.py +5 -33
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree.py +13 -7
- memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/organize/conflict.py → memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/organize/handler.py +34 -50
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/organize/manager.py +8 -96
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +49 -43
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py +107 -142
- memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +229 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +11 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/recall.py +15 -8
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/reranker.py +1 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
- memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/retrieve/searcher.py +284 -0
- memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +100 -0
- memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/retrieve/utils.py +52 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +62 -58
- memoryos-1.0.0/src/memos/memos_tools/dinding_report_bot.py +422 -0
- memoryos-1.0.0/src/memos/memos_tools/lockfree_dict.py +120 -0
- memoryos-1.0.0/src/memos/memos_tools/notification_service.py +44 -0
- memoryos-1.0.0/src/memos/memos_tools/notification_utils.py +96 -0
- memoryos-1.0.0/src/memos/memos_tools/thread_safe_dict.py +288 -0
- memoryos-1.0.0/src/memos/parsers/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/settings.py +3 -1
- memoryos-1.0.0/src/memos/templates/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/templates/mem_reader_prompts.py +4 -1
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/templates/mem_scheduler_prompts.py +62 -15
- memoryos-1.0.0/src/memos/templates/mos_prompts.py +179 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/templates/tree_reorganize_prompts.py +24 -17
- memoryos-1.0.0/src/memos/utils.py +19 -0
- memoryos-1.0.0/src/memos/vec_dbs/__init__.py +0 -0
- memoryos-0.2.1/src/memos/mem_scheduler/general_scheduler.py +0 -186
- memoryos-0.2.1/src/memos/mem_scheduler/modules/misc.py +0 -39
- memoryos-0.2.1/src/memos/mem_scheduler/modules/retriever.py +0 -268
- memoryos-0.2.1/src/memos/mem_scheduler/modules/schemas.py +0 -328
- memoryos-0.2.1/src/memos/mem_scheduler/utils.py +0 -75
- memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/organize/redundancy.py +0 -193
- memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/retrieve/searcher.py +0 -209
- memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +0 -68
- memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/retrieve/utils.py +0 -48
- memoryos-0.2.1/src/memos/templates/mos_prompts.py +0 -63
- {memoryos-0.2.1 → memoryos-1.0.0}/LICENSE +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/exceptions.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/mcp_serve.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/product_api.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/routers/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/api/start_api.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/chunkers/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/chunkers/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/chunkers/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/chunkers/sentence_chunker.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/cli.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/chunker.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/embedder.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/llm.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/mem_chat.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/mem_cube.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/memory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/parser.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/utils.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/configs/vec_db.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/dependency.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/deprecation.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/ark.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/ollama.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/sentence_transformer.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/embedders/universal_api.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/exceptions.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/graph_dbs/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/graph_dbs/item.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/graph_dbs/neo4j_community.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/hello_world.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/deepseek.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/hf.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/hf_singleton.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/ollama.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/openai.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/qwen.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/utils.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/llms/vllm.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_chat/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_chat/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_chat/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_chat/simple.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_cube/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_cube/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_cube/general.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_os/client.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_reader/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_reader/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_reader/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_reader/memory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_scheduler/__init__.py +0 -0
- {memoryos-0.2.1/src/memos/mem_scheduler/modules → memoryos-1.0.0/src/memos/mem_scheduler/general_modules}/__init__.py +0 -0
- {memoryos-0.2.1/src/memos/memories → memoryos-1.0.0/src/memos/mem_scheduler/monitors}/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_scheduler/scheduler_factory.py +0 -0
- {memoryos-0.2.1/src/memos/memories/activation → memoryos-1.0.0/src/memos/mem_scheduler/schemas}/__init__.py +0 -0
- {memoryos-0.2.1/src/memos/memories/parametric → memoryos-1.0.0/src/memos/mem_scheduler/utils}/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/mem_user/persistent_user_manager.py +0 -0
- {memoryos-0.2.1/src/memos/memories/textual → memoryos-1.0.0/src/memos/memories}/__init__.py +0 -0
- {memoryos-0.2.1/src/memos/memories/textual/tree_text_memory → memoryos-1.0.0/src/memos/memories/activation}/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/activation/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/activation/vllmkv.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/factory.py +0 -0
- {memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/organize → memoryos-1.0.0/src/memos/memories/parametric}/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/parametric/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/parametric/item.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/parametric/lora.py +0 -0
- {memoryos-0.2.1/src/memos/memories/textual/tree_text_memory/retrieve → memoryos-1.0.0/src/memos/memories/textual}/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/naive.py +0 -0
- {memoryos-0.2.1/src/memos/parsers → memoryos-1.0.0/src/memos/memories/textual/tree_text_memory}/__init__.py +0 -0
- {memoryos-0.2.1/src/memos/templates → memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/organize}/__init__.py +0 -0
- {memoryos-0.2.1/src/memos/vec_dbs → memoryos-1.0.0/src/memos/memories/textual/tree_text_memory/retrieve}/__init__.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/memories/textual/tree_text_memory/retrieve/reasoner.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/parsers/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/parsers/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/parsers/markitdown.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/types.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/vec_dbs/base.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/vec_dbs/factory.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/vec_dbs/item.py +0 -0
- {memoryos-0.2.1 → memoryos-1.0.0}/src/memos/vec_dbs/qdrant.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: MemoryOS
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Intelligence Begins with Memory
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: memory,llm,language model,memoryOS,agent,kv cache,lora
|
|
@@ -35,6 +35,7 @@ Requires-Dist: ollama (>=0.4.8,<0.5.0)
|
|
|
35
35
|
Requires-Dist: openai (>=1.77.0,<2.0.0)
|
|
36
36
|
Requires-Dist: pika (>=1.3.2,<2.0.0) ; extra == "all"
|
|
37
37
|
Requires-Dist: pika (>=1.3.2,<2.0.0) ; extra == "mem-scheduler"
|
|
38
|
+
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
38
39
|
Requires-Dist: qdrant-client (>=1.14.2,<2.0.0) ; extra == "all"
|
|
39
40
|
Requires-Dist: redis (>=6.2.0,<7.0.0) ; extra == "all"
|
|
40
41
|
Requires-Dist: redis (>=6.2.0,<7.0.0) ; extra == "mem-scheduler"
|
|
@@ -309,6 +310,11 @@ MemOS is licensed under the [Apache 2.0 License](./LICENSE).
|
|
|
309
310
|
|
|
310
311
|
Stay up to date with the latest MemOS announcements, releases, and community highlights.
|
|
311
312
|
|
|
313
|
+
|
|
314
|
+
- **2025-08-07** - 🎉 *MemOS v1.0.0 (MemCube Release)*: First MemCube with word game demo, LongMemEval evaluation, BochaAISearchRetriever integration, NebulaGraph support, enhanced search capabilities, and official Playground launch.
|
|
315
|
+
- **2025-07-29** – 🎉 *MemOS v0.2.2 (Nebula Update)*: Internet search+Nebula DB integration, refactored memory scheduler, KV Cache stress tests, MemCube Cookbook release (CN/EN), and 4b/1.7b/0.6b memory ops models.
|
|
316
|
+
- **2025-07-21** – 🎉 *MemOS v0.2.1 (Neo Release)*: Lightweight Neo version with plaintext+KV Cache functionality, Docker/multi-tenant support, MCP expansion, and new Cookbook/Mud game examples.
|
|
317
|
+
- **2025-07-11** – 🎉 *MemOS v0.2.0 (Cross-Platform)*: Added doc search/bilingual UI, MemReader-4B (local deploy), full Win/Mac/Linux support, and playground end-to-end connection.
|
|
312
318
|
- **2025-07-07** – 🎉 *MemOS 1.0 (Stellar) Preview Release*: A SOTA Memory OS for LLMs is now open-sourced.
|
|
313
319
|
- **2025-07-04** – 🎉 *MemOS Paper Released*: [MemOS: A Memory OS for AI System](https://arxiv.org/abs/2507.03724) was published on arXiv.
|
|
314
320
|
- **2025-05-28** – 🎉 *Short Paper Uploaded*: [MemOS: An Operating System for Memory-Augmented Generation (MAG) in Large Language Models](https://arxiv.org/abs/2505.22101) was published on arXiv.
|
|
@@ -251,6 +251,11 @@ MemOS is licensed under the [Apache 2.0 License](./LICENSE).
|
|
|
251
251
|
|
|
252
252
|
Stay up to date with the latest MemOS announcements, releases, and community highlights.
|
|
253
253
|
|
|
254
|
+
|
|
255
|
+
- **2025-08-07** - 🎉 *MemOS v1.0.0 (MemCube Release)*: First MemCube with word game demo, LongMemEval evaluation, BochaAISearchRetriever integration, NebulaGraph support, enhanced search capabilities, and official Playground launch.
|
|
256
|
+
- **2025-07-29** – 🎉 *MemOS v0.2.2 (Nebula Update)*: Internet search+Nebula DB integration, refactored memory scheduler, KV Cache stress tests, MemCube Cookbook release (CN/EN), and 4b/1.7b/0.6b memory ops models.
|
|
257
|
+
- **2025-07-21** – 🎉 *MemOS v0.2.1 (Neo Release)*: Lightweight Neo version with plaintext+KV Cache functionality, Docker/multi-tenant support, MCP expansion, and new Cookbook/Mud game examples.
|
|
258
|
+
- **2025-07-11** – 🎉 *MemOS v0.2.0 (Cross-Platform)*: Added doc search/bilingual UI, MemReader-4B (local deploy), full Win/Mac/Linux support, and playground end-to-end connection.
|
|
254
259
|
- **2025-07-07** – 🎉 *MemOS 1.0 (Stellar) Preview Release*: A SOTA Memory OS for LLMs is now open-sourced.
|
|
255
260
|
- **2025-07-04** – 🎉 *MemOS Paper Released*: [MemOS: A Memory OS for AI System](https://arxiv.org/abs/2507.03724) was published on arXiv.
|
|
256
261
|
- **2025-05-28** – 🎉 *Short Paper Uploaded*: [MemOS: An Operating System for Memory-Augmented Generation (MAG) in Large Language Models](https://arxiv.org/abs/2505.22101) was published on arXiv.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
##############################################################################
|
|
5
5
|
|
|
6
6
|
name = "MemoryOS"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "1.0.0"
|
|
8
8
|
description = "Intelligence Begins with Memory"
|
|
9
9
|
license = {text = "Apache-2.0"}
|
|
10
10
|
readme = "README.md"
|
|
@@ -44,6 +44,7 @@ dependencies = [
|
|
|
44
44
|
"sqlalchemy (>=2.0.41,<3.0.0)", # SQL toolkit
|
|
45
45
|
"scikit-learn (>=1.7.0,<2.0.0)", # Machine learning
|
|
46
46
|
"fastmcp (>=2.10.5,<3.0.0)",
|
|
47
|
+
"python-dateutil (>=2.9.0.post0,<3.0.0)",
|
|
47
48
|
]
|
|
48
49
|
|
|
49
50
|
[project.urls]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import os
|
|
2
3
|
|
|
3
4
|
from typing import Any
|
|
@@ -99,9 +100,9 @@ class APIConfig:
|
|
|
99
100
|
"backend": "universal_api",
|
|
100
101
|
"config": {
|
|
101
102
|
"provider": os.getenv("MOS_EMBEDDER_PROVIDER", "openai"),
|
|
102
|
-
"api_key": os.getenv("
|
|
103
|
+
"api_key": os.getenv("MOS_EMBEDDER_API_KEY", "sk-xxxx"),
|
|
103
104
|
"model_name_or_path": os.getenv("MOS_EMBEDDER_MODEL", "text-embedding-3-large"),
|
|
104
|
-
"base_url": os.getenv("
|
|
105
|
+
"base_url": os.getenv("MOS_EMBEDDER_API_BASE", "http://openai.com"),
|
|
105
106
|
},
|
|
106
107
|
}
|
|
107
108
|
else: # ollama
|
|
@@ -115,6 +116,47 @@ class APIConfig:
|
|
|
115
116
|
},
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
@staticmethod
|
|
120
|
+
def get_internet_config() -> dict[str, Any]:
|
|
121
|
+
"""Get embedder configuration."""
|
|
122
|
+
return {
|
|
123
|
+
"backend": "bocha",
|
|
124
|
+
"config": {
|
|
125
|
+
"api_key": os.getenv("BOCHA_API_KEY"),
|
|
126
|
+
"max_results": 15,
|
|
127
|
+
"num_per_request": 10,
|
|
128
|
+
"reader": {
|
|
129
|
+
"backend": "simple_struct",
|
|
130
|
+
"config": {
|
|
131
|
+
"llm": {
|
|
132
|
+
"backend": "openai",
|
|
133
|
+
"config": {
|
|
134
|
+
"model_name_or_path": os.getenv("MEMRADER_MODEL"),
|
|
135
|
+
"temperature": 0.6,
|
|
136
|
+
"max_tokens": 5000,
|
|
137
|
+
"top_p": 0.95,
|
|
138
|
+
"top_k": 20,
|
|
139
|
+
"api_key": os.getenv("MEMRADER_API_KEY", "EMPTY"),
|
|
140
|
+
"api_base": os.getenv("MEMRADER_API_BASE"),
|
|
141
|
+
"remove_think_prefix": True,
|
|
142
|
+
"extra_body": {"chat_template_kwargs": {"enable_thinking": False}},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
"embedder": APIConfig.get_embedder_config(),
|
|
146
|
+
"chunker": {
|
|
147
|
+
"backend": "sentence",
|
|
148
|
+
"config": {
|
|
149
|
+
"tokenizer_or_token_counter": "gpt2",
|
|
150
|
+
"chunk_size": 512,
|
|
151
|
+
"chunk_overlap": 128,
|
|
152
|
+
"min_sentences_per_chunk": 1,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
|
|
118
160
|
@staticmethod
|
|
119
161
|
def get_neo4j_community_config(user_id: str | None = None) -> dict[str, Any]:
|
|
120
162
|
"""Get Neo4j community configuration."""
|
|
@@ -126,14 +168,14 @@ class APIConfig:
|
|
|
126
168
|
"user_name": f"memos{user_id.replace('-', '')}",
|
|
127
169
|
"auto_create": True,
|
|
128
170
|
"use_multi_db": False,
|
|
129
|
-
"embedding_dimension": 3072,
|
|
171
|
+
"embedding_dimension": int(os.getenv("EMBEDDING_DIMENSION", 3072)),
|
|
130
172
|
"vec_config": {
|
|
131
173
|
# Pass nested config to initialize external vector DB
|
|
132
174
|
# If you use qdrant, please use Server instead of local mode.
|
|
133
175
|
"backend": "qdrant",
|
|
134
176
|
"config": {
|
|
135
177
|
"collection_name": "neo4j_vec_db",
|
|
136
|
-
"vector_dimension": 3072,
|
|
178
|
+
"vector_dimension": int(os.getenv("EMBEDDING_DIMENSION", 3072)),
|
|
137
179
|
"distance_metric": "cosine",
|
|
138
180
|
"host": "localhost",
|
|
139
181
|
"port": 6333,
|
|
@@ -159,7 +201,7 @@ class APIConfig:
|
|
|
159
201
|
"password": os.getenv("NEO4J_PASSWORD", "12345678"),
|
|
160
202
|
"auto_create": True,
|
|
161
203
|
"use_multi_db": True,
|
|
162
|
-
"embedding_dimension": 3072,
|
|
204
|
+
"embedding_dimension": int(os.getenv("EMBEDDING_DIMENSION", 3072)),
|
|
163
205
|
}
|
|
164
206
|
|
|
165
207
|
@staticmethod
|
|
@@ -173,7 +215,33 @@ class APIConfig:
|
|
|
173
215
|
"user_name": f"memos{user_id.replace('-', '')}",
|
|
174
216
|
"auto_create": True,
|
|
175
217
|
"use_multi_db": False,
|
|
176
|
-
"embedding_dimension": 3072,
|
|
218
|
+
"embedding_dimension": int(os.getenv("EMBEDDING_DIMENSION", 3072)),
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@staticmethod
|
|
222
|
+
def get_nebular_config(user_id: str | None = None) -> dict[str, Any]:
|
|
223
|
+
"""Get Nebular configuration."""
|
|
224
|
+
return {
|
|
225
|
+
"uri": json.loads(os.getenv("NEBULAR_HOSTS", '["localhost"]')),
|
|
226
|
+
"user": os.getenv("NEBULAR_USER", "root"),
|
|
227
|
+
"password": os.getenv("NEBULAR_PASSWORD", "xxxxxx"),
|
|
228
|
+
"space": os.getenv("NEBULAR_SPACE", "shared-tree-textual-memory"),
|
|
229
|
+
"user_name": f"memos{user_id.replace('-', '')}",
|
|
230
|
+
"use_multi_db": False,
|
|
231
|
+
"auto_create": True,
|
|
232
|
+
"embedding_dimension": int(os.getenv("EMBEDDING_DIMENSION", 3072)),
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def get_mysql_config() -> dict[str, Any]:
|
|
237
|
+
"""Get MySQL configuration."""
|
|
238
|
+
return {
|
|
239
|
+
"host": os.getenv("MYSQL_HOST", "localhost"),
|
|
240
|
+
"port": int(os.getenv("MYSQL_PORT", "3306")),
|
|
241
|
+
"username": os.getenv("MYSQL_USERNAME", "root"),
|
|
242
|
+
"password": os.getenv("MYSQL_PASSWORD", "12345678"),
|
|
243
|
+
"database": os.getenv("MYSQL_DATABASE", "memos_users"),
|
|
244
|
+
"charset": os.getenv("MYSQL_CHARSET", "utf8mb4"),
|
|
177
245
|
}
|
|
178
246
|
|
|
179
247
|
@staticmethod
|
|
@@ -183,7 +251,6 @@ class APIConfig:
|
|
|
183
251
|
"backend": "general_scheduler",
|
|
184
252
|
"config": {
|
|
185
253
|
"top_k": int(os.getenv("MOS_SCHEDULER_TOP_K", "10")),
|
|
186
|
-
"top_n": int(os.getenv("MOS_SCHEDULER_TOP_N", "5")),
|
|
187
254
|
"act_mem_update_interval": int(
|
|
188
255
|
os.getenv("MOS_SCHEDULER_ACT_MEM_UPDATE_INTERVAL", "300")
|
|
189
256
|
),
|
|
@@ -198,7 +265,7 @@ class APIConfig:
|
|
|
198
265
|
"MOS_SCHEDULER_ENABLE_PARALLEL_DISPATCH", "true"
|
|
199
266
|
).lower()
|
|
200
267
|
== "true",
|
|
201
|
-
"
|
|
268
|
+
"enable_activation_memory": True,
|
|
202
269
|
},
|
|
203
270
|
}
|
|
204
271
|
|
|
@@ -212,6 +279,34 @@ class APIConfig:
|
|
|
212
279
|
"""Check if default cube config is enabled via environment variable."""
|
|
213
280
|
return os.getenv("MOS_ENABLE_DEFAULT_CUBE_CONFIG", "false").lower() == "true"
|
|
214
281
|
|
|
282
|
+
@staticmethod
|
|
283
|
+
def is_dingding_bot_enabled() -> bool:
|
|
284
|
+
"""Check if DingDing bot is enabled via environment variable."""
|
|
285
|
+
return os.getenv("ENABLE_DINGDING_BOT", "false").lower() == "true"
|
|
286
|
+
|
|
287
|
+
@staticmethod
|
|
288
|
+
def get_dingding_bot_config() -> dict[str, Any] | None:
|
|
289
|
+
"""Get DingDing bot configuration if enabled."""
|
|
290
|
+
if not APIConfig.is_dingding_bot_enabled():
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
"enabled": True,
|
|
295
|
+
"access_token_user": os.getenv("DINGDING_ACCESS_TOKEN_USER", ""),
|
|
296
|
+
"secret_user": os.getenv("DINGDING_SECRET_USER", ""),
|
|
297
|
+
"access_token_error": os.getenv("DINGDING_ACCESS_TOKEN_ERROR", ""),
|
|
298
|
+
"secret_error": os.getenv("DINGDING_SECRET_ERROR", ""),
|
|
299
|
+
"robot_code": os.getenv("DINGDING_ROBOT_CODE", ""),
|
|
300
|
+
"app_key": os.getenv("DINGDING_APP_KEY", ""),
|
|
301
|
+
"app_secret": os.getenv("DINGDING_APP_SECRET", ""),
|
|
302
|
+
"oss_endpoint": os.getenv("OSS_ENDPOINT", ""),
|
|
303
|
+
"oss_region": os.getenv("OSS_REGION", ""),
|
|
304
|
+
"oss_bucket_name": os.getenv("OSS_BUCKET_NAME", ""),
|
|
305
|
+
"oss_access_key_id": os.getenv("OSS_ACCESS_KEY_ID", ""),
|
|
306
|
+
"oss_access_key_secret": os.getenv("OSS_ACCESS_KEY_SECRET", ""),
|
|
307
|
+
"oss_public_base_url": os.getenv("OSS_PUBLIC_BASE_URL", ""),
|
|
308
|
+
}
|
|
309
|
+
|
|
215
310
|
@staticmethod
|
|
216
311
|
def get_product_default_config() -> dict[str, Any]:
|
|
217
312
|
"""Get default configuration for Product API."""
|
|
@@ -224,6 +319,7 @@ class APIConfig:
|
|
|
224
319
|
"vllm": vllm_config,
|
|
225
320
|
}
|
|
226
321
|
backend = os.getenv("MOS_CHAT_MODEL_PROVIDER", "openai")
|
|
322
|
+
mysql_config = APIConfig.get_mysql_config()
|
|
227
323
|
config = {
|
|
228
324
|
"user_id": os.getenv("MOS_USER_ID", "root"),
|
|
229
325
|
"chat_model": {"backend": backend, "config": backend_model[backend]},
|
|
@@ -260,6 +356,13 @@ class APIConfig:
|
|
|
260
356
|
else:
|
|
261
357
|
config["enable_mem_scheduler"] = False
|
|
262
358
|
|
|
359
|
+
# Add user manager configuration if enabled
|
|
360
|
+
if os.getenv("MOS_USER_MANAGER_BACKEND", "sqlite").lower() == "mysql":
|
|
361
|
+
config["user_manager"] = {
|
|
362
|
+
"backend": "mysql",
|
|
363
|
+
"config": mysql_config,
|
|
364
|
+
}
|
|
365
|
+
|
|
263
366
|
return config
|
|
264
367
|
|
|
265
368
|
@staticmethod
|
|
@@ -300,9 +403,9 @@ class APIConfig:
|
|
|
300
403
|
def create_user_config(user_name: str, user_id: str) -> tuple[MOSConfig, GeneralMemCube]:
|
|
301
404
|
"""Create configuration for a specific user."""
|
|
302
405
|
openai_config = APIConfig.get_openai_config()
|
|
303
|
-
|
|
304
406
|
qwen_config = APIConfig.qwen_config()
|
|
305
407
|
vllm_config = APIConfig.vllm_config()
|
|
408
|
+
mysql_config = APIConfig.get_mysql_config()
|
|
306
409
|
backend = os.getenv("MOS_CHAT_MODEL_PROVIDER", "openai")
|
|
307
410
|
backend_model = {
|
|
308
411
|
"openai": openai_config,
|
|
@@ -341,7 +444,6 @@ class APIConfig:
|
|
|
341
444
|
"top_k": 30,
|
|
342
445
|
"max_turns_window": 20,
|
|
343
446
|
}
|
|
344
|
-
|
|
345
447
|
# Add scheduler configuration if enabled
|
|
346
448
|
if APIConfig.is_scheduler_enabled():
|
|
347
449
|
config_dict["mem_scheduler"] = APIConfig.get_scheduler_config()
|
|
@@ -349,11 +451,32 @@ class APIConfig:
|
|
|
349
451
|
else:
|
|
350
452
|
config_dict["enable_mem_scheduler"] = False
|
|
351
453
|
|
|
454
|
+
# Add user manager configuration if enabled
|
|
455
|
+
if os.getenv("MOS_USER_MANAGER_BACKEND", "sqlite").lower() == "mysql":
|
|
456
|
+
config_dict["user_manager"] = {
|
|
457
|
+
"backend": "mysql",
|
|
458
|
+
"config": mysql_config,
|
|
459
|
+
}
|
|
460
|
+
|
|
352
461
|
default_config = MOSConfig(**config_dict)
|
|
353
462
|
|
|
354
|
-
|
|
355
|
-
|
|
463
|
+
neo4j_community_config = APIConfig.get_neo4j_community_config(user_id)
|
|
464
|
+
neo4j_config = APIConfig.get_neo4j_config(user_id)
|
|
465
|
+
nebular_config = APIConfig.get_nebular_config(user_id)
|
|
466
|
+
internet_config = (
|
|
467
|
+
APIConfig.get_internet_config()
|
|
468
|
+
if os.getenv("ENABLE_INTERNET", "false").lower() == "true"
|
|
469
|
+
else None
|
|
470
|
+
)
|
|
471
|
+
graph_db_backend_map = {
|
|
472
|
+
"neo4j-community": neo4j_community_config,
|
|
473
|
+
"neo4j": neo4j_config,
|
|
474
|
+
"nebular": nebular_config,
|
|
475
|
+
}
|
|
476
|
+
graph_db_backend = os.getenv("NEO4J_BACKEND", "neo4j-community").lower()
|
|
477
|
+
if graph_db_backend in graph_db_backend_map:
|
|
356
478
|
# Create MemCube config
|
|
479
|
+
|
|
357
480
|
default_cube_config = GeneralMemCubeConfig.model_validate(
|
|
358
481
|
{
|
|
359
482
|
"user_id": user_id,
|
|
@@ -364,10 +487,11 @@ class APIConfig:
|
|
|
364
487
|
"extractor_llm": {"backend": "openai", "config": openai_config},
|
|
365
488
|
"dispatcher_llm": {"backend": "openai", "config": openai_config},
|
|
366
489
|
"graph_db": {
|
|
367
|
-
"backend":
|
|
368
|
-
"config":
|
|
490
|
+
"backend": graph_db_backend,
|
|
491
|
+
"config": graph_db_backend_map[graph_db_backend],
|
|
369
492
|
},
|
|
370
493
|
"embedder": APIConfig.get_embedder_config(),
|
|
494
|
+
"internet_retriever": internet_config,
|
|
371
495
|
},
|
|
372
496
|
},
|
|
373
497
|
"act_mem": {}
|
|
@@ -377,31 +501,7 @@ class APIConfig:
|
|
|
377
501
|
}
|
|
378
502
|
)
|
|
379
503
|
else:
|
|
380
|
-
|
|
381
|
-
# Create MemCube config
|
|
382
|
-
default_cube_config = GeneralMemCubeConfig.model_validate(
|
|
383
|
-
{
|
|
384
|
-
"user_id": user_id,
|
|
385
|
-
"cube_id": f"{user_name}_default_cube",
|
|
386
|
-
"text_mem": {
|
|
387
|
-
"backend": "tree_text",
|
|
388
|
-
"config": {
|
|
389
|
-
"extractor_llm": {"backend": "openai", "config": openai_config},
|
|
390
|
-
"dispatcher_llm": {"backend": "openai", "config": openai_config},
|
|
391
|
-
"graph_db": {
|
|
392
|
-
"backend": "neo4j",
|
|
393
|
-
"config": neo4j_config,
|
|
394
|
-
},
|
|
395
|
-
"embedder": APIConfig.get_embedder_config(),
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
"act_mem": {}
|
|
399
|
-
if os.getenv("ENABLE_ACTIVATION_MEMORY", "false").lower() == "false"
|
|
400
|
-
else APIConfig.get_activation_vllm_config(),
|
|
401
|
-
"para_mem": {},
|
|
402
|
-
}
|
|
403
|
-
)
|
|
404
|
-
|
|
504
|
+
raise ValueError(f"Invalid Neo4j backend: {graph_db_backend}")
|
|
405
505
|
default_mem_cube = GeneralMemCube(default_cube_config)
|
|
406
506
|
return default_config, default_mem_cube
|
|
407
507
|
|
|
@@ -416,9 +516,21 @@ class APIConfig:
|
|
|
416
516
|
return None
|
|
417
517
|
|
|
418
518
|
openai_config = APIConfig.get_openai_config()
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
519
|
+
neo4j_community_config = APIConfig.get_neo4j_community_config(user_id="default")
|
|
520
|
+
neo4j_config = APIConfig.get_neo4j_config(user_id="default")
|
|
521
|
+
nebular_config = APIConfig.get_nebular_config(user_id="default")
|
|
522
|
+
graph_db_backend_map = {
|
|
523
|
+
"neo4j-community": neo4j_community_config,
|
|
524
|
+
"neo4j": neo4j_config,
|
|
525
|
+
"nebular": nebular_config,
|
|
526
|
+
}
|
|
527
|
+
internet_config = (
|
|
528
|
+
APIConfig.get_internet_config()
|
|
529
|
+
if os.getenv("ENABLE_INTERNET", "false").lower() == "true"
|
|
530
|
+
else None
|
|
531
|
+
)
|
|
532
|
+
graph_db_backend = os.getenv("NEO4J_BACKEND", "neo4j-community").lower()
|
|
533
|
+
if graph_db_backend in graph_db_backend_map:
|
|
422
534
|
return GeneralMemCubeConfig.model_validate(
|
|
423
535
|
{
|
|
424
536
|
"user_id": "default",
|
|
@@ -429,12 +541,13 @@ class APIConfig:
|
|
|
429
541
|
"extractor_llm": {"backend": "openai", "config": openai_config},
|
|
430
542
|
"dispatcher_llm": {"backend": "openai", "config": openai_config},
|
|
431
543
|
"graph_db": {
|
|
432
|
-
"backend":
|
|
433
|
-
"config":
|
|
544
|
+
"backend": graph_db_backend,
|
|
545
|
+
"config": graph_db_backend_map[graph_db_backend],
|
|
434
546
|
},
|
|
435
547
|
"embedder": APIConfig.get_embedder_config(),
|
|
436
548
|
"reorganize": os.getenv("MOS_ENABLE_REORGANIZE", "false").lower()
|
|
437
549
|
== "true",
|
|
550
|
+
"internet_retriever": internet_config,
|
|
438
551
|
},
|
|
439
552
|
},
|
|
440
553
|
"act_mem": {}
|
|
@@ -444,28 +557,4 @@ class APIConfig:
|
|
|
444
557
|
}
|
|
445
558
|
)
|
|
446
559
|
else:
|
|
447
|
-
|
|
448
|
-
return GeneralMemCubeConfig.model_validate(
|
|
449
|
-
{
|
|
450
|
-
"user_id": "default",
|
|
451
|
-
"cube_id": "default_cube",
|
|
452
|
-
"text_mem": {
|
|
453
|
-
"backend": "tree_text",
|
|
454
|
-
"config": {
|
|
455
|
-
"extractor_llm": {"backend": "openai", "config": openai_config},
|
|
456
|
-
"dispatcher_llm": {"backend": "openai", "config": openai_config},
|
|
457
|
-
"graph_db": {
|
|
458
|
-
"backend": "neo4j",
|
|
459
|
-
"config": neo4j_config,
|
|
460
|
-
},
|
|
461
|
-
"embedder": APIConfig.get_embedder_config(),
|
|
462
|
-
"reorganize": os.getenv("MOS_ENABLE_REORGANIZE", "false").lower()
|
|
463
|
-
== "true",
|
|
464
|
-
},
|
|
465
|
-
},
|
|
466
|
-
"act_mem": {}
|
|
467
|
-
if os.getenv("ENABLE_ACTIVATION_MEMORY", "false").lower() == "false"
|
|
468
|
-
else APIConfig.get_activation_vllm_config(),
|
|
469
|
-
"para_mem": {},
|
|
470
|
-
}
|
|
471
|
-
)
|
|
560
|
+
raise ValueError(f"Invalid Neo4j backend: {graph_db_backend}")
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Global request context management for trace_id and request-scoped data.
|
|
3
|
+
|
|
4
|
+
This module provides optional trace_id functionality that can be enabled
|
|
5
|
+
when using the API components. It uses ContextVar to ensure thread safety
|
|
6
|
+
and request isolation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import uuid
|
|
10
|
+
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from contextvars import ContextVar
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Global context variable for request-scoped data
|
|
17
|
+
_request_context: ContextVar[dict[str, Any] | None] = ContextVar("request_context", default=None)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RequestContext:
|
|
21
|
+
"""
|
|
22
|
+
Request-scoped context object that holds trace_id and other request data.
|
|
23
|
+
|
|
24
|
+
This provides a Flask g-like object for FastAPI applications.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, trace_id: str | None = None):
|
|
28
|
+
self.trace_id = trace_id or str(uuid.uuid4())
|
|
29
|
+
self._data: dict[str, Any] = {}
|
|
30
|
+
|
|
31
|
+
def set(self, key: str, value: Any) -> None:
|
|
32
|
+
"""Set a value in the context."""
|
|
33
|
+
self._data[key] = value
|
|
34
|
+
|
|
35
|
+
def get(self, key: str, default: Any | None = None) -> Any:
|
|
36
|
+
"""Get a value from the context."""
|
|
37
|
+
return self._data.get(key, default)
|
|
38
|
+
|
|
39
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
40
|
+
if name.startswith("_") or name == "trace_id":
|
|
41
|
+
super().__setattr__(name, value)
|
|
42
|
+
else:
|
|
43
|
+
if not hasattr(self, "_data"):
|
|
44
|
+
super().__setattr__(name, value)
|
|
45
|
+
else:
|
|
46
|
+
self._data[name] = value
|
|
47
|
+
|
|
48
|
+
def __getattr__(self, name: str) -> Any:
|
|
49
|
+
if hasattr(self, "_data") and name in self._data:
|
|
50
|
+
return self._data[name]
|
|
51
|
+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
|
|
52
|
+
|
|
53
|
+
def to_dict(self) -> dict[str, Any]:
|
|
54
|
+
"""Convert context to dictionary."""
|
|
55
|
+
return {"trace_id": self.trace_id, "data": self._data.copy()}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def set_request_context(context: RequestContext) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Set the current request context.
|
|
61
|
+
|
|
62
|
+
This is typically called by the API dependency injection system.
|
|
63
|
+
"""
|
|
64
|
+
_request_context.set(context.to_dict())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_current_trace_id() -> str | None:
|
|
68
|
+
"""
|
|
69
|
+
Get the current request's trace_id.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The trace_id if available, None otherwise.
|
|
73
|
+
"""
|
|
74
|
+
context = _request_context.get()
|
|
75
|
+
if context:
|
|
76
|
+
return context.get("trace_id")
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_current_context() -> RequestContext | None:
|
|
81
|
+
"""
|
|
82
|
+
Get the current request context.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
The current RequestContext if available, None otherwise.
|
|
86
|
+
"""
|
|
87
|
+
context_dict = _request_context.get()
|
|
88
|
+
if context_dict:
|
|
89
|
+
ctx = RequestContext(trace_id=context_dict.get("trace_id"))
|
|
90
|
+
ctx._data = context_dict.get("data", {}).copy()
|
|
91
|
+
return ctx
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def require_context() -> RequestContext:
|
|
96
|
+
"""
|
|
97
|
+
Get the current request context, raising an error if not available.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The current RequestContext.
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
RuntimeError: If called outside of a request context.
|
|
104
|
+
"""
|
|
105
|
+
context = get_current_context()
|
|
106
|
+
if context is None:
|
|
107
|
+
raise RuntimeError(
|
|
108
|
+
"No request context available. This function must be called within a request handler."
|
|
109
|
+
)
|
|
110
|
+
return context
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# Type for trace_id getter function
|
|
114
|
+
TraceIdGetter = Callable[[], str | None]
|
|
115
|
+
|
|
116
|
+
# Global variable to hold the trace_id getter function
|
|
117
|
+
_trace_id_getter: TraceIdGetter | None = None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def set_trace_id_getter(getter: TraceIdGetter) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Set a custom trace_id getter function.
|
|
123
|
+
|
|
124
|
+
This allows the logging system to retrieve trace_id without importing
|
|
125
|
+
API-specific general_modules.
|
|
126
|
+
"""
|
|
127
|
+
global _trace_id_getter
|
|
128
|
+
_trace_id_getter = getter
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_trace_id_for_logging() -> str | None:
|
|
132
|
+
"""
|
|
133
|
+
Get trace_id for logging purposes.
|
|
134
|
+
|
|
135
|
+
This function is used by the logging system and will use either
|
|
136
|
+
the custom getter function or fall back to the default context.
|
|
137
|
+
"""
|
|
138
|
+
if _trace_id_getter:
|
|
139
|
+
try:
|
|
140
|
+
return _trace_id_getter()
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
return get_current_trace_id()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Initialize the default trace_id getter
|
|
147
|
+
set_trace_id_getter(get_current_trace_id)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from fastapi import Depends, Header, Request
|
|
5
|
+
|
|
6
|
+
from memos.api.context.context import RequestContext, set_request_context
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
# Type alias for the RequestContext from context module
|
|
12
|
+
G = RequestContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_trace_id_from_header(
|
|
16
|
+
trace_id: str | None = Header(None, alias="trace-id"),
|
|
17
|
+
x_trace_id: str | None = Header(None, alias="x-trace-id"),
|
|
18
|
+
g_trace_id: str | None = Header(None, alias="g-trace-id"),
|
|
19
|
+
) -> str | None:
|
|
20
|
+
"""
|
|
21
|
+
Extract trace_id from various possible headers.
|
|
22
|
+
|
|
23
|
+
Priority: g-trace-id > x-trace-id > trace-id
|
|
24
|
+
"""
|
|
25
|
+
return g_trace_id or x_trace_id or trace_id
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def generate_trace_id() -> str:
|
|
29
|
+
"""
|
|
30
|
+
Get a random trace_id.
|
|
31
|
+
"""
|
|
32
|
+
return os.urandom(16).hex()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_request_context(
|
|
36
|
+
request: Request, trace_id: str | None = Depends(get_trace_id_from_header)
|
|
37
|
+
) -> RequestContext:
|
|
38
|
+
"""
|
|
39
|
+
Get request context object with trace_id and request metadata.
|
|
40
|
+
|
|
41
|
+
This function creates a RequestContext and automatically sets it
|
|
42
|
+
in the global context for use throughout the request lifecycle.
|
|
43
|
+
"""
|
|
44
|
+
# Create context object
|
|
45
|
+
ctx = RequestContext(trace_id=trace_id)
|
|
46
|
+
|
|
47
|
+
# Set the context globally for this request
|
|
48
|
+
set_request_context(ctx)
|
|
49
|
+
|
|
50
|
+
# Log request start
|
|
51
|
+
logger.info(f"Request started with trace_id: {ctx.trace_id}")
|
|
52
|
+
|
|
53
|
+
# Add request metadata to context
|
|
54
|
+
ctx.set("method", request.method)
|
|
55
|
+
ctx.set("path", request.url.path)
|
|
56
|
+
ctx.set("client_ip", request.client.host if request.client else None)
|
|
57
|
+
|
|
58
|
+
return ctx
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_g_object(trace_id: str | None = Depends(get_trace_id_from_header)) -> G:
|
|
62
|
+
"""
|
|
63
|
+
Get Flask g-like object for the current request.
|
|
64
|
+
|
|
65
|
+
This creates a RequestContext and sets it globally for access
|
|
66
|
+
throughout the request lifecycle.
|
|
67
|
+
"""
|
|
68
|
+
if trace_id is None:
|
|
69
|
+
trace_id = generate_trace_id()
|
|
70
|
+
|
|
71
|
+
g = RequestContext(trace_id=trace_id)
|
|
72
|
+
set_request_context(g)
|
|
73
|
+
logger.info(f"Request g object created with trace_id: {g.trace_id}")
|
|
74
|
+
return g
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_current_g() -> G | None:
|
|
78
|
+
"""
|
|
79
|
+
Get the current request's g object from anywhere in the application.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The current request's g object if available, None otherwise.
|
|
83
|
+
"""
|
|
84
|
+
from memos.context import get_current_context
|
|
85
|
+
|
|
86
|
+
return get_current_context()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def require_g() -> G:
|
|
90
|
+
"""
|
|
91
|
+
Get the current request's g object, raising an error if not available.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
The current request's g object.
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
RuntimeError: If called outside of a request context.
|
|
98
|
+
"""
|
|
99
|
+
from memos.context import require_context
|
|
100
|
+
|
|
101
|
+
return require_context()
|