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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
class SimpleTextSplitter:
|
|
2
|
+
"""Simple text splitter wrapper."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, chunk_size: int, chunk_overlap: int):
|
|
5
|
+
self.chunk_size = chunk_size
|
|
6
|
+
self.chunk_overlap = chunk_overlap
|
|
7
|
+
|
|
8
|
+
def chunk(self, text: str, **kwargs) -> list[str]:
|
|
9
|
+
return self._simple_split_text(text, self.chunk_size, self.chunk_overlap)
|
|
10
|
+
|
|
11
|
+
def _simple_split_text(self, text: str, chunk_size: int, chunk_overlap: int) -> list[str]:
|
|
12
|
+
"""
|
|
13
|
+
Simple text splitter as fallback when langchain is not available.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
text: Text to split
|
|
17
|
+
chunk_size: Maximum size of chunks
|
|
18
|
+
chunk_overlap: Overlap between chunks
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
List of text chunks
|
|
22
|
+
"""
|
|
23
|
+
if not text or len(text) <= chunk_size:
|
|
24
|
+
return [text] if text.strip() else []
|
|
25
|
+
|
|
26
|
+
chunks = []
|
|
27
|
+
start = 0
|
|
28
|
+
text_len = len(text)
|
|
29
|
+
|
|
30
|
+
while start < text_len:
|
|
31
|
+
# Calculate end position
|
|
32
|
+
end = min(start + chunk_size, text_len)
|
|
33
|
+
|
|
34
|
+
# If not the last chunk, try to break at a good position
|
|
35
|
+
if end < text_len:
|
|
36
|
+
# Try to break at newline, sentence end, or space
|
|
37
|
+
for separator in ["\n\n", "\n", "。", "!", "?", ". ", "! ", "? ", " "]:
|
|
38
|
+
last_sep = text.rfind(separator, start, end)
|
|
39
|
+
if last_sep != -1:
|
|
40
|
+
end = last_sep + len(separator)
|
|
41
|
+
break
|
|
42
|
+
|
|
43
|
+
chunk = text[start:end].strip()
|
|
44
|
+
if chunk:
|
|
45
|
+
chunks.append(chunk)
|
|
46
|
+
|
|
47
|
+
# Move start position with overlap
|
|
48
|
+
start = max(start + 1, end - chunk_overlap)
|
|
49
|
+
|
|
50
|
+
return chunks
|
memos/cli.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MemOS CLI Tool
|
|
3
|
+
This script provides command-line interface for MemOS operations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import zipfile
|
|
10
|
+
|
|
11
|
+
from io import BytesIO
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def export_openapi(output: str) -> bool:
|
|
15
|
+
"""Export OpenAPI schema to JSON file."""
|
|
16
|
+
from memos.api.server_api import app
|
|
17
|
+
|
|
18
|
+
# Create directory if it doesn't exist
|
|
19
|
+
if os.path.dirname(output):
|
|
20
|
+
os.makedirs(os.path.dirname(output), exist_ok=True)
|
|
21
|
+
|
|
22
|
+
with open(output, "w") as f:
|
|
23
|
+
json.dump(app.openapi(), f, indent=2)
|
|
24
|
+
f.write("\n")
|
|
25
|
+
|
|
26
|
+
print(f"✅ OpenAPI schema exported to: {output}")
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def download_examples(dest: str) -> bool:
|
|
31
|
+
import requests
|
|
32
|
+
|
|
33
|
+
"""Download examples from the MemOS repository."""
|
|
34
|
+
zip_url = "https://github.com/MemTensor/MemOS/archive/refs/heads/main.zip"
|
|
35
|
+
print(f"📥 Downloading examples from {zip_url}...")
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
response = requests.get(zip_url)
|
|
39
|
+
response.raise_for_status()
|
|
40
|
+
|
|
41
|
+
with zipfile.ZipFile(BytesIO(response.content)) as z:
|
|
42
|
+
extracted_files = []
|
|
43
|
+
for file in z.namelist():
|
|
44
|
+
if "MemOS-main/examples/" in file and not file.endswith("/"):
|
|
45
|
+
# Remove the prefix and extract to dest
|
|
46
|
+
relative_path = file.replace("MemOS-main/examples/", "")
|
|
47
|
+
extract_path = os.path.join(dest, relative_path)
|
|
48
|
+
|
|
49
|
+
# Create directory if it doesn't exist
|
|
50
|
+
os.makedirs(os.path.dirname(extract_path), exist_ok=True)
|
|
51
|
+
|
|
52
|
+
# Extract the file
|
|
53
|
+
with z.open(file) as source, open(extract_path, "wb") as target:
|
|
54
|
+
target.write(source.read())
|
|
55
|
+
extracted_files.append(extract_path)
|
|
56
|
+
|
|
57
|
+
print(f"✅ Examples downloaded to: {dest}")
|
|
58
|
+
print(f"📁 {len(extracted_files)} files extracted")
|
|
59
|
+
|
|
60
|
+
except requests.RequestException as e:
|
|
61
|
+
print(f"❌ Error downloading examples: {e}")
|
|
62
|
+
return False
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print(f"❌ Error extracting examples: {e}")
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main():
|
|
71
|
+
"""Main CLI entry point."""
|
|
72
|
+
parser = argparse.ArgumentParser(
|
|
73
|
+
prog="memos",
|
|
74
|
+
description="MemOS Command Line Interface",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Create subparsers for different commands
|
|
78
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
79
|
+
|
|
80
|
+
# Download examples command
|
|
81
|
+
examples_parser = subparsers.add_parser("download_examples", help="Download example files")
|
|
82
|
+
examples_parser.add_argument(
|
|
83
|
+
"--dest",
|
|
84
|
+
type=str,
|
|
85
|
+
default="./examples",
|
|
86
|
+
help="Destination directory for examples (default: ./examples)",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Export API command
|
|
90
|
+
api_parser = subparsers.add_parser("export_openapi", help="Export OpenAPI schema to JSON file")
|
|
91
|
+
api_parser.add_argument(
|
|
92
|
+
"--output",
|
|
93
|
+
type=str,
|
|
94
|
+
default="openapi.json",
|
|
95
|
+
help="Output path for OpenAPI schema (default: openapi.json)",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Parse arguments
|
|
99
|
+
args = parser.parse_args()
|
|
100
|
+
|
|
101
|
+
# Handle commands
|
|
102
|
+
if args.command == "download_examples":
|
|
103
|
+
success = download_examples(args.dest)
|
|
104
|
+
exit(0 if success else 1)
|
|
105
|
+
elif args.command == "export_openapi":
|
|
106
|
+
success = export_openapi(args.output)
|
|
107
|
+
exit(0 if success else 1)
|
|
108
|
+
else:
|
|
109
|
+
parser.print_help()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
main()
|
|
File without changes
|
memos/configs/base.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
8
|
+
|
|
9
|
+
from memos.log import get_logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseConfig(BaseModel):
|
|
16
|
+
"""Base configuration.
|
|
17
|
+
|
|
18
|
+
All configurations should inherit from this class.
|
|
19
|
+
This class uses Pydantic's ConfigDict to enforce strict validation
|
|
20
|
+
and forbids extra fields."""
|
|
21
|
+
|
|
22
|
+
model_schema: str = Field(
|
|
23
|
+
"NOT_SET",
|
|
24
|
+
description="Schema for configuration. This value will be automatically set.",
|
|
25
|
+
exclude=True,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
29
|
+
|
|
30
|
+
@model_validator(mode="after")
|
|
31
|
+
def set_default_schema(self) -> "BaseConfig":
|
|
32
|
+
dot_path_schema = self.__module__ + "." + self.__class__.__name__
|
|
33
|
+
if self.model_schema == dot_path_schema:
|
|
34
|
+
return self
|
|
35
|
+
if self.model_schema != "NOT_SET":
|
|
36
|
+
logger.warning(
|
|
37
|
+
f"Schema is set to {self.model_schema}, but it should be {dot_path_schema}. "
|
|
38
|
+
"Changing schema to the default value."
|
|
39
|
+
)
|
|
40
|
+
self.model_schema = dot_path_schema
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_json_file(cls, json_path: str) -> Any:
|
|
45
|
+
"""Load configuration from a JSON file."""
|
|
46
|
+
with open(json_path, encoding="utf-8") as f:
|
|
47
|
+
data = f.read()
|
|
48
|
+
return cls.model_validate_json(data)
|
|
49
|
+
|
|
50
|
+
def to_json_file(self, json_path: str) -> None:
|
|
51
|
+
"""Dump configuration to a JSON file."""
|
|
52
|
+
dir_path = os.path.dirname(json_path)
|
|
53
|
+
if dir_path:
|
|
54
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
55
|
+
with open(json_path, "w", encoding="utf-8") as f:
|
|
56
|
+
f.write(self.model_dump_json(indent=2, warnings="none"))
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_yaml_file(cls, yaml_path: str) -> Any:
|
|
60
|
+
"""Load configuration from a YAML file."""
|
|
61
|
+
with open(yaml_path, encoding="utf-8") as f:
|
|
62
|
+
data = yaml.safe_load(f)
|
|
63
|
+
return cls.model_validate(data)
|
|
64
|
+
|
|
65
|
+
def to_yaml_file(self, yaml_path: str) -> None:
|
|
66
|
+
"""Dump configuration to a YAML file."""
|
|
67
|
+
|
|
68
|
+
dir_path = os.path.dirname(yaml_path)
|
|
69
|
+
if dir_path:
|
|
70
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
71
|
+
|
|
72
|
+
with open(yaml_path, "w", encoding="utf-8") as f:
|
|
73
|
+
yaml.safe_dump(
|
|
74
|
+
self.model_dump(mode="json", warnings="none"),
|
|
75
|
+
f,
|
|
76
|
+
default_flow_style=False,
|
|
77
|
+
allow_unicode=True,
|
|
78
|
+
indent=2,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def get(self, key, default=None):
|
|
82
|
+
return getattr(self, key, default)
|
memos/configs/chunker.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Any, ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import Field, field_validator, model_validator
|
|
4
|
+
|
|
5
|
+
from memos.configs.base import BaseConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseChunkerConfig(BaseConfig):
|
|
9
|
+
"""Base configuration class for chunkers."""
|
|
10
|
+
|
|
11
|
+
tokenizer_or_token_counter: str = Field(
|
|
12
|
+
default="gpt2", description="Tokenizer model name or a token counting function"
|
|
13
|
+
)
|
|
14
|
+
chunk_size: int = Field(default=512, description="Maximum tokens per chunk")
|
|
15
|
+
chunk_overlap: int = Field(default=128, description="Overlap between chunks")
|
|
16
|
+
min_sentences_per_chunk: int = Field(default=1, description="Minimum sentences in each chunk")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SentenceChunkerConfig(BaseChunkerConfig):
|
|
20
|
+
"""Configuration for sentence-based text chunker."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MarkdownChunkerConfig(BaseChunkerConfig):
|
|
24
|
+
"""Configuration for markdown-based text chunker."""
|
|
25
|
+
|
|
26
|
+
headers_to_split_on: list[tuple[str, str]] = Field(
|
|
27
|
+
default=[("#", "Header 1"), ("##", "Header 2"), ("###", "Header 3")],
|
|
28
|
+
description="Headers to split on",
|
|
29
|
+
)
|
|
30
|
+
strip_headers: bool = Field(default=True, description="Strip headers from the text")
|
|
31
|
+
recursive: bool = Field(
|
|
32
|
+
default=False, description="Whether to use recursive character text splitter"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ChunkerConfigFactory(BaseConfig):
|
|
37
|
+
"""Factory class for creating chunker configurations."""
|
|
38
|
+
|
|
39
|
+
backend: str = Field(..., description="Backend for chunker")
|
|
40
|
+
config: dict[str, Any] = Field(..., description="Configuration for the chunker backend")
|
|
41
|
+
|
|
42
|
+
backend_to_class: ClassVar[dict[str, Any]] = {
|
|
43
|
+
"sentence": SentenceChunkerConfig,
|
|
44
|
+
"markdown": MarkdownChunkerConfig,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@field_validator("backend")
|
|
48
|
+
@classmethod
|
|
49
|
+
def validate_backend(cls, backend: str) -> str:
|
|
50
|
+
"""Validate the backend field."""
|
|
51
|
+
if backend not in cls.backend_to_class:
|
|
52
|
+
raise ValueError(f"Invalid backend: {backend}")
|
|
53
|
+
return backend
|
|
54
|
+
|
|
55
|
+
@model_validator(mode="after")
|
|
56
|
+
def create_config(self) -> "ChunkerConfigFactory":
|
|
57
|
+
config_class = self.backend_to_class[self.backend]
|
|
58
|
+
self.config = config_class(**self.config)
|
|
59
|
+
return self
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import Any, ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import Field, field_validator, model_validator
|
|
4
|
+
|
|
5
|
+
from memos.configs.base import BaseConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseEmbedderConfig(BaseConfig):
|
|
9
|
+
"""Base configuration class for embedding models."""
|
|
10
|
+
|
|
11
|
+
model_name_or_path: str = Field(..., description="Model name or path")
|
|
12
|
+
embedding_dims: int | None = Field(
|
|
13
|
+
default=None, description="Number of dimensions for the embedding"
|
|
14
|
+
)
|
|
15
|
+
max_tokens: int | None = Field(
|
|
16
|
+
default=8192,
|
|
17
|
+
description="Maximum number of tokens per text. Texts exceeding this limit will be automatically truncated. Set to None to disable truncation.",
|
|
18
|
+
)
|
|
19
|
+
headers_extra: dict[str, Any] | None = Field(
|
|
20
|
+
default=None,
|
|
21
|
+
description="Extra headers for the embedding model, only for universal_api backend",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class OllamaEmbedderConfig(BaseEmbedderConfig):
|
|
26
|
+
api_base: str = Field(default="http://localhost:11434", description="Base URL for Ollama API")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ArkEmbedderConfig(BaseEmbedderConfig):
|
|
30
|
+
api_key: str = Field(..., description="Ark API key")
|
|
31
|
+
api_base: str = Field(
|
|
32
|
+
default="https://ark.cn-beijing.volces.com/api/v3/", description="Base URL for Ark API"
|
|
33
|
+
)
|
|
34
|
+
chunk_size: int = Field(default=1, description="Chunk size for Ark API")
|
|
35
|
+
multi_modal: bool = Field(
|
|
36
|
+
default=False,
|
|
37
|
+
description="Whether to use multi-modal embedding (text + image) with Ark",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SenTranEmbedderConfig(BaseEmbedderConfig):
|
|
42
|
+
"""Configuration class for Sentence Transformer embeddings."""
|
|
43
|
+
|
|
44
|
+
trust_remote_code: bool = Field(
|
|
45
|
+
default=True,
|
|
46
|
+
description="Whether to trust remote code when loading the model",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UniversalAPIEmbedderConfig(BaseEmbedderConfig):
|
|
51
|
+
"""
|
|
52
|
+
Configuration class for universal API embedding providers, e.g.,
|
|
53
|
+
OpenAI, etc.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
provider: str = Field(..., description="Provider name, e.g., 'openai'")
|
|
57
|
+
api_key: str = Field(..., description="API key for the embedding provider")
|
|
58
|
+
base_url: str | None = Field(
|
|
59
|
+
default=None, description="Optional base URL for custom or proxied endpoint"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class EmbedderConfigFactory(BaseConfig):
|
|
64
|
+
"""Factory class for creating embedder configurations."""
|
|
65
|
+
|
|
66
|
+
backend: str = Field(..., description="Backend for embedding model")
|
|
67
|
+
config: dict[str, Any] = Field(..., description="Configuration for the embedding model backend")
|
|
68
|
+
|
|
69
|
+
backend_to_class: ClassVar[dict[str, Any]] = {
|
|
70
|
+
"ollama": OllamaEmbedderConfig,
|
|
71
|
+
"sentence_transformer": SenTranEmbedderConfig,
|
|
72
|
+
"ark": ArkEmbedderConfig,
|
|
73
|
+
"universal_api": UniversalAPIEmbedderConfig,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@field_validator("backend")
|
|
77
|
+
@classmethod
|
|
78
|
+
def validate_backend(cls, backend: str) -> str:
|
|
79
|
+
"""Validate the backend field."""
|
|
80
|
+
if backend not in cls.backend_to_class:
|
|
81
|
+
raise ValueError(f"Invalid backend: {backend}")
|
|
82
|
+
return backend
|
|
83
|
+
|
|
84
|
+
@model_validator(mode="after")
|
|
85
|
+
def create_config(self) -> "EmbedderConfigFactory":
|
|
86
|
+
config_class = self.backend_to_class[self.backend]
|
|
87
|
+
self.config = config_class(**self.config)
|
|
88
|
+
return self
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
from typing import Any, ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
4
|
+
|
|
5
|
+
from memos.configs.base import BaseConfig
|
|
6
|
+
from memos.configs.vec_db import VectorDBConfigFactory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseGraphDBConfig(BaseConfig):
|
|
10
|
+
"""Base class for all graph database configurations."""
|
|
11
|
+
|
|
12
|
+
uri: str | list
|
|
13
|
+
user: str
|
|
14
|
+
password: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Neo4jGraphDBConfig(BaseGraphDBConfig):
|
|
18
|
+
"""
|
|
19
|
+
Neo4j-specific configuration.
|
|
20
|
+
|
|
21
|
+
This config supports:
|
|
22
|
+
1) Physical isolation (multi-db) — each user gets a dedicated Neo4j database.
|
|
23
|
+
2) Logical isolation (single-db) — all users share one or more databases, but each node is tagged with `user_name`.
|
|
24
|
+
|
|
25
|
+
How to use:
|
|
26
|
+
- If `use_multi_db=True`, then `db_name` should usually be the same as `user_name`.
|
|
27
|
+
Each user gets a separate database for physical isolation.
|
|
28
|
+
Example: db_name = "alice", user_name = None or "alice".
|
|
29
|
+
|
|
30
|
+
- If `use_multi_db=False`, then `db_name` is your shared database (e.g., "neo4j" or "shared_db").
|
|
31
|
+
You must provide `user_name` to logically isolate each user's data.
|
|
32
|
+
All nodes and queries must respect this tag.
|
|
33
|
+
|
|
34
|
+
Example configs:
|
|
35
|
+
---
|
|
36
|
+
# Physical isolation:
|
|
37
|
+
db_name = "alice"
|
|
38
|
+
use_multi_db = True
|
|
39
|
+
user_name = None
|
|
40
|
+
|
|
41
|
+
# Logical isolation:
|
|
42
|
+
db_name = "shared_db_student_group"
|
|
43
|
+
use_multi_db = False
|
|
44
|
+
user_name = "alice"
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
db_name: str = Field(..., description="The name of the target Neo4j database")
|
|
48
|
+
auto_create: bool = Field(
|
|
49
|
+
default=False,
|
|
50
|
+
description="If True, automatically create the target db_name in multi-db mode if it does not exist.",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
use_multi_db: bool = Field(
|
|
54
|
+
default=True,
|
|
55
|
+
description=(
|
|
56
|
+
"If True: use Neo4j's multi-database feature for physical isolation; "
|
|
57
|
+
"each user typically gets a separate database. "
|
|
58
|
+
"If False: use a single shared database with logical isolation by user_name."
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
user_name: str | None = Field(
|
|
63
|
+
default=None,
|
|
64
|
+
description=(
|
|
65
|
+
"Logical user or tenant ID for data isolation. "
|
|
66
|
+
"Required if use_multi_db is False. "
|
|
67
|
+
"All nodes must be tagged with this and all queries must filter by this."
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
embedding_dimension: int = Field(default=768, description="Dimension of vector embedding")
|
|
72
|
+
|
|
73
|
+
@model_validator(mode="after")
|
|
74
|
+
def validate_config(self):
|
|
75
|
+
"""Validate logical constraints to avoid misconfiguration."""
|
|
76
|
+
if not self.use_multi_db and not self.user_name:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
"In single-database mode (use_multi_db=False), `user_name` must be provided for logical isolation."
|
|
79
|
+
)
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Neo4jCommunityGraphDBConfig(Neo4jGraphDBConfig):
|
|
84
|
+
"""
|
|
85
|
+
Community edition config for Neo4j.
|
|
86
|
+
|
|
87
|
+
Notes:
|
|
88
|
+
- Must set `use_multi_db = False`
|
|
89
|
+
- Must provide `user_name` for logical isolation
|
|
90
|
+
- Embedding vector DB config is required
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
vec_config: VectorDBConfigFactory = Field(
|
|
94
|
+
..., description="Vector DB config for embedding search"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
@model_validator(mode="after")
|
|
98
|
+
def validate_community(self):
|
|
99
|
+
if self.use_multi_db:
|
|
100
|
+
raise ValueError("Neo4j Community Edition does not support use_multi_db=True.")
|
|
101
|
+
if not self.user_name:
|
|
102
|
+
raise ValueError("Neo4j Community config requires user_name for logical isolation.")
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class NebulaGraphDBConfig(BaseGraphDBConfig):
|
|
107
|
+
"""
|
|
108
|
+
NebulaGraph-specific configuration.
|
|
109
|
+
|
|
110
|
+
Key concepts:
|
|
111
|
+
- `space`: Equivalent to a database or namespace. All tag/edge/schema live within a space.
|
|
112
|
+
- `user_name`: Used for logical tenant isolation if needed.
|
|
113
|
+
- `auto_create`: Whether to automatically create the target space if it does not exist.
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
---
|
|
117
|
+
hosts = ["127.0.0.1:9669"]
|
|
118
|
+
user = "root"
|
|
119
|
+
password = "nebula"
|
|
120
|
+
space = "shared_graph"
|
|
121
|
+
user_name = "alice"
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
space: str = Field(
|
|
125
|
+
..., description="The name of the target NebulaGraph space (like a database)"
|
|
126
|
+
)
|
|
127
|
+
user_name: str | None = Field(
|
|
128
|
+
default=None,
|
|
129
|
+
description="Logical user or tenant ID for data isolation (optional, used in metadata tagging)",
|
|
130
|
+
)
|
|
131
|
+
auto_create: bool = Field(
|
|
132
|
+
default=False,
|
|
133
|
+
description="Whether to auto-create the space if it does not exist",
|
|
134
|
+
)
|
|
135
|
+
use_multi_db: bool = Field(
|
|
136
|
+
default=True,
|
|
137
|
+
description=(
|
|
138
|
+
"If True: use Neo4j's multi-database feature for physical isolation; "
|
|
139
|
+
"each user typically gets a separate database. "
|
|
140
|
+
"If False: use a single shared database with logical isolation by user_name."
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
max_client: int = Field(
|
|
144
|
+
default=1000,
|
|
145
|
+
description=("max_client"),
|
|
146
|
+
)
|
|
147
|
+
embedding_dimension: int = Field(default=3072, description="Dimension of vector embedding")
|
|
148
|
+
|
|
149
|
+
@model_validator(mode="after")
|
|
150
|
+
def validate_config(self):
|
|
151
|
+
"""Validate config."""
|
|
152
|
+
if not self.space:
|
|
153
|
+
raise ValueError("`space` must be provided")
|
|
154
|
+
return self
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class PolarDBGraphDBConfig(BaseConfig):
|
|
158
|
+
"""
|
|
159
|
+
PolarDB-specific configuration.
|
|
160
|
+
|
|
161
|
+
Key concepts:
|
|
162
|
+
- `db_name`: The name of the target PolarDB database
|
|
163
|
+
- `user_name`: Used for logical tenant isolation if needed
|
|
164
|
+
- `auto_create`: Whether to automatically create the target database if it does not exist
|
|
165
|
+
- `use_multi_db`: Whether to use multi-database mode for physical isolation
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
---
|
|
169
|
+
host = "localhost"
|
|
170
|
+
port = 5432
|
|
171
|
+
user = "postgres"
|
|
172
|
+
password = "password"
|
|
173
|
+
db_name = "memos_db"
|
|
174
|
+
user_name = "alice"
|
|
175
|
+
use_multi_db = True
|
|
176
|
+
auto_create = True
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
host: str = Field(..., description="Database host")
|
|
180
|
+
port: int = Field(default=5432, description="Database port")
|
|
181
|
+
user: str = Field(..., description="Database user")
|
|
182
|
+
password: str = Field(..., description="Database password")
|
|
183
|
+
db_name: str = Field(..., description="The name of the target PolarDB database")
|
|
184
|
+
user_name: str | None = Field(
|
|
185
|
+
default=None,
|
|
186
|
+
description="Logical user or tenant ID for data isolation (optional, used in metadata tagging)",
|
|
187
|
+
)
|
|
188
|
+
auto_create: bool = Field(
|
|
189
|
+
default=False,
|
|
190
|
+
description="Whether to auto-create the database if it does not exist",
|
|
191
|
+
)
|
|
192
|
+
use_multi_db: bool = Field(
|
|
193
|
+
default=True,
|
|
194
|
+
description=(
|
|
195
|
+
"If True: use multi-database mode for physical isolation; "
|
|
196
|
+
"each tenant typically gets a separate database. "
|
|
197
|
+
"If False: use a single shared database with logical isolation by user_name."
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
embedding_dimension: int = Field(default=1024, description="Dimension of vector embedding")
|
|
201
|
+
maxconn: int = Field(
|
|
202
|
+
default=100,
|
|
203
|
+
description="Maximum number of connections in the connection pool",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
@model_validator(mode="after")
|
|
207
|
+
def validate_config(self):
|
|
208
|
+
"""Validate config."""
|
|
209
|
+
if not self.db_name:
|
|
210
|
+
raise ValueError("`db_name` must be provided")
|
|
211
|
+
return self
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class GraphDBConfigFactory(BaseModel):
|
|
215
|
+
backend: str = Field(..., description="Backend for graph database")
|
|
216
|
+
config: dict[str, Any] = Field(..., description="Configuration for the graph database backend")
|
|
217
|
+
|
|
218
|
+
backend_to_class: ClassVar[dict[str, Any]] = {
|
|
219
|
+
"neo4j": Neo4jGraphDBConfig,
|
|
220
|
+
"neo4j-community": Neo4jCommunityGraphDBConfig,
|
|
221
|
+
"nebular": NebulaGraphDBConfig,
|
|
222
|
+
"polardb": PolarDBGraphDBConfig,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@field_validator("backend")
|
|
226
|
+
@classmethod
|
|
227
|
+
def validate_backend(cls, backend: str) -> str:
|
|
228
|
+
if backend not in cls.backend_to_class:
|
|
229
|
+
raise ValueError(f"Unsupported graph db backend: {backend}")
|
|
230
|
+
return backend
|
|
231
|
+
|
|
232
|
+
@model_validator(mode="after")
|
|
233
|
+
def instantiate_config(self):
|
|
234
|
+
config_class = self.backend_to_class[self.backend]
|
|
235
|
+
self.config = config_class(**self.config)
|
|
236
|
+
return self
|