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 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 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