From c239d8a483120143707c42e459b48fb3c7261afb Mon Sep 17 00:00:00 2001 From: Asish Kumar <87874775+officialasishkumar@users.noreply.github.com> Date: Sun, 12 Apr 2026 03:06:05 +0530 Subject: [PATCH] fix(client): prevent feedback telemetry TypeError (#4795) --- mem0/client/main.py | 4 +-- tests/test_client_feedback.py | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/test_client_feedback.py diff --git a/mem0/client/main.py b/mem0/client/main.py index 6cf74e412..3c30e10a6 100644 --- a/mem0/client/main.py +++ b/mem0/client/main.py @@ -873,7 +873,7 @@ class MemoryClient: response = self.client.post("/v1/feedback/", json=data) response.raise_for_status() - capture_client_event("client.feedback", self, data, {"sync_type": "sync"}) + capture_client_event("client.feedback", self, {**data, "sync_type": "sync"}) return response.json() def _prepare_payload(self, messages: List[Dict[str, str]], kwargs: Dict[str, Any]) -> Dict[str, Any]: @@ -1743,5 +1743,5 @@ class AsyncMemoryClient: response = await self.async_client.post("/v1/feedback/", json=data) response.raise_for_status() - capture_client_event("client.feedback", self, data, {"sync_type": "async"}) + capture_client_event("client.feedback", self, {**data, "sync_type": "async"}) return response.json() diff --git a/tests/test_client_feedback.py b/tests/test_client_feedback.py new file mode 100644 index 000000000..7f72983e7 --- /dev/null +++ b/tests/test_client_feedback.py @@ -0,0 +1,59 @@ +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from mem0.client.main import AsyncMemoryClient, MemoryClient + + +def _build_memory_client(response_payload=None): + client = MemoryClient.__new__(MemoryClient) + client.user_email = "user@example.com" + client.client = MagicMock() + response = client.client.post.return_value + response.json.return_value = response_payload or {"message": "Feedback recorded"} + response.raise_for_status.return_value = None + return client + + +def test_memory_client_feedback_uses_single_telemetry_payload(): + client = _build_memory_client() + + with patch("mem0.client.main.capture_client_event") as mock_capture: + result = client.feedback("mem_1", feedback="positive", feedback_reason="accurate") + + assert result == {"message": "Feedback recorded"} + client.client.post.assert_called_once_with( + "/v1/feedback/", + json={"memory_id": "mem_1", "feedback": "POSITIVE", "feedback_reason": "accurate"}, + ) + mock_capture.assert_called_once_with( + "client.feedback", + client, + {"memory_id": "mem_1", "feedback": "POSITIVE", "feedback_reason": "accurate", "sync_type": "sync"}, + ) + + +@pytest.mark.asyncio +async def test_async_memory_client_feedback_uses_single_telemetry_payload(): + client = AsyncMemoryClient.__new__(AsyncMemoryClient) + client.user_email = "user@example.com" + client.async_client = MagicMock() + + response = MagicMock() + response.json.return_value = {"message": "Feedback recorded"} + response.raise_for_status.return_value = None + client.async_client.post = AsyncMock(return_value=response) + + with patch("mem0.client.main.capture_client_event") as mock_capture: + result = await client.feedback("mem_1", feedback="negative", feedback_reason="outdated") + + assert result == {"message": "Feedback recorded"} + client.async_client.post.assert_awaited_once_with( + "/v1/feedback/", + json={"memory_id": "mem_1", "feedback": "NEGATIVE", "feedback_reason": "outdated"}, + ) + mock_capture.assert_called_once_with( + "client.feedback", + client, + {"memory_id": "mem_1", "feedback": "NEGATIVE", "feedback_reason": "outdated", "sync_type": "async"}, + )