From f100c1dd93d304ea01fcbe3284d6c83a5bbfcc42 Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 10 Dec 2025 10:25:44 +0800 Subject: [PATCH 1/6] feat: timer false --- src/memos/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memos/utils.py b/src/memos/utils.py index a29eaf99d..cb50aab8d 100644 --- a/src/memos/utils.py +++ b/src/memos/utils.py @@ -80,7 +80,7 @@ def wrapper(*args, **kwargs): return decorator(func) -def timed(func=None, *, log=True, log_prefix=""): +def timed(func=None, *, log=False, log_prefix=""): def decorator(fn): def wrapper(*args, **kwargs): start = time.perf_counter() From 25fee6a55805a14fdc964e009e0f8801aea8509f Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 24 Dec 2025 11:57:52 +0800 Subject: [PATCH 2/6] feat: add openai request body log --- src/memos/llms/openai.py | 49 ++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/memos/llms/openai.py b/src/memos/llms/openai.py index 1d180eebd..563b8723e 100644 --- a/src/memos/llms/openai.py +++ b/src/memos/llms/openai.py @@ -31,21 +31,23 @@ def __init__(self, config: OpenAILLMConfig): @timed_with_status( log_prefix="OpenAI LLM", log_extra_args=lambda self, messages, **kwargs: { - "model_name_or_path": kwargs.get("model_name_or_path", self.config.model_name_or_path) + "model_name_or_path": kwargs.get("model_name_or_path", self.config.model_name_or_path), + "messages": messages, }, ) def generate(self, messages: MessageList, **kwargs) -> str: """Generate a response from OpenAI LLM, optionally overriding generation params.""" - response = self.client.chat.completions.create( - model=kwargs.get("model_name_or_path", self.config.model_name_or_path), - messages=messages, - 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), - extra_body=kwargs.get("extra_body", self.config.extra_body), - tools=kwargs.get("tools", NOT_GIVEN), - timeout=kwargs.get("timeout", 30), - ) + request_body = { + "model": kwargs.get("model_name_or_path", self.config.model_name_or_path), + "messages": messages, + "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), + "extra_body": kwargs.get("extra_body", self.config.extra_body), + "tools": kwargs.get("tools", NOT_GIVEN), + } + logger.info(f"OpenAI LLM Request body: {request_body}") + response = self.client.chat.completions.create(**request_body) logger.info(f"Response from OpenAI: {response.model_dump_json()}") tool_calls = getattr(response.choices[0].message, "tool_calls", None) if isinstance(tool_calls, list) and len(tool_calls) > 0: @@ -61,7 +63,7 @@ def generate(self, messages: MessageList, **kwargs) -> str: return response_content @timed_with_status( - log_prefix="OpenAI LLM", + log_prefix="OpenAI LLM Stream", log_extra_args=lambda self, messages, **kwargs: { "model_name_or_path": self.config.model_name_or_path }, @@ -72,16 +74,19 @@ def generate_stream(self, messages: MessageList, **kwargs) -> Generator[str, Non logger.info("stream api not support tools") return - response = self.client.chat.completions.create( - model=self.config.model_name_or_path, - messages=messages, - stream=True, - 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), - extra_body=kwargs.get("extra_body", self.config.extra_body), - tools=kwargs.get("tools", NOT_GIVEN), - ) + request_body = { + "model": self.config.model_name_or_path, + "messages": messages, + "stream": True, + "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), + "extra_body": kwargs.get("extra_body", self.config.extra_body), + "tools": kwargs.get("tools", NOT_GIVEN), + } + + logger.info(f"OpenAI LLM Stream Request body: {request_body}") + response = self.client.chat.completions.create(**request_body) reasoning_started = False From 74addb247d4bf84ed9a7a4a1dd1f09e532ce895e Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 24 Dec 2025 14:15:03 +0800 Subject: [PATCH 3/6] feat: add openai request body log --- src/memos/api/client.py | 138 ++++++++++++++++++-------------- src/memos/api/product_models.py | 8 +- 2 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/memos/api/client.py b/src/memos/api/client.py index 5fb80b5bd..1129ddddf 100644 --- a/src/memos/api/client.py +++ b/src/memos/api/client.py @@ -10,6 +10,7 @@ MemOSAddFeedBackResponse, MemOSAddKnowledgebaseFileResponse, MemOSAddResponse, + MemOSChatResponse, MemOSCreateKnowledgebaseResponse, MemOSDeleteKnowledgebaseResponse, MemOSDeleteMemoryResponse, @@ -17,11 +18,11 @@ MemOSGetMemoryResponse, MemOSGetMessagesResponse, MemOSGetTaskStatusResponse, - MemOSSearchResponse, MemOSChatResponse, + MemOSSearchResponse, ) - from memos.log import get_logger + logger = get_logger(__name__) MAX_RETRY_COUNT = 3 @@ -32,7 +33,7 @@ class MemOSClient: def __init__(self, api_key: str | None = None, base_url: str | None = None): self.base_url = ( - base_url or os.getenv("MEMOS_BASE_URL") or "https://memos.memtensor.cn/api/openmem/v1" + base_url or os.getenv("MEMOS_BASE_URL") or "https://memos.memtensor.cn/api/openmem/v1" ) api_key = api_key or os.getenv("MEMOS_API_KEY") @@ -48,12 +49,12 @@ def _validate_required_params(self, **params): raise ValueError(f"{param_name} is required") def get_message( - self, - user_id: str, - conversation_id: str | None = None, - conversation_limit_number: int = 6, - message_limit_number: int = 6, - source: str | None = None, + self, + user_id: str, + conversation_id: str | None = None, + conversation_limit_number: int = 6, + message_limit_number: int = 6, + source: str | None = None, ) -> MemOSGetMessagesResponse | None: """Get messages""" # Validate required parameters @@ -82,18 +83,18 @@ def get_message( raise def add_message( - self, - messages: list[dict[str, Any]], - user_id: str, - conversation_id: str, - info: dict[str, Any] | None = None, - source: str | None = None, - app_id: str | None = None, - agent_id: str | None = None, - async_mode: bool = True, - tags: list[str] | None = None, - allow_public: bool = False, - allow_knowledgebase_ids: list[str] | None = None, + self, + messages: list[dict[str, Any]], + user_id: str, + conversation_id: str, + info: dict[str, Any] | None = None, + source: str | None = None, + app_id: str | None = None, + agent_id: str | None = None, + async_mode: bool = True, + tags: list[str] | None = None, + allow_public: bool = False, + allow_knowledgebase_ids: list[str] | None = None, ) -> MemOSAddResponse | None: """Add message""" # Validate required parameters @@ -130,18 +131,18 @@ def add_message( raise def search_memory( - self, - query: str, - user_id: str, - conversation_id: str, - memory_limit_number: int = 6, - include_preference: bool = True, - knowledgebase_ids: list[str] | None = None, - filter: dict[str, Any] | None = None, - source: str | None = None, - include_tool_memory: bool = False, - preference_limit_number: int = 6, - tool_memory_limit_number: int = 6, + self, + query: str, + user_id: str, + conversation_id: str, + memory_limit_number: int = 6, + include_preference: bool = True, + knowledgebase_ids: list[str] | None = None, + filter: dict[str, Any] | None = None, + source: str | None = None, + include_tool_memory: bool = False, + preference_limit_number: int = 6, + tool_memory_limit_number: int = 6, ) -> MemOSSearchResponse | None: """Search memories""" # Validate required parameters @@ -202,7 +203,7 @@ def get_memory(self, user_id: str, include_preference: str) -> MemOSGetMemoryRes raise def create_knowledgebase( - self, knowledgebase_name: str, knowledgebase_description: str + self, knowledgebase_name: str, knowledgebase_description: str ) -> MemOSCreateKnowledgebaseResponse | None: """ Create knowledgebase @@ -234,7 +235,7 @@ def create_knowledgebase( raise def delete_knowledgebase( - self, knowledgebase_id: str + self, knowledgebase_id: str ) -> MemOSDeleteKnowledgebaseResponse | None: """ Delete knowledgebase @@ -262,7 +263,7 @@ def delete_knowledgebase( raise def add_knowledgebase_file_json( - self, knowledgebase_id: str, file: list[dict[str, Any]] + self, knowledgebase_id: str, file: list[dict[str, Any]] ) -> MemOSAddKnowledgebaseFileResponse | None: """ add knowledgebase-file from json @@ -291,7 +292,7 @@ def add_knowledgebase_file_json( raise def add_knowledgebase_file_form( - self, knowledgebase_id: str, files: list[str] + self, knowledgebase_id: str, files: list[str] ) -> MemOSAddKnowledgebaseFileResponse | None: """ add knowledgebase-file from form @@ -328,7 +329,6 @@ def build_file_form_param(file_path): headers=headers, timeout=30, files=[build_file_form_param(file_path) for file_path in files], - ) response.raise_for_status() response_data = response.json() @@ -341,7 +341,7 @@ def build_file_form_param(file_path): raise def delete_knowledgebase_file( - self, file_ids: list[str] + self, file_ids: list[str] ) -> MemOSDeleteKnowledgebaseResponse | None: """ delete knowledgebase-file @@ -369,7 +369,7 @@ def delete_knowledgebase_file( raise def get_knowledgebase_file( - self, file_ids: list[str] + self, file_ids: list[str] ) -> MemOSGetKnowledgebaseFileResponse | None: """ get knowledgebase-file @@ -423,15 +423,15 @@ def get_task_status(self, task_id: str) -> MemOSGetTaskStatusResponse | None: raise def add_feedback( - self, - user_id: str, - conversation_id: str, - feedback_content: str, - agent_id: str | None = None, - app_id: str | None = None, - feedback_time: str | None = None, - allow_public: bool = False, - allow_knowledgebase_ids: list[str] | None = None, + self, + user_id: str, + conversation_id: str, + feedback_content: str, + agent_id: str | None = None, + app_id: str | None = None, + feedback_time: str | None = None, + allow_public: bool = False, + allow_knowledgebase_ids: list[str] | None = None, ) -> MemOSAddFeedBackResponse | None: """Add feedback""" # Validate required parameters @@ -465,7 +465,7 @@ def add_feedback( raise def delete_memory( - self, user_ids: list[str], memory_ids: list[str] + self, user_ids: list[str], memory_ids: list[str] ) -> MemOSDeleteMemoryResponse | None: """delete_memory memories""" # Validate required parameters @@ -492,18 +492,37 @@ def delete_memory( raise def chat( - self, user_id: str, conversation_id: str, query: str, internet_search: bool = False, - force_stop: bool = False, use_mem_os_cube: bool = False, source: str | None = None, - system_prompt: str | None = None, model_name: str | None = None, knowledgebase_ids: list[str] | None = None, - filter: dict[str: Any] | None = None, add_message_on_answer: bool = False, app_id: str | None = None, - agent_id: str | None = None, async_mode: bool = True, tags: list[str] | None = None, - info: dict[str:Any] | None = None, allow_public: bool = False, max_tokens: int = 8192, - temperature: float | None = None, top_p: float | None = None, include_preference: bool = True, - preference_limit_number: int = 6, memory_limit_number: int = 6, + self, + user_id: str, + conversation_id: str, + query: str, + internet_search: bool = False, + force_stop: bool = False, + use_mem_os_cube: bool = False, + source: str | None = None, + system_prompt: str | None = None, + model_name: str | None = None, + knowledgebase_ids: list[str] | None = None, + filter: dict[str:Any] | None = None, + add_message_on_answer: bool = False, + app_id: str | None = None, + agent_id: str | None = None, + async_mode: bool = True, + tags: list[str] | None = None, + info: dict[str:Any] | None = None, + allow_public: bool = False, + max_tokens: int = 8192, + temperature: float | None = None, + top_p: float | None = None, + include_preference: bool = True, + preference_limit_number: int = 6, + memory_limit_number: int = 6, ) -> MemOSChatResponse | None: """chat""" # Validate required parameters - self._validate_required_params(user_id=user_id, conversation_id=conversation_id, query=query) + self._validate_required_params( + user_id=user_id, conversation_id=conversation_id, query=query + ) url = f"{self.base_url}/chat" payload = { @@ -531,7 +550,6 @@ def chat( "include_preference": include_preference, "preference_limit_number": preference_limit_number, "memory_limit_number": memory_limit_number, - } for retry in range(MAX_RETRY_COUNT): diff --git a/src/memos/api/product_models.py b/src/memos/api/product_models.py index ac08c696d..adcb68a96 100644 --- a/src/memos/api/product_models.py +++ b/src/memos/api/product_models.py @@ -874,6 +874,7 @@ class DeleteMessageData(BaseModel): success: bool = Field(..., description="Operation success status") + class ChatMessageData(BaseModel): """Data model for chat Message based on actual API.""" @@ -950,6 +951,7 @@ def success(self) -> bool: """Convenient access to success status.""" return self.data.success + class MemOSChatResponse(BaseModel): """Response model for chat operation based on actual API.""" @@ -968,11 +970,11 @@ class MemOSGetTaskStatusResponse(BaseModel): code: int = Field(..., description="Response status code") message: str = Field(..., description="Response message") - data: list[GetTaskStatusMessageData] = Field(..., description="delete results data") + data: list[GetTaskStatusMessageData] = Field(..., description="Task status data") @property - def data(self) -> list[GetTaskStatusMessageData]: - """Convenient access to task status.""" + def messages(self) -> list[GetTaskStatusMessageData]: + """Convenient access to task status messages.""" return self.data From aef64c2b6afb76b148d2fb634381f1106933179e Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 24 Dec 2025 14:16:52 +0800 Subject: [PATCH 4/6] feat: add openai request body log --- docker/.env.example | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index ac921beb5..85d9080a5 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -167,11 +167,6 @@ OSS_ACCESS_KEY_ID= OSS_ACCESS_KEY_SECRET= OSS_PUBLIC_BASE_URL= -## Logging / external sink -CUSTOM_LOGGER_URL= -CUSTOM_LOGGER_TOKEN= -CUSTOM_LOGGER_WORKERS=2 - ## SDK / external client MEMOS_API_KEY= MEMOS_BASE_URL=https://memos.memtensor.cn/api/openmem/v1 From 05b6c84462788d934c74e461a45cb24117890c16 Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Wed, 24 Dec 2025 15:59:16 +0800 Subject: [PATCH 5/6] feat: update openapi.json --- docs/openapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/openapi.json b/docs/openapi.json index ee2ff1368..da1879089 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -884,7 +884,7 @@ "type": "string", "title": "Session Id", "description": "Session ID for the MOS. This is used to distinguish between different dialogue", - "default": "41bb5e18-252d-4948-918c-07d82aa47086" + "default": "9863a3fb-5fc0-4c43-979f-a58b0318cfa9" }, "chat_model": { "$ref": "#/components/schemas/LLMConfigFactory", From 01deb1f5f12b6c9e8c623ac41ec62659c7c5e05d Mon Sep 17 00:00:00 2001 From: harvey_xiang Date: Thu, 25 Dec 2025 17:00:41 +0800 Subject: [PATCH 6/6] feat: add timer status error log --- src/memos/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/memos/utils.py b/src/memos/utils.py index b57967db0..4f2666efd 100644 --- a/src/memos/utils.py +++ b/src/memos/utils.py @@ -1,5 +1,6 @@ import functools import time +import traceback from memos.log import get_logger @@ -35,6 +36,7 @@ def decorator(fn): def wrapper(*args, **kwargs): start = time.perf_counter() exc_type = None + exc_message = None result = None success_flag = False @@ -44,6 +46,7 @@ def wrapper(*args, **kwargs): return result except Exception as e: exc_type = type(e) + exc_message = traceback.format_exc() success_flag = False if fallback is not None and callable(fallback): @@ -78,7 +81,9 @@ def wrapper(*args, **kwargs): status_info = f", status: {status}" if not success_flag and exc_type is not None: - status_info += f", error: {exc_type.__name__}" + status_info += ( + f", error_type: {exc_type.__name__}, error_message: {exc_message}" + ) msg = ( f"[TIMER_WITH_STATUS] {log_prefix or fn.__name__} "