From 954fdab5fe5e95bedf4709f66c850f5d6a6f102f Mon Sep 17 00:00:00 2001 From: HarveyXiang Date: Wed, 19 Nov 2025 12:41:01 +0800 Subject: [PATCH 1/9] Update API Reference link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4269abf0a..c5fcd93ce 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Get Free API: [Try API](https://memos-dashboard.openmem.net/quickstart/?source=g - **Website**: https://memos.openmem.net/ - **Documentation**: https://memos-docs.openmem.net/home/overview/ -- **API Reference**: https://memos-docs.openmem.net/docs/api/info/ +- **API Reference**: https://memos-docs.openmem.net/api-reference/configure-memos/ - **Source Code**: https://github.com/MemTensor/MemOS ## 📰 News From 8a93addf96feaefcf0bea346d357974c5354ed29 Mon Sep 17 00:00:00 2001 From: "yuan.wang" Date: Wed, 19 Nov 2025 15:57:30 +0800 Subject: [PATCH 2/9] hotfix bug in pref init --- README.md | 4 +-- evaluation/.env-example | 1 - src/memos/api/routers/server_router.py | 50 +++++++++++++++++--------- src/memos/mem_cube/navie.py | 20 ++++++----- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index c5fcd93ce..cb464b9cd 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ Designed for **AI companions, role-playing NPCs, and multi-agent systems**, MemO -Get Free API: [Try API](https://memos-dashboard.openmem.net/quickstart/?source=github) - +Get Free API: [Try API](https://memos-dashboard.openmem.net/quickstart/?source=github) + --- diff --git a/evaluation/.env-example b/evaluation/.env-example index 5381532c2..bab6f679e 100644 --- a/evaluation/.env-example +++ b/evaluation/.env-example @@ -21,4 +21,3 @@ MEMU_API_KEY="mu_xxx" SUPERMEMORY_API_KEY="sm_xxx" MEMOBASE_API_KEY="xxx" MEMOBASE_PROJECT_URL="http://***.***.***.***:8019" - diff --git a/src/memos/api/routers/server_router.py b/src/memos/api/routers/server_router.py index 8df383bfb..23aec0cb0 100644 --- a/src/memos/api/routers/server_router.py +++ b/src/memos/api/routers/server_router.py @@ -187,7 +187,11 @@ def init_server(): # Create component instances graph_db = GraphStoreFactory.from_config(graph_db_config) - vector_db = VecDBFactory.from_config(vector_db_config) + vector_db = ( + VecDBFactory.from_config(vector_db_config) + if os.getenv("ENABLE_PREFERENCE_MEMORY", "false").lower() == "true" + else None + ) llm = LLMFactory.from_config(llm_config) embedder = EmbedderFactory.from_config(embedder_config) mem_reader = MemReaderFactory.from_config(mem_reader_config) @@ -195,24 +199,36 @@ def init_server(): internet_retriever = InternetRetrieverFactory.from_config( internet_retriever_config, embedder=embedder ) - pref_extractor = ExtractorFactory.from_config( - config_factory=pref_extractor_config, - llm_provider=llm, - embedder=embedder, - vector_db=vector_db, + pref_extractor = ( + ExtractorFactory.from_config( + config_factory=pref_extractor_config, + llm_provider=llm, + embedder=embedder, + vector_db=vector_db, + ) + if os.getenv("ENABLE_PREFERENCE_MEMORY", "false").lower() == "true" + else None ) - pref_adder = AdderFactory.from_config( - config_factory=pref_adder_config, - llm_provider=llm, - embedder=embedder, - vector_db=vector_db, + pref_adder = ( + AdderFactory.from_config( + config_factory=pref_adder_config, + llm_provider=llm, + embedder=embedder, + vector_db=vector_db, + ) + if os.getenv("ENABLE_PREFERENCE_MEMORY", "false").lower() == "true" + else None ) - pref_retriever = RetrieverFactory.from_config( - config_factory=pref_retriever_config, - llm_provider=llm, - embedder=embedder, - reranker=reranker, - vector_db=vector_db, + pref_retriever = ( + RetrieverFactory.from_config( + config_factory=pref_retriever_config, + llm_provider=llm, + embedder=embedder, + reranker=reranker, + vector_db=vector_db, + ) + if os.getenv("ENABLE_PREFERENCE_MEMORY", "false").lower() == "true" + else None ) # Initialize memory manager diff --git a/src/memos/mem_cube/navie.py b/src/memos/mem_cube/navie.py index ba9f136b7..3acc441a0 100644 --- a/src/memos/mem_cube/navie.py +++ b/src/memos/mem_cube/navie.py @@ -58,14 +58,18 @@ def __init__( ) self._act_mem: BaseActMemory | None = None self._para_mem: BaseParaMemory | None = None - self._pref_mem: BaseTextMemory | None = SimplePreferenceTextMemory( - extractor_llm=llm, - vector_db=vector_db, - embedder=embedder, - reranker=reranker, - extractor=pref_extractor, - adder=pref_adder, - retriever=pref_retriever, + self._pref_mem: BaseTextMemory | None = ( + SimplePreferenceTextMemory( + extractor_llm=llm, + vector_db=vector_db, + embedder=embedder, + reranker=reranker, + extractor=pref_extractor, + adder=pref_adder, + retriever=pref_retriever, + ) + if os.getenv("ENABLE_PREFERENCE_MEMORY", "false").lower() == "true" + else None ) def load( From b0482974c8b4426150f4ab2bbd5dd10c669c33ed Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 19 Nov 2025 21:28:41 +0800 Subject: [PATCH 3/9] feat: log support time rotating --- src/memos/log.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/memos/log.py b/src/memos/log.py index faa808414..1f107df2b 100644 --- a/src/memos/log.py +++ b/src/memos/log.py @@ -34,10 +34,12 @@ def _setup_logfile() -> Path: Returns: the logfile Path """ - logfile = Path(settings.MEMOS_DIR / "logs" / "memos.log") - logfile.parent.mkdir(parents=True, exist_ok=True) - logfile.touch(exist_ok=True) - return logfile + today_str = time.strftime("%Y-%m-%d", time.localtime()) + today_file_name = f"{today_str}.memos.log" + today_file = Path(settings.MEMOS_DIR / "logs" / today_file_name) + today_file.parent.mkdir(parents=True, exist_ok=True) + + return today_file class ContextFilter(logging.Filter): @@ -187,7 +189,7 @@ def close(self): }, "handlers": { "console": { - "level": selected_log_level, + "level": "DEBUG", "class": "logging.StreamHandler", "stream": stdout, "formatter": "no_datetime", @@ -195,10 +197,11 @@ def close(self): }, "file": { "level": "DEBUG", - "class": "logging.handlers.RotatingFileHandler", + "class": "logging.handlers.TimedRotatingFileHandler", + "when": "midnight", + "interval": 1, + "backupCount": 5, "filename": _setup_logfile(), - "maxBytes": 1024**2 * 10, - "backupCount": 10, "formatter": "standard", "filters": ["context_filter"], }, From 307e06af4ef3d88f63cb59d6c2215ebb0fde1a0a Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 19 Nov 2025 21:40:05 +0800 Subject: [PATCH 4/9] feat: log support time rotating --- src/memos/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memos/log.py b/src/memos/log.py index 1f107df2b..89c75cc30 100644 --- a/src/memos/log.py +++ b/src/memos/log.py @@ -189,7 +189,7 @@ def close(self): }, "handlers": { "console": { - "level": "DEBUG", + "level": selected_log_level, "class": "logging.StreamHandler", "stream": stdout, "formatter": "no_datetime", @@ -200,7 +200,7 @@ def close(self): "class": "logging.handlers.TimedRotatingFileHandler", "when": "midnight", "interval": 1, - "backupCount": 5, + "backupCount": 3, "filename": _setup_logfile(), "formatter": "standard", "filters": ["context_filter"], From 76f8f312d812a9d94e8851553024ad6c19712112 Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Thu, 20 Nov 2025 10:18:06 +0800 Subject: [PATCH 5/9] feat: log support time rotating --- src/memos/log.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/memos/log.py b/src/memos/log.py index 89c75cc30..874f2c6a7 100644 --- a/src/memos/log.py +++ b/src/memos/log.py @@ -34,12 +34,11 @@ def _setup_logfile() -> Path: Returns: the logfile Path """ - today_str = time.strftime("%Y-%m-%d", time.localtime()) - today_file_name = f"{today_str}.memos.log" - today_file = Path(settings.MEMOS_DIR / "logs" / today_file_name) - today_file.parent.mkdir(parents=True, exist_ok=True) + logfile = Path(settings.MEMOS_DIR / "logs" / "memos.log") + logfile.parent.mkdir(parents=True, exist_ok=True) + logfile.touch(exist_ok=True) - return today_file + return logfile class ContextFilter(logging.Filter): From 0d5016774448a053feb1b4d2a402b9ebc2ca810f Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Thu, 20 Nov 2025 11:47:25 +0800 Subject: [PATCH 6/9] feat: delete useless log --- src/memos/mem_scheduler/utils/metrics.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/memos/mem_scheduler/utils/metrics.py b/src/memos/mem_scheduler/utils/metrics.py index 5155c98b3..ff5ad4e42 100644 --- a/src/memos/mem_scheduler/utils/metrics.py +++ b/src/memos/mem_scheduler/utils/metrics.py @@ -184,12 +184,7 @@ def on_enqueue( inst_rate = (1.0 / max(1e-3, dt)) if dt is not None else 0.0 # first sample: no spike ls.last_enqueue_ts = now ls.backlog += 1 - old_lam = ls.lambda_ewma.value_at(now) ls.lambda_ewma.update(inst_rate, now) - new_lam = ls.lambda_ewma.value_at(now) - print( - f"[DEBUG enqueue] {label} backlog={ls.backlog} dt={dt if dt is not None else '—'}s inst={inst_rate:.3f} λ {old_lam:.3f}→{new_lam:.3f}" - ) self._label_topk[label].add(mem_cube_id) ds = self._get_detail(label, mem_cube_id) if ds: From d6557f1767e1b146f2dd105356f3f17d4c99c329 Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Thu, 20 Nov 2025 12:00:04 +0800 Subject: [PATCH 7/9] feat: delete useless log --- .../monitors/dispatcher_monitor.py | 17 +++-------------- src/memos/mem_scheduler/utils/metrics.py | 5 ----- .../tree_text_memory/retrieve/recall.py | 18 ------------------ 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/src/memos/mem_scheduler/monitors/dispatcher_monitor.py b/src/memos/mem_scheduler/monitors/dispatcher_monitor.py index 46c4e2d49..58ec51beb 100644 --- a/src/memos/mem_scheduler/monitors/dispatcher_monitor.py +++ b/src/memos/mem_scheduler/monitors/dispatcher_monitor.py @@ -129,7 +129,9 @@ def _check_pools_health(self) -> None: pool_info=pool_info, stuck_max_interval=4, ) - logger.info(f"Pool '{name}'. is_healthy: {is_healthy}. pool_info: {pool_info}") + if not is_healthy: + logger.info(f"Pool '{name}'. is_healthy: {is_healthy}. pool_info: {pool_info}") + with self._pool_lock: if is_healthy: pool_info["failure_count"] = 0 @@ -231,20 +233,7 @@ def _check_pool_health( # Log health status with comprehensive information if self.dispatcher: - # Check thread activity - active_threads = sum( - 1 - for t in threading.enumerate() - if t.name.startswith(executor._thread_name_prefix) # pylint: disable=protected-access - ) - - task_count = self.dispatcher.get_running_task_count() max_workers = pool_info.get("max_workers", 0) - stuck_count = len(stuck_tasks) - logger.info( - f"Pool health check passed - {active_threads} active threads, " - f"{task_count} running tasks, pool size: {max_workers}, stuck tasks: {stuck_count}" - ) return True, "" diff --git a/src/memos/mem_scheduler/utils/metrics.py b/src/memos/mem_scheduler/utils/metrics.py index ff5ad4e42..0434bb4dc 100644 --- a/src/memos/mem_scheduler/utils/metrics.py +++ b/src/memos/mem_scheduler/utils/metrics.py @@ -217,12 +217,7 @@ def on_done( ls.last_done_ts = now if ls.backlog > 0: ls.backlog -= 1 - old_mu = ls.mu_ewma.value_at(now) ls.mu_ewma.update(inst_rate, now) - new_mu = ls.mu_ewma.value_at(now) - print( - f"[DEBUG done] {label} backlog={ls.backlog} dt={dt if dt is not None else '—'}s inst={inst_rate:.3f} μ {old_mu:.3f}→{new_mu:.3f}" - ) ds = self._detail_stats.get((label, mem_cube_id)) if ds: prev_ts_d = ds.last_done_ts diff --git a/src/memos/memories/textual/tree_text_memory/retrieve/recall.py b/src/memos/memories/textual/tree_text_memory/retrieve/recall.py index 8cf2f47f3..40e220658 100644 --- a/src/memos/memories/textual/tree_text_memory/retrieve/recall.py +++ b/src/memos/memories/textual/tree_text_memory/retrieve/recall.py @@ -104,15 +104,6 @@ def retrieve( # Merge and deduplicate by ID combined = {item.id: item for item in graph_results + vector_results + bm25_results} - graph_ids = {item.id for item in graph_results} - combined_ids = set(combined.keys()) - lost_ids = graph_ids - combined_ids - - if lost_ids: - print( - f"[DEBUG] The following nodes were in graph_results but missing in combined: {lost_ids}" - ) - return list(combined.values()) def retrieve_from_cube( @@ -150,15 +141,6 @@ def retrieve_from_cube( # Merge and deduplicate by ID combined = {item.id: item for item in graph_results} - graph_ids = {item.id for item in graph_results} - combined_ids = set(combined.keys()) - lost_ids = graph_ids - combined_ids - - if lost_ids: - print( - f"[DEBUG] The following nodes were in graph_results but missing in combined: {lost_ids}" - ) - return list(combined.values()) def _graph_recall( From e7ea7f992dffeb5c5b9c3bdfb769f7b435c511fb Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Thu, 20 Nov 2025 15:01:06 +0800 Subject: [PATCH 8/9] feat: add time log --- src/memos/embedders/universal_api.py | 2 +- src/memos/llms/openai.py | 12 ++++++++++-- src/memos/log.py | 2 +- src/memos/reranker/http_bge.py | 2 +- src/memos/utils.py | 3 ++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/memos/embedders/universal_api.py b/src/memos/embedders/universal_api.py index fc51cf073..583a02acb 100644 --- a/src/memos/embedders/universal_api.py +++ b/src/memos/embedders/universal_api.py @@ -26,7 +26,7 @@ def __init__(self, config: UniversalAPIEmbedderConfig): else: raise ValueError(f"Embeddings unsupported provider: {self.provider}") - @timed(log=True, log_prefix="EmbedderAPI") + @timed(log=True, log_prefix="model_timed_embedding") def embed(self, texts: list[str]) -> list[list[float]]: if self.provider == "openai" or self.provider == "azure": try: diff --git a/src/memos/llms/openai.py b/src/memos/llms/openai.py index 1a1703340..da55ae593 100644 --- a/src/memos/llms/openai.py +++ b/src/memos/llms/openai.py @@ -1,5 +1,6 @@ import hashlib import json +import time from collections.abc import Generator from typing import ClassVar @@ -57,12 +58,15 @@ def clear_cache(cls): cls._instances.clear() logger.info("OpenAI LLM instance cache cleared") - @timed(log=True, log_prefix="OpenAI LLM") + @timed(log=True, log_prefix="model_timed_openai") def generate(self, messages: MessageList, **kwargs) -> str: """Generate a response from OpenAI LLM, optionally overriding generation params.""" temperature = kwargs.get("temperature", self.config.temperature) max_tokens = kwargs.get("max_tokens", self.config.max_tokens) top_p = kwargs.get("top_p", self.config.top_p) + start_time = time.time() + logger.info(f"openai model request start, model_name: {self.config.model_name_or_path}") + response = self.client.chat.completions.create( model=self.config.model_name_or_path, messages=messages, @@ -71,7 +75,11 @@ def generate(self, messages: MessageList, **kwargs) -> str: max_tokens=max_tokens, top_p=top_p, ) - logger.info(f"Response from OpenAI: {response.model_dump_json()}") + + end_time = time.time() + logger.info( + f"openai model request end, time_cost: {end_time - start_time:.0f} ms, response from OpenAI: {response.model_dump_json()}" + ) response_content = response.choices[0].message.content if self.config.remove_think_prefix: return remove_thinking_tags(response_content) diff --git a/src/memos/log.py b/src/memos/log.py index 874f2c6a7..6b98e3615 100644 --- a/src/memos/log.py +++ b/src/memos/log.py @@ -188,7 +188,7 @@ def close(self): }, "handlers": { "console": { - "level": selected_log_level, + "level": "INFO", "class": "logging.StreamHandler", "stream": stdout, "formatter": "no_datetime", diff --git a/src/memos/reranker/http_bge.py b/src/memos/reranker/http_bge.py index 41011df14..db5a51fc2 100644 --- a/src/memos/reranker/http_bge.py +++ b/src/memos/reranker/http_bge.py @@ -119,7 +119,7 @@ def __init__( self.warn_unknown_filter_keys = bool(warn_unknown_filter_keys) self._warned_missing_keys: set[str] = set() - @timed(log=True, log_prefix="RerankerAPI") + @timed(log=True, log_prefix="model_timed_rerank") def rerank( self, query: str, diff --git a/src/memos/utils.py b/src/memos/utils.py index 9ae27bb81..4b1a59834 100644 --- a/src/memos/utils.py +++ b/src/memos/utils.py @@ -17,8 +17,9 @@ def wrapper(*args, **kwargs): start = time.perf_counter() result = fn(*args, **kwargs) elapsed = time.perf_counter() - start + elapsed_ms = elapsed * 1000.0 if log: - logger.info(f"[TIMER] {log_prefix or fn.__name__} took {elapsed:.2f} seconds") + logger.info(f"[TIMER] {log_prefix or fn.__name__} took {elapsed_ms:.0f} ms") return result return wrapper From 66d558d34ec374c456897008b70538ccb6fcba7d Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Thu, 20 Nov 2025 15:03:55 +0800 Subject: [PATCH 9/9] feat: add time log --- src/memos/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memos/log.py b/src/memos/log.py index 6b98e3615..874f2c6a7 100644 --- a/src/memos/log.py +++ b/src/memos/log.py @@ -188,7 +188,7 @@ def close(self): }, "handlers": { "console": { - "level": "INFO", + "level": selected_log_level, "class": "logging.StreamHandler", "stream": stdout, "formatter": "no_datetime",