agentrun-mem0ai 0.0.11__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.
- agentrun_mem0/__init__.py +6 -0
- agentrun_mem0/client/__init__.py +0 -0
- agentrun_mem0/client/main.py +1747 -0
- agentrun_mem0/client/project.py +931 -0
- agentrun_mem0/client/utils.py +115 -0
- agentrun_mem0/configs/__init__.py +0 -0
- agentrun_mem0/configs/base.py +90 -0
- agentrun_mem0/configs/embeddings/__init__.py +0 -0
- agentrun_mem0/configs/embeddings/base.py +110 -0
- agentrun_mem0/configs/enums.py +7 -0
- agentrun_mem0/configs/llms/__init__.py +0 -0
- agentrun_mem0/configs/llms/anthropic.py +56 -0
- agentrun_mem0/configs/llms/aws_bedrock.py +192 -0
- agentrun_mem0/configs/llms/azure.py +57 -0
- agentrun_mem0/configs/llms/base.py +62 -0
- agentrun_mem0/configs/llms/deepseek.py +56 -0
- agentrun_mem0/configs/llms/lmstudio.py +59 -0
- agentrun_mem0/configs/llms/ollama.py +56 -0
- agentrun_mem0/configs/llms/openai.py +79 -0
- agentrun_mem0/configs/llms/vllm.py +56 -0
- agentrun_mem0/configs/prompts.py +459 -0
- agentrun_mem0/configs/rerankers/__init__.py +0 -0
- agentrun_mem0/configs/rerankers/base.py +17 -0
- agentrun_mem0/configs/rerankers/cohere.py +15 -0
- agentrun_mem0/configs/rerankers/config.py +12 -0
- agentrun_mem0/configs/rerankers/huggingface.py +17 -0
- agentrun_mem0/configs/rerankers/llm.py +48 -0
- agentrun_mem0/configs/rerankers/sentence_transformer.py +16 -0
- agentrun_mem0/configs/rerankers/zero_entropy.py +28 -0
- agentrun_mem0/configs/vector_stores/__init__.py +0 -0
- agentrun_mem0/configs/vector_stores/alibabacloud_mysql.py +64 -0
- agentrun_mem0/configs/vector_stores/aliyun_tablestore.py +32 -0
- agentrun_mem0/configs/vector_stores/azure_ai_search.py +57 -0
- agentrun_mem0/configs/vector_stores/azure_mysql.py +84 -0
- agentrun_mem0/configs/vector_stores/baidu.py +27 -0
- agentrun_mem0/configs/vector_stores/chroma.py +58 -0
- agentrun_mem0/configs/vector_stores/databricks.py +61 -0
- agentrun_mem0/configs/vector_stores/elasticsearch.py +65 -0
- agentrun_mem0/configs/vector_stores/faiss.py +37 -0
- agentrun_mem0/configs/vector_stores/langchain.py +30 -0
- agentrun_mem0/configs/vector_stores/milvus.py +42 -0
- agentrun_mem0/configs/vector_stores/mongodb.py +25 -0
- agentrun_mem0/configs/vector_stores/neptune.py +27 -0
- agentrun_mem0/configs/vector_stores/opensearch.py +41 -0
- agentrun_mem0/configs/vector_stores/pgvector.py +52 -0
- agentrun_mem0/configs/vector_stores/pinecone.py +55 -0
- agentrun_mem0/configs/vector_stores/qdrant.py +47 -0
- agentrun_mem0/configs/vector_stores/redis.py +24 -0
- agentrun_mem0/configs/vector_stores/s3_vectors.py +28 -0
- agentrun_mem0/configs/vector_stores/supabase.py +44 -0
- agentrun_mem0/configs/vector_stores/upstash_vector.py +34 -0
- agentrun_mem0/configs/vector_stores/valkey.py +15 -0
- agentrun_mem0/configs/vector_stores/vertex_ai_vector_search.py +28 -0
- agentrun_mem0/configs/vector_stores/weaviate.py +41 -0
- agentrun_mem0/embeddings/__init__.py +0 -0
- agentrun_mem0/embeddings/aws_bedrock.py +100 -0
- agentrun_mem0/embeddings/azure_openai.py +55 -0
- agentrun_mem0/embeddings/base.py +31 -0
- agentrun_mem0/embeddings/configs.py +30 -0
- agentrun_mem0/embeddings/gemini.py +39 -0
- agentrun_mem0/embeddings/huggingface.py +44 -0
- agentrun_mem0/embeddings/langchain.py +35 -0
- agentrun_mem0/embeddings/lmstudio.py +29 -0
- agentrun_mem0/embeddings/mock.py +11 -0
- agentrun_mem0/embeddings/ollama.py +53 -0
- agentrun_mem0/embeddings/openai.py +49 -0
- agentrun_mem0/embeddings/together.py +31 -0
- agentrun_mem0/embeddings/vertexai.py +64 -0
- agentrun_mem0/exceptions.py +503 -0
- agentrun_mem0/graphs/__init__.py +0 -0
- agentrun_mem0/graphs/configs.py +105 -0
- agentrun_mem0/graphs/neptune/__init__.py +0 -0
- agentrun_mem0/graphs/neptune/base.py +497 -0
- agentrun_mem0/graphs/neptune/neptunedb.py +511 -0
- agentrun_mem0/graphs/neptune/neptunegraph.py +474 -0
- agentrun_mem0/graphs/tools.py +371 -0
- agentrun_mem0/graphs/utils.py +97 -0
- agentrun_mem0/llms/__init__.py +0 -0
- agentrun_mem0/llms/anthropic.py +87 -0
- agentrun_mem0/llms/aws_bedrock.py +665 -0
- agentrun_mem0/llms/azure_openai.py +141 -0
- agentrun_mem0/llms/azure_openai_structured.py +91 -0
- agentrun_mem0/llms/base.py +131 -0
- agentrun_mem0/llms/configs.py +34 -0
- agentrun_mem0/llms/deepseek.py +107 -0
- agentrun_mem0/llms/gemini.py +201 -0
- agentrun_mem0/llms/groq.py +88 -0
- agentrun_mem0/llms/langchain.py +94 -0
- agentrun_mem0/llms/litellm.py +87 -0
- agentrun_mem0/llms/lmstudio.py +114 -0
- agentrun_mem0/llms/ollama.py +117 -0
- agentrun_mem0/llms/openai.py +147 -0
- agentrun_mem0/llms/openai_structured.py +52 -0
- agentrun_mem0/llms/sarvam.py +89 -0
- agentrun_mem0/llms/together.py +88 -0
- agentrun_mem0/llms/vllm.py +107 -0
- agentrun_mem0/llms/xai.py +52 -0
- agentrun_mem0/memory/__init__.py +0 -0
- agentrun_mem0/memory/base.py +63 -0
- agentrun_mem0/memory/graph_memory.py +698 -0
- agentrun_mem0/memory/kuzu_memory.py +713 -0
- agentrun_mem0/memory/main.py +2229 -0
- agentrun_mem0/memory/memgraph_memory.py +689 -0
- agentrun_mem0/memory/setup.py +56 -0
- agentrun_mem0/memory/storage.py +218 -0
- agentrun_mem0/memory/telemetry.py +90 -0
- agentrun_mem0/memory/utils.py +208 -0
- agentrun_mem0/proxy/__init__.py +0 -0
- agentrun_mem0/proxy/main.py +189 -0
- agentrun_mem0/reranker/__init__.py +9 -0
- agentrun_mem0/reranker/base.py +20 -0
- agentrun_mem0/reranker/cohere_reranker.py +85 -0
- agentrun_mem0/reranker/huggingface_reranker.py +147 -0
- agentrun_mem0/reranker/llm_reranker.py +142 -0
- agentrun_mem0/reranker/sentence_transformer_reranker.py +107 -0
- agentrun_mem0/reranker/zero_entropy_reranker.py +96 -0
- agentrun_mem0/utils/factory.py +283 -0
- agentrun_mem0/utils/gcp_auth.py +167 -0
- agentrun_mem0/vector_stores/__init__.py +0 -0
- agentrun_mem0/vector_stores/alibabacloud_mysql.py +547 -0
- agentrun_mem0/vector_stores/aliyun_tablestore.py +252 -0
- agentrun_mem0/vector_stores/azure_ai_search.py +396 -0
- agentrun_mem0/vector_stores/azure_mysql.py +463 -0
- agentrun_mem0/vector_stores/baidu.py +368 -0
- agentrun_mem0/vector_stores/base.py +58 -0
- agentrun_mem0/vector_stores/chroma.py +332 -0
- agentrun_mem0/vector_stores/configs.py +67 -0
- agentrun_mem0/vector_stores/databricks.py +761 -0
- agentrun_mem0/vector_stores/elasticsearch.py +237 -0
- agentrun_mem0/vector_stores/faiss.py +479 -0
- agentrun_mem0/vector_stores/langchain.py +180 -0
- agentrun_mem0/vector_stores/milvus.py +250 -0
- agentrun_mem0/vector_stores/mongodb.py +310 -0
- agentrun_mem0/vector_stores/neptune_analytics.py +467 -0
- agentrun_mem0/vector_stores/opensearch.py +292 -0
- agentrun_mem0/vector_stores/pgvector.py +404 -0
- agentrun_mem0/vector_stores/pinecone.py +382 -0
- agentrun_mem0/vector_stores/qdrant.py +270 -0
- agentrun_mem0/vector_stores/redis.py +295 -0
- agentrun_mem0/vector_stores/s3_vectors.py +176 -0
- agentrun_mem0/vector_stores/supabase.py +237 -0
- agentrun_mem0/vector_stores/upstash_vector.py +293 -0
- agentrun_mem0/vector_stores/valkey.py +824 -0
- agentrun_mem0/vector_stores/vertex_ai_vector_search.py +635 -0
- agentrun_mem0/vector_stores/weaviate.py +343 -0
- agentrun_mem0ai-0.0.11.data/data/README.md +205 -0
- agentrun_mem0ai-0.0.11.dist-info/METADATA +277 -0
- agentrun_mem0ai-0.0.11.dist-info/RECORD +150 -0
- agentrun_mem0ai-0.0.11.dist-info/WHEEL +4 -0
- agentrun_mem0ai-0.0.11.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,1747 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from agentrun_mem0.client.project import AsyncProject, Project
|
|
11
|
+
from agentrun_mem0.client.utils import api_error_handler
|
|
12
|
+
# Exception classes are referenced in docstrings only
|
|
13
|
+
from agentrun_mem0.memory.setup import get_user_id, setup_config
|
|
14
|
+
from agentrun_mem0.memory.telemetry import capture_client_event
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
warnings.filterwarnings("default", category=DeprecationWarning)
|
|
19
|
+
|
|
20
|
+
# Setup user config
|
|
21
|
+
setup_config()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MemoryClient:
|
|
25
|
+
"""Client for interacting with the Mem0 API.
|
|
26
|
+
|
|
27
|
+
This class provides methods to create, retrieve, search, and delete
|
|
28
|
+
memories using the Mem0 API.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
api_key (str): The API key for authenticating with the Mem0 API.
|
|
32
|
+
host (str): The base URL for the Mem0 API.
|
|
33
|
+
client (httpx.Client): The HTTP client used for making API requests.
|
|
34
|
+
org_id (str, optional): Organization ID.
|
|
35
|
+
project_id (str, optional): Project ID.
|
|
36
|
+
user_id (str): Unique identifier for the user.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
api_key: Optional[str] = None,
|
|
42
|
+
host: Optional[str] = None,
|
|
43
|
+
org_id: Optional[str] = None,
|
|
44
|
+
project_id: Optional[str] = None,
|
|
45
|
+
client: Optional[httpx.Client] = None,
|
|
46
|
+
):
|
|
47
|
+
"""Initialize the MemoryClient.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
api_key: The API key for authenticating with the Mem0 API. If not
|
|
51
|
+
provided, it will attempt to use the MEM0_API_KEY
|
|
52
|
+
environment variable.
|
|
53
|
+
host: The base URL for the Mem0 API. Defaults to
|
|
54
|
+
"https://api.mem0.ai".
|
|
55
|
+
org_id: The ID of the organization.
|
|
56
|
+
project_id: The ID of the project.
|
|
57
|
+
client: A custom httpx.Client instance. If provided, it will be
|
|
58
|
+
used instead of creating a new one. Note that base_url and
|
|
59
|
+
headers will be set/overridden as needed.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If no API key is provided or found in the environment.
|
|
63
|
+
"""
|
|
64
|
+
self.api_key = api_key or os.getenv("MEM0_API_KEY")
|
|
65
|
+
self.host = host or "https://api.mem0.ai"
|
|
66
|
+
self.org_id = org_id
|
|
67
|
+
self.project_id = project_id
|
|
68
|
+
self.user_id = get_user_id()
|
|
69
|
+
|
|
70
|
+
if not self.api_key:
|
|
71
|
+
raise ValueError("Mem0 API Key not provided. Please provide an API Key.")
|
|
72
|
+
|
|
73
|
+
# Create MD5 hash of API key for user_id
|
|
74
|
+
self.user_id = hashlib.md5(self.api_key.encode()).hexdigest()
|
|
75
|
+
|
|
76
|
+
if client is not None:
|
|
77
|
+
self.client = client
|
|
78
|
+
# Ensure the client has the correct base_url and headers
|
|
79
|
+
self.client.base_url = httpx.URL(self.host)
|
|
80
|
+
self.client.headers.update(
|
|
81
|
+
{
|
|
82
|
+
"Authorization": f"Token {self.api_key}",
|
|
83
|
+
"Mem0-User-ID": self.user_id,
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
self.client = httpx.Client(
|
|
88
|
+
base_url=self.host,
|
|
89
|
+
headers={
|
|
90
|
+
"Authorization": f"Token {self.api_key}",
|
|
91
|
+
"Mem0-User-ID": self.user_id,
|
|
92
|
+
},
|
|
93
|
+
timeout=300,
|
|
94
|
+
)
|
|
95
|
+
self.user_email = self._validate_api_key()
|
|
96
|
+
|
|
97
|
+
# Initialize project manager
|
|
98
|
+
self.project = Project(
|
|
99
|
+
client=self.client,
|
|
100
|
+
org_id=self.org_id,
|
|
101
|
+
project_id=self.project_id,
|
|
102
|
+
user_email=self.user_email,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
capture_client_event("client.init", self, {"sync_type": "sync"})
|
|
106
|
+
|
|
107
|
+
def _validate_api_key(self):
|
|
108
|
+
"""Validate the API key by making a test request."""
|
|
109
|
+
try:
|
|
110
|
+
params = self._prepare_params()
|
|
111
|
+
response = self.client.get("/v1/ping/", params=params)
|
|
112
|
+
data = response.json()
|
|
113
|
+
|
|
114
|
+
response.raise_for_status()
|
|
115
|
+
|
|
116
|
+
if data.get("org_id") and data.get("project_id"):
|
|
117
|
+
self.org_id = data.get("org_id")
|
|
118
|
+
self.project_id = data.get("project_id")
|
|
119
|
+
|
|
120
|
+
return data.get("user_email")
|
|
121
|
+
|
|
122
|
+
except httpx.HTTPStatusError as e:
|
|
123
|
+
try:
|
|
124
|
+
error_data = e.response.json()
|
|
125
|
+
error_message = error_data.get("detail", str(e))
|
|
126
|
+
except Exception:
|
|
127
|
+
error_message = str(e)
|
|
128
|
+
raise ValueError(f"Error: {error_message}")
|
|
129
|
+
|
|
130
|
+
@api_error_handler
|
|
131
|
+
def add(self, messages, **kwargs) -> Dict[str, Any]:
|
|
132
|
+
"""Add a new memory.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
messages: A list of message dictionaries, a single message dictionary,
|
|
136
|
+
or a string. If a string is provided, it will be converted to
|
|
137
|
+
a user message.
|
|
138
|
+
**kwargs: Additional parameters such as user_id, agent_id, app_id,
|
|
139
|
+
metadata, filters, async_mode.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
A dictionary containing the API response in v1.1 format.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
ValidationError: If the input data is invalid.
|
|
146
|
+
AuthenticationError: If authentication fails.
|
|
147
|
+
RateLimitError: If rate limits are exceeded.
|
|
148
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
149
|
+
NetworkError: If network connectivity issues occur.
|
|
150
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
151
|
+
"""
|
|
152
|
+
# Handle different message input formats (align with OSS behavior)
|
|
153
|
+
if isinstance(messages, str):
|
|
154
|
+
messages = [{"role": "user", "content": messages}]
|
|
155
|
+
elif isinstance(messages, dict):
|
|
156
|
+
messages = [messages]
|
|
157
|
+
elif not isinstance(messages, list):
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"messages must be str, dict, or list[dict], got {type(messages).__name__}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
kwargs = self._prepare_params(kwargs)
|
|
163
|
+
|
|
164
|
+
# Remove deprecated parameters
|
|
165
|
+
if "output_format" in kwargs:
|
|
166
|
+
warnings.warn(
|
|
167
|
+
"output_format parameter is deprecated and ignored. All responses now use v1.1 format.",
|
|
168
|
+
DeprecationWarning,
|
|
169
|
+
stacklevel=2,
|
|
170
|
+
)
|
|
171
|
+
kwargs.pop("output_format")
|
|
172
|
+
|
|
173
|
+
# Set async_mode to True by default, but allow user override
|
|
174
|
+
if "async_mode" not in kwargs:
|
|
175
|
+
kwargs["async_mode"] = True
|
|
176
|
+
|
|
177
|
+
# Force v1.1 format for all add operations
|
|
178
|
+
kwargs["output_format"] = "v1.1"
|
|
179
|
+
payload = self._prepare_payload(messages, kwargs)
|
|
180
|
+
response = self.client.post("/v1/memories/", json=payload)
|
|
181
|
+
response.raise_for_status()
|
|
182
|
+
if "metadata" in kwargs:
|
|
183
|
+
del kwargs["metadata"]
|
|
184
|
+
capture_client_event("client.add", self, {"keys": list(kwargs.keys()), "sync_type": "sync"})
|
|
185
|
+
return response.json()
|
|
186
|
+
|
|
187
|
+
@api_error_handler
|
|
188
|
+
def get(self, memory_id: str) -> Dict[str, Any]:
|
|
189
|
+
"""Retrieve a specific memory by ID.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
memory_id: The ID of the memory to retrieve.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
A dictionary containing the memory data.
|
|
196
|
+
|
|
197
|
+
Raises:
|
|
198
|
+
ValidationError: If the input data is invalid.
|
|
199
|
+
AuthenticationError: If authentication fails.
|
|
200
|
+
RateLimitError: If rate limits are exceeded.
|
|
201
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
202
|
+
NetworkError: If network connectivity issues occur.
|
|
203
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
204
|
+
"""
|
|
205
|
+
params = self._prepare_params()
|
|
206
|
+
response = self.client.get(f"/v1/memories/{memory_id}/", params=params)
|
|
207
|
+
response.raise_for_status()
|
|
208
|
+
capture_client_event("client.get", self, {"memory_id": memory_id, "sync_type": "sync"})
|
|
209
|
+
return response.json()
|
|
210
|
+
|
|
211
|
+
@api_error_handler
|
|
212
|
+
def get_all(self, **kwargs) -> Dict[str, Any]:
|
|
213
|
+
"""Retrieve all memories, with optional filtering.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
**kwargs: Optional parameters for filtering (user_id, agent_id,
|
|
217
|
+
app_id, top_k, page, page_size).
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
A dictionary containing memories in v1.1 format: {"results": [...]}
|
|
221
|
+
|
|
222
|
+
Raises:
|
|
223
|
+
ValidationError: If the input data is invalid.
|
|
224
|
+
AuthenticationError: If authentication fails.
|
|
225
|
+
RateLimitError: If rate limits are exceeded.
|
|
226
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
227
|
+
NetworkError: If network connectivity issues occur.
|
|
228
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
229
|
+
"""
|
|
230
|
+
params = self._prepare_params(kwargs)
|
|
231
|
+
params.pop("output_format", None) # Remove output_format for get operations
|
|
232
|
+
params.pop("async_mode", None)
|
|
233
|
+
|
|
234
|
+
if "page" in params and "page_size" in params:
|
|
235
|
+
query_params = {
|
|
236
|
+
"page": params.pop("page"),
|
|
237
|
+
"page_size": params.pop("page_size"),
|
|
238
|
+
}
|
|
239
|
+
response = self.client.post("/v2/memories/", json=params, params=query_params)
|
|
240
|
+
else:
|
|
241
|
+
response = self.client.post("/v2/memories/", json=params)
|
|
242
|
+
response.raise_for_status()
|
|
243
|
+
if "metadata" in kwargs:
|
|
244
|
+
del kwargs["metadata"]
|
|
245
|
+
capture_client_event(
|
|
246
|
+
"client.get_all",
|
|
247
|
+
self,
|
|
248
|
+
{
|
|
249
|
+
"api_version": "v2",
|
|
250
|
+
"keys": list(kwargs.keys()),
|
|
251
|
+
"sync_type": "sync",
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
result = response.json()
|
|
255
|
+
|
|
256
|
+
# Ensure v1.1 format (wrap raw list if needed)
|
|
257
|
+
if isinstance(result, list):
|
|
258
|
+
return {"results": result}
|
|
259
|
+
return result
|
|
260
|
+
|
|
261
|
+
@api_error_handler
|
|
262
|
+
def search(self, query: str, **kwargs) -> Dict[str, Any]:
|
|
263
|
+
"""Search memories based on a query.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
query: The search query string.
|
|
267
|
+
**kwargs: Additional parameters such as user_id, agent_id, app_id,
|
|
268
|
+
top_k, filters.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
A dictionary containing search results in v1.1 format: {"results": [...]}
|
|
272
|
+
|
|
273
|
+
Raises:
|
|
274
|
+
ValidationError: If the input data is invalid.
|
|
275
|
+
AuthenticationError: If authentication fails.
|
|
276
|
+
RateLimitError: If rate limits are exceeded.
|
|
277
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
278
|
+
NetworkError: If network connectivity issues occur.
|
|
279
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
280
|
+
"""
|
|
281
|
+
payload = {"query": query}
|
|
282
|
+
params = self._prepare_params(kwargs)
|
|
283
|
+
params.pop("output_format", None) # Remove output_format for search operations
|
|
284
|
+
params.pop("async_mode", None)
|
|
285
|
+
|
|
286
|
+
payload.update(params)
|
|
287
|
+
|
|
288
|
+
response = self.client.post("/v2/memories/search/", json=payload)
|
|
289
|
+
response.raise_for_status()
|
|
290
|
+
if "metadata" in kwargs:
|
|
291
|
+
del kwargs["metadata"]
|
|
292
|
+
capture_client_event(
|
|
293
|
+
"client.search",
|
|
294
|
+
self,
|
|
295
|
+
{
|
|
296
|
+
"api_version": "v2",
|
|
297
|
+
"keys": list(kwargs.keys()),
|
|
298
|
+
"sync_type": "sync",
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
result = response.json()
|
|
302
|
+
|
|
303
|
+
# Ensure v1.1 format (wrap raw list if needed)
|
|
304
|
+
if isinstance(result, list):
|
|
305
|
+
return {"results": result}
|
|
306
|
+
return result
|
|
307
|
+
|
|
308
|
+
@api_error_handler
|
|
309
|
+
def update(
|
|
310
|
+
self,
|
|
311
|
+
memory_id: str,
|
|
312
|
+
text: Optional[str] = None,
|
|
313
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
314
|
+
) -> Dict[str, Any]:
|
|
315
|
+
"""
|
|
316
|
+
Update a memory by ID.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
memory_id (str): Memory ID.
|
|
320
|
+
text (str, optional): New content to update the memory with.
|
|
321
|
+
metadata (dict, optional): Metadata to update in the memory.
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Dict[str, Any]: The response from the server.
|
|
325
|
+
|
|
326
|
+
Example:
|
|
327
|
+
>>> client.update(memory_id="mem_123", text="Likes to play tennis on weekends")
|
|
328
|
+
"""
|
|
329
|
+
if text is None and metadata is None:
|
|
330
|
+
raise ValueError("Either text or metadata must be provided for update.")
|
|
331
|
+
|
|
332
|
+
payload = {}
|
|
333
|
+
if text is not None:
|
|
334
|
+
payload["text"] = text
|
|
335
|
+
if metadata is not None:
|
|
336
|
+
payload["metadata"] = metadata
|
|
337
|
+
|
|
338
|
+
capture_client_event("client.update", self, {"memory_id": memory_id, "sync_type": "sync"})
|
|
339
|
+
params = self._prepare_params()
|
|
340
|
+
response = self.client.put(f"/v1/memories/{memory_id}/", json=payload, params=params)
|
|
341
|
+
response.raise_for_status()
|
|
342
|
+
return response.json()
|
|
343
|
+
|
|
344
|
+
@api_error_handler
|
|
345
|
+
def delete(self, memory_id: str) -> Dict[str, Any]:
|
|
346
|
+
"""Delete a specific memory by ID.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
memory_id: The ID of the memory to delete.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
A dictionary containing the API response.
|
|
353
|
+
|
|
354
|
+
Raises:
|
|
355
|
+
ValidationError: If the input data is invalid.
|
|
356
|
+
AuthenticationError: If authentication fails.
|
|
357
|
+
RateLimitError: If rate limits are exceeded.
|
|
358
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
359
|
+
NetworkError: If network connectivity issues occur.
|
|
360
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
361
|
+
"""
|
|
362
|
+
params = self._prepare_params()
|
|
363
|
+
response = self.client.delete(f"/v1/memories/{memory_id}/", params=params)
|
|
364
|
+
response.raise_for_status()
|
|
365
|
+
capture_client_event("client.delete", self, {"memory_id": memory_id, "sync_type": "sync"})
|
|
366
|
+
return response.json()
|
|
367
|
+
|
|
368
|
+
@api_error_handler
|
|
369
|
+
def delete_all(self, **kwargs) -> Dict[str, str]:
|
|
370
|
+
"""Delete all memories, with optional filtering.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
**kwargs: Optional parameters for filtering (user_id, agent_id,
|
|
374
|
+
app_id).
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
A dictionary containing the API response.
|
|
378
|
+
|
|
379
|
+
Raises:
|
|
380
|
+
ValidationError: If the input data is invalid.
|
|
381
|
+
AuthenticationError: If authentication fails.
|
|
382
|
+
RateLimitError: If rate limits are exceeded.
|
|
383
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
384
|
+
NetworkError: If network connectivity issues occur.
|
|
385
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
386
|
+
"""
|
|
387
|
+
params = self._prepare_params(kwargs)
|
|
388
|
+
response = self.client.delete("/v1/memories/", params=params)
|
|
389
|
+
response.raise_for_status()
|
|
390
|
+
capture_client_event(
|
|
391
|
+
"client.delete_all",
|
|
392
|
+
self,
|
|
393
|
+
{"keys": list(kwargs.keys()), "sync_type": "sync"},
|
|
394
|
+
)
|
|
395
|
+
return response.json()
|
|
396
|
+
|
|
397
|
+
@api_error_handler
|
|
398
|
+
def history(self, memory_id: str) -> List[Dict[str, Any]]:
|
|
399
|
+
"""Retrieve the history of a specific memory.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
memory_id: The ID of the memory to retrieve history for.
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
A list of dictionaries containing the memory history.
|
|
406
|
+
|
|
407
|
+
Raises:
|
|
408
|
+
ValidationError: If the input data is invalid.
|
|
409
|
+
AuthenticationError: If authentication fails.
|
|
410
|
+
RateLimitError: If rate limits are exceeded.
|
|
411
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
412
|
+
NetworkError: If network connectivity issues occur.
|
|
413
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
414
|
+
"""
|
|
415
|
+
params = self._prepare_params()
|
|
416
|
+
response = self.client.get(f"/v1/memories/{memory_id}/history/", params=params)
|
|
417
|
+
response.raise_for_status()
|
|
418
|
+
capture_client_event("client.history", self, {"memory_id": memory_id, "sync_type": "sync"})
|
|
419
|
+
return response.json()
|
|
420
|
+
|
|
421
|
+
@api_error_handler
|
|
422
|
+
def users(self) -> Dict[str, Any]:
|
|
423
|
+
"""Get all users, agents, and sessions for which memories exist."""
|
|
424
|
+
params = self._prepare_params()
|
|
425
|
+
response = self.client.get("/v1/entities/", params=params)
|
|
426
|
+
response.raise_for_status()
|
|
427
|
+
capture_client_event("client.users", self, {"sync_type": "sync"})
|
|
428
|
+
return response.json()
|
|
429
|
+
|
|
430
|
+
@api_error_handler
|
|
431
|
+
def delete_users(
|
|
432
|
+
self,
|
|
433
|
+
user_id: Optional[str] = None,
|
|
434
|
+
agent_id: Optional[str] = None,
|
|
435
|
+
app_id: Optional[str] = None,
|
|
436
|
+
run_id: Optional[str] = None,
|
|
437
|
+
) -> Dict[str, str]:
|
|
438
|
+
"""Delete specific entities or all entities if no filters provided.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
user_id: Optional user ID to delete specific user
|
|
442
|
+
agent_id: Optional agent ID to delete specific agent
|
|
443
|
+
app_id: Optional app ID to delete specific app
|
|
444
|
+
run_id: Optional run ID to delete specific run
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
Dict with success message
|
|
448
|
+
|
|
449
|
+
Raises:
|
|
450
|
+
ValueError: If specified entity not found
|
|
451
|
+
ValidationError: If the input data is invalid.
|
|
452
|
+
AuthenticationError: If authentication fails.
|
|
453
|
+
MemoryNotFoundError: If the entity doesn't exist.
|
|
454
|
+
NetworkError: If network connectivity issues occur.
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
if user_id:
|
|
458
|
+
to_delete = [{"type": "user", "name": user_id}]
|
|
459
|
+
elif agent_id:
|
|
460
|
+
to_delete = [{"type": "agent", "name": agent_id}]
|
|
461
|
+
elif app_id:
|
|
462
|
+
to_delete = [{"type": "app", "name": app_id}]
|
|
463
|
+
elif run_id:
|
|
464
|
+
to_delete = [{"type": "run", "name": run_id}]
|
|
465
|
+
else:
|
|
466
|
+
entities = self.users()
|
|
467
|
+
# Filter entities based on provided IDs using list comprehension
|
|
468
|
+
to_delete = [{"type": entity["type"], "name": entity["name"]} for entity in entities["results"]]
|
|
469
|
+
|
|
470
|
+
params = self._prepare_params()
|
|
471
|
+
|
|
472
|
+
if not to_delete:
|
|
473
|
+
raise ValueError("No entities to delete")
|
|
474
|
+
|
|
475
|
+
# Delete entities and check response immediately
|
|
476
|
+
for entity in to_delete:
|
|
477
|
+
response = self.client.delete(f"/v2/entities/{entity['type']}/{entity['name']}/", params=params)
|
|
478
|
+
response.raise_for_status()
|
|
479
|
+
|
|
480
|
+
capture_client_event(
|
|
481
|
+
"client.delete_users",
|
|
482
|
+
self,
|
|
483
|
+
{
|
|
484
|
+
"user_id": user_id,
|
|
485
|
+
"agent_id": agent_id,
|
|
486
|
+
"app_id": app_id,
|
|
487
|
+
"run_id": run_id,
|
|
488
|
+
"sync_type": "sync",
|
|
489
|
+
},
|
|
490
|
+
)
|
|
491
|
+
return {
|
|
492
|
+
"message": "Entity deleted successfully."
|
|
493
|
+
if (user_id or agent_id or app_id or run_id)
|
|
494
|
+
else "All users, agents, apps and runs deleted."
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
@api_error_handler
|
|
498
|
+
def reset(self) -> Dict[str, str]:
|
|
499
|
+
"""Reset the client by deleting all users and memories.
|
|
500
|
+
|
|
501
|
+
This method deletes all users, agents, sessions, and memories
|
|
502
|
+
associated with the client.
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
Dict[str, str]: Message client reset successful.
|
|
506
|
+
|
|
507
|
+
Raises:
|
|
508
|
+
ValidationError: If the input data is invalid.
|
|
509
|
+
AuthenticationError: If authentication fails.
|
|
510
|
+
RateLimitError: If rate limits are exceeded.
|
|
511
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
512
|
+
NetworkError: If network connectivity issues occur.
|
|
513
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
514
|
+
"""
|
|
515
|
+
self.delete_users()
|
|
516
|
+
|
|
517
|
+
capture_client_event("client.reset", self, {"sync_type": "sync"})
|
|
518
|
+
return {"message": "Client reset successful. All users and memories deleted."}
|
|
519
|
+
|
|
520
|
+
@api_error_handler
|
|
521
|
+
def batch_update(self, memories: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
522
|
+
"""Batch update memories.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
memories: List of memory dictionaries to update. Each dictionary must contain:
|
|
526
|
+
- memory_id (str): ID of the memory to update
|
|
527
|
+
- text (str, optional): New text content for the memory
|
|
528
|
+
- metadata (dict, optional): New metadata for the memory
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Dict[str, Any]: The response from the server.
|
|
532
|
+
|
|
533
|
+
Raises:
|
|
534
|
+
ValidationError: If the input data is invalid.
|
|
535
|
+
AuthenticationError: If authentication fails.
|
|
536
|
+
RateLimitError: If rate limits are exceeded.
|
|
537
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
538
|
+
NetworkError: If network connectivity issues occur.
|
|
539
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
540
|
+
"""
|
|
541
|
+
response = self.client.put("/v1/batch/", json={"memories": memories})
|
|
542
|
+
response.raise_for_status()
|
|
543
|
+
|
|
544
|
+
capture_client_event("client.batch_update", self, {"sync_type": "sync"})
|
|
545
|
+
return response.json()
|
|
546
|
+
|
|
547
|
+
@api_error_handler
|
|
548
|
+
def batch_delete(self, memories: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
549
|
+
"""Batch delete memories.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
memories: List of memory dictionaries to delete. Each dictionary
|
|
553
|
+
must contain:
|
|
554
|
+
- memory_id (str): ID of the memory to delete
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
str: Message indicating the success of the batch deletion.
|
|
558
|
+
|
|
559
|
+
Raises:
|
|
560
|
+
ValidationError: If the input data is invalid.
|
|
561
|
+
AuthenticationError: If authentication fails.
|
|
562
|
+
RateLimitError: If rate limits are exceeded.
|
|
563
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
564
|
+
NetworkError: If network connectivity issues occur.
|
|
565
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
566
|
+
"""
|
|
567
|
+
response = self.client.request("DELETE", "/v1/batch/", json={"memories": memories})
|
|
568
|
+
response.raise_for_status()
|
|
569
|
+
|
|
570
|
+
capture_client_event("client.batch_delete", self, {"sync_type": "sync"})
|
|
571
|
+
return response.json()
|
|
572
|
+
|
|
573
|
+
@api_error_handler
|
|
574
|
+
def create_memory_export(self, schema: str, **kwargs) -> Dict[str, Any]:
|
|
575
|
+
"""Create a memory export with the provided schema.
|
|
576
|
+
|
|
577
|
+
Args:
|
|
578
|
+
schema: JSON schema defining the export structure
|
|
579
|
+
**kwargs: Optional filters like user_id, run_id, etc.
|
|
580
|
+
|
|
581
|
+
Returns:
|
|
582
|
+
Dict containing export request ID and status message
|
|
583
|
+
"""
|
|
584
|
+
response = self.client.post(
|
|
585
|
+
"/v1/exports/",
|
|
586
|
+
json={"schema": schema, **self._prepare_params(kwargs)},
|
|
587
|
+
)
|
|
588
|
+
response.raise_for_status()
|
|
589
|
+
capture_client_event(
|
|
590
|
+
"client.create_memory_export",
|
|
591
|
+
self,
|
|
592
|
+
{
|
|
593
|
+
"schema": schema,
|
|
594
|
+
"keys": list(kwargs.keys()),
|
|
595
|
+
"sync_type": "sync",
|
|
596
|
+
},
|
|
597
|
+
)
|
|
598
|
+
return response.json()
|
|
599
|
+
|
|
600
|
+
@api_error_handler
|
|
601
|
+
def get_memory_export(self, **kwargs) -> Dict[str, Any]:
|
|
602
|
+
"""Get a memory export.
|
|
603
|
+
|
|
604
|
+
Args:
|
|
605
|
+
**kwargs: Filters like user_id to get specific export
|
|
606
|
+
|
|
607
|
+
Returns:
|
|
608
|
+
Dict containing the exported data
|
|
609
|
+
"""
|
|
610
|
+
response = self.client.post("/v1/exports/get/", json=self._prepare_params(kwargs))
|
|
611
|
+
response.raise_for_status()
|
|
612
|
+
capture_client_event(
|
|
613
|
+
"client.get_memory_export",
|
|
614
|
+
self,
|
|
615
|
+
{"keys": list(kwargs.keys()), "sync_type": "sync"},
|
|
616
|
+
)
|
|
617
|
+
return response.json()
|
|
618
|
+
|
|
619
|
+
@api_error_handler
|
|
620
|
+
def get_summary(self, filters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
621
|
+
"""Get the summary of a memory export.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
filters: Optional filters to apply to the summary request
|
|
625
|
+
|
|
626
|
+
Returns:
|
|
627
|
+
Dict containing the export status and summary data
|
|
628
|
+
"""
|
|
629
|
+
|
|
630
|
+
response = self.client.post("/v1/summary/", json=self._prepare_params({"filters": filters}))
|
|
631
|
+
response.raise_for_status()
|
|
632
|
+
capture_client_event("client.get_summary", self, {"sync_type": "sync"})
|
|
633
|
+
return response.json()
|
|
634
|
+
|
|
635
|
+
@api_error_handler
|
|
636
|
+
def get_project(self, fields: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
637
|
+
"""Get instructions or categories for the current project.
|
|
638
|
+
|
|
639
|
+
Args:
|
|
640
|
+
fields: List of fields to retrieve
|
|
641
|
+
|
|
642
|
+
Returns:
|
|
643
|
+
Dictionary containing the requested fields.
|
|
644
|
+
|
|
645
|
+
Raises:
|
|
646
|
+
ValidationError: If the input data is invalid.
|
|
647
|
+
AuthenticationError: If authentication fails.
|
|
648
|
+
RateLimitError: If rate limits are exceeded.
|
|
649
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
650
|
+
NetworkError: If network connectivity issues occur.
|
|
651
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
652
|
+
ValueError: If org_id or project_id are not set.
|
|
653
|
+
"""
|
|
654
|
+
logger.warning(
|
|
655
|
+
"get_project() method is going to be deprecated in version v1.0 of the package. Please use the client.project.get() method instead."
|
|
656
|
+
)
|
|
657
|
+
if not (self.org_id and self.project_id):
|
|
658
|
+
raise ValueError("org_id and project_id must be set to access instructions or categories")
|
|
659
|
+
|
|
660
|
+
params = self._prepare_params({"fields": fields})
|
|
661
|
+
response = self.client.get(
|
|
662
|
+
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
|
663
|
+
params=params,
|
|
664
|
+
)
|
|
665
|
+
response.raise_for_status()
|
|
666
|
+
capture_client_event(
|
|
667
|
+
"client.get_project_details",
|
|
668
|
+
self,
|
|
669
|
+
{"fields": fields, "sync_type": "sync"},
|
|
670
|
+
)
|
|
671
|
+
return response.json()
|
|
672
|
+
|
|
673
|
+
@api_error_handler
|
|
674
|
+
def update_project(
|
|
675
|
+
self,
|
|
676
|
+
custom_instructions: Optional[str] = None,
|
|
677
|
+
custom_categories: Optional[List[str]] = None,
|
|
678
|
+
retrieval_criteria: Optional[List[Dict[str, Any]]] = None,
|
|
679
|
+
enable_graph: Optional[bool] = None,
|
|
680
|
+
version: Optional[str] = None,
|
|
681
|
+
) -> Dict[str, Any]:
|
|
682
|
+
"""Update the project settings.
|
|
683
|
+
|
|
684
|
+
Args:
|
|
685
|
+
custom_instructions: New instructions for the project
|
|
686
|
+
custom_categories: New categories for the project
|
|
687
|
+
retrieval_criteria: New retrieval criteria for the project
|
|
688
|
+
enable_graph: Enable or disable the graph for the project
|
|
689
|
+
version: Version of the project
|
|
690
|
+
|
|
691
|
+
Returns:
|
|
692
|
+
Dictionary containing the API response.
|
|
693
|
+
|
|
694
|
+
Raises:
|
|
695
|
+
ValidationError: If the input data is invalid.
|
|
696
|
+
AuthenticationError: If authentication fails.
|
|
697
|
+
RateLimitError: If rate limits are exceeded.
|
|
698
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
699
|
+
NetworkError: If network connectivity issues occur.
|
|
700
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
701
|
+
ValueError: If org_id or project_id are not set.
|
|
702
|
+
"""
|
|
703
|
+
logger.warning(
|
|
704
|
+
"update_project() method is going to be deprecated in version v1.0 of the package. Please use the client.project.update() method instead."
|
|
705
|
+
)
|
|
706
|
+
if not (self.org_id and self.project_id):
|
|
707
|
+
raise ValueError("org_id and project_id must be set to update instructions or categories")
|
|
708
|
+
|
|
709
|
+
if (
|
|
710
|
+
custom_instructions is None
|
|
711
|
+
and custom_categories is None
|
|
712
|
+
and retrieval_criteria is None
|
|
713
|
+
and enable_graph is None
|
|
714
|
+
and version is None
|
|
715
|
+
):
|
|
716
|
+
raise ValueError(
|
|
717
|
+
"Currently we only support updating custom_instructions or "
|
|
718
|
+
"custom_categories or retrieval_criteria, so you must "
|
|
719
|
+
"provide at least one of them"
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
payload = self._prepare_params(
|
|
723
|
+
{
|
|
724
|
+
"custom_instructions": custom_instructions,
|
|
725
|
+
"custom_categories": custom_categories,
|
|
726
|
+
"retrieval_criteria": retrieval_criteria,
|
|
727
|
+
"enable_graph": enable_graph,
|
|
728
|
+
"version": version,
|
|
729
|
+
}
|
|
730
|
+
)
|
|
731
|
+
response = self.client.patch(
|
|
732
|
+
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
|
733
|
+
json=payload,
|
|
734
|
+
)
|
|
735
|
+
response.raise_for_status()
|
|
736
|
+
capture_client_event(
|
|
737
|
+
"client.update_project",
|
|
738
|
+
self,
|
|
739
|
+
{
|
|
740
|
+
"custom_instructions": custom_instructions,
|
|
741
|
+
"custom_categories": custom_categories,
|
|
742
|
+
"retrieval_criteria": retrieval_criteria,
|
|
743
|
+
"enable_graph": enable_graph,
|
|
744
|
+
"version": version,
|
|
745
|
+
"sync_type": "sync",
|
|
746
|
+
},
|
|
747
|
+
)
|
|
748
|
+
return response.json()
|
|
749
|
+
|
|
750
|
+
def chat(self):
|
|
751
|
+
"""Start a chat with the Mem0 AI. (Not implemented)
|
|
752
|
+
|
|
753
|
+
Raises:
|
|
754
|
+
NotImplementedError: This method is not implemented yet.
|
|
755
|
+
"""
|
|
756
|
+
raise NotImplementedError("Chat is not implemented yet")
|
|
757
|
+
|
|
758
|
+
@api_error_handler
|
|
759
|
+
def get_webhooks(self, project_id: str) -> Dict[str, Any]:
|
|
760
|
+
"""Get webhooks configuration for the project.
|
|
761
|
+
|
|
762
|
+
Args:
|
|
763
|
+
project_id: The ID of the project to get webhooks for.
|
|
764
|
+
|
|
765
|
+
Returns:
|
|
766
|
+
Dictionary containing webhook details.
|
|
767
|
+
|
|
768
|
+
Raises:
|
|
769
|
+
ValidationError: If the input data is invalid.
|
|
770
|
+
AuthenticationError: If authentication fails.
|
|
771
|
+
RateLimitError: If rate limits are exceeded.
|
|
772
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
773
|
+
NetworkError: If network connectivity issues occur.
|
|
774
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
775
|
+
ValueError: If project_id is not set.
|
|
776
|
+
"""
|
|
777
|
+
|
|
778
|
+
response = self.client.get(f"api/v1/webhooks/projects/{project_id}/")
|
|
779
|
+
response.raise_for_status()
|
|
780
|
+
capture_client_event("client.get_webhook", self, {"sync_type": "sync"})
|
|
781
|
+
return response.json()
|
|
782
|
+
|
|
783
|
+
@api_error_handler
|
|
784
|
+
def create_webhook(self, url: str, name: str, project_id: str, event_types: List[str]) -> Dict[str, Any]:
|
|
785
|
+
"""Create a webhook for the current project.
|
|
786
|
+
|
|
787
|
+
Args:
|
|
788
|
+
url: The URL to send the webhook to.
|
|
789
|
+
name: The name of the webhook.
|
|
790
|
+
event_types: List of event types to trigger the webhook for.
|
|
791
|
+
|
|
792
|
+
Returns:
|
|
793
|
+
Dictionary containing the created webhook details.
|
|
794
|
+
|
|
795
|
+
Raises:
|
|
796
|
+
ValidationError: If the input data is invalid.
|
|
797
|
+
AuthenticationError: If authentication fails.
|
|
798
|
+
RateLimitError: If rate limits are exceeded.
|
|
799
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
800
|
+
NetworkError: If network connectivity issues occur.
|
|
801
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
802
|
+
ValueError: If project_id is not set.
|
|
803
|
+
"""
|
|
804
|
+
|
|
805
|
+
payload = {"url": url, "name": name, "event_types": event_types}
|
|
806
|
+
response = self.client.post(f"api/v1/webhooks/projects/{project_id}/", json=payload)
|
|
807
|
+
response.raise_for_status()
|
|
808
|
+
capture_client_event("client.create_webhook", self, {"sync_type": "sync"})
|
|
809
|
+
return response.json()
|
|
810
|
+
|
|
811
|
+
@api_error_handler
|
|
812
|
+
def update_webhook(
|
|
813
|
+
self,
|
|
814
|
+
webhook_id: int,
|
|
815
|
+
name: Optional[str] = None,
|
|
816
|
+
url: Optional[str] = None,
|
|
817
|
+
event_types: Optional[List[str]] = None,
|
|
818
|
+
) -> Dict[str, Any]:
|
|
819
|
+
"""Update a webhook configuration.
|
|
820
|
+
|
|
821
|
+
Args:
|
|
822
|
+
webhook_id: ID of the webhook to update
|
|
823
|
+
name: Optional new name for the webhook
|
|
824
|
+
url: Optional new URL for the webhook
|
|
825
|
+
event_types: Optional list of event types to trigger the webhook for.
|
|
826
|
+
|
|
827
|
+
Returns:
|
|
828
|
+
Dictionary containing the updated webhook details.
|
|
829
|
+
|
|
830
|
+
Raises:
|
|
831
|
+
ValidationError: If the input data is invalid.
|
|
832
|
+
AuthenticationError: If authentication fails.
|
|
833
|
+
RateLimitError: If rate limits are exceeded.
|
|
834
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
835
|
+
NetworkError: If network connectivity issues occur.
|
|
836
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
837
|
+
"""
|
|
838
|
+
|
|
839
|
+
payload = {k: v for k, v in {"name": name, "url": url, "event_types": event_types}.items() if v is not None}
|
|
840
|
+
response = self.client.put(f"api/v1/webhooks/{webhook_id}/", json=payload)
|
|
841
|
+
response.raise_for_status()
|
|
842
|
+
capture_client_event("client.update_webhook", self, {"webhook_id": webhook_id, "sync_type": "sync"})
|
|
843
|
+
return response.json()
|
|
844
|
+
|
|
845
|
+
@api_error_handler
|
|
846
|
+
def delete_webhook(self, webhook_id: int) -> Dict[str, str]:
|
|
847
|
+
"""Delete a webhook configuration.
|
|
848
|
+
|
|
849
|
+
Args:
|
|
850
|
+
webhook_id: ID of the webhook to delete
|
|
851
|
+
|
|
852
|
+
Returns:
|
|
853
|
+
Dictionary containing success message.
|
|
854
|
+
|
|
855
|
+
Raises:
|
|
856
|
+
ValidationError: If the input data is invalid.
|
|
857
|
+
AuthenticationError: If authentication fails.
|
|
858
|
+
RateLimitError: If rate limits are exceeded.
|
|
859
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
860
|
+
NetworkError: If network connectivity issues occur.
|
|
861
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
862
|
+
"""
|
|
863
|
+
|
|
864
|
+
response = self.client.delete(f"api/v1/webhooks/{webhook_id}/")
|
|
865
|
+
response.raise_for_status()
|
|
866
|
+
capture_client_event(
|
|
867
|
+
"client.delete_webhook",
|
|
868
|
+
self,
|
|
869
|
+
{"webhook_id": webhook_id, "sync_type": "sync"},
|
|
870
|
+
)
|
|
871
|
+
return response.json()
|
|
872
|
+
|
|
873
|
+
@api_error_handler
|
|
874
|
+
def feedback(
|
|
875
|
+
self,
|
|
876
|
+
memory_id: str,
|
|
877
|
+
feedback: Optional[str] = None,
|
|
878
|
+
feedback_reason: Optional[str] = None,
|
|
879
|
+
) -> Dict[str, str]:
|
|
880
|
+
VALID_FEEDBACK_VALUES = {"POSITIVE", "NEGATIVE", "VERY_NEGATIVE"}
|
|
881
|
+
|
|
882
|
+
feedback = feedback.upper() if feedback else None
|
|
883
|
+
if feedback is not None and feedback not in VALID_FEEDBACK_VALUES:
|
|
884
|
+
raise ValueError(f"feedback must be one of {', '.join(VALID_FEEDBACK_VALUES)} or None")
|
|
885
|
+
|
|
886
|
+
data = {
|
|
887
|
+
"memory_id": memory_id,
|
|
888
|
+
"feedback": feedback,
|
|
889
|
+
"feedback_reason": feedback_reason,
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
response = self.client.post("/v1/feedback/", json=data)
|
|
893
|
+
response.raise_for_status()
|
|
894
|
+
capture_client_event("client.feedback", self, data, {"sync_type": "sync"})
|
|
895
|
+
return response.json()
|
|
896
|
+
|
|
897
|
+
def _prepare_payload(self, messages: List[Dict[str, str]], kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
898
|
+
"""Prepare the payload for API requests.
|
|
899
|
+
|
|
900
|
+
Args:
|
|
901
|
+
messages: The messages to include in the payload.
|
|
902
|
+
kwargs: Additional keyword arguments to include in the payload.
|
|
903
|
+
|
|
904
|
+
Returns:
|
|
905
|
+
A dictionary containing the prepared payload.
|
|
906
|
+
"""
|
|
907
|
+
payload = {}
|
|
908
|
+
payload["messages"] = messages
|
|
909
|
+
|
|
910
|
+
payload.update({k: v for k, v in kwargs.items() if v is not None})
|
|
911
|
+
return payload
|
|
912
|
+
|
|
913
|
+
def _prepare_params(self, kwargs: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
914
|
+
"""Prepare query parameters for API requests.
|
|
915
|
+
|
|
916
|
+
Args:
|
|
917
|
+
kwargs: Keyword arguments to include in the parameters.
|
|
918
|
+
|
|
919
|
+
Returns:
|
|
920
|
+
A dictionary containing the prepared parameters.
|
|
921
|
+
|
|
922
|
+
Raises:
|
|
923
|
+
ValueError: If either org_id or project_id is provided but not both.
|
|
924
|
+
"""
|
|
925
|
+
|
|
926
|
+
if kwargs is None:
|
|
927
|
+
kwargs = {}
|
|
928
|
+
|
|
929
|
+
# Add org_id and project_id if both are available
|
|
930
|
+
if self.org_id and self.project_id:
|
|
931
|
+
kwargs["org_id"] = self.org_id
|
|
932
|
+
kwargs["project_id"] = self.project_id
|
|
933
|
+
elif self.org_id or self.project_id:
|
|
934
|
+
raise ValueError("Please provide both org_id and project_id")
|
|
935
|
+
|
|
936
|
+
return {k: v for k, v in kwargs.items() if v is not None}
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
class AsyncMemoryClient:
|
|
940
|
+
"""Asynchronous client for interacting with the Mem0 API.
|
|
941
|
+
|
|
942
|
+
This class provides asynchronous versions of all MemoryClient methods.
|
|
943
|
+
It uses httpx.AsyncClient for making non-blocking API requests.
|
|
944
|
+
"""
|
|
945
|
+
|
|
946
|
+
def __init__(
|
|
947
|
+
self,
|
|
948
|
+
api_key: Optional[str] = None,
|
|
949
|
+
host: Optional[str] = None,
|
|
950
|
+
org_id: Optional[str] = None,
|
|
951
|
+
project_id: Optional[str] = None,
|
|
952
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
953
|
+
):
|
|
954
|
+
"""Initialize the AsyncMemoryClient.
|
|
955
|
+
|
|
956
|
+
Args:
|
|
957
|
+
api_key: The API key for authenticating with the Mem0 API. If not
|
|
958
|
+
provided, it will attempt to use the MEM0_API_KEY
|
|
959
|
+
environment variable.
|
|
960
|
+
host: The base URL for the Mem0 API. Defaults to
|
|
961
|
+
"https://api.mem0.ai".
|
|
962
|
+
org_id: The ID of the organization.
|
|
963
|
+
project_id: The ID of the project.
|
|
964
|
+
client: A custom httpx.AsyncClient instance. If provided, it will
|
|
965
|
+
be used instead of creating a new one. Note that base_url
|
|
966
|
+
and headers will be set/overridden as needed.
|
|
967
|
+
|
|
968
|
+
Raises:
|
|
969
|
+
ValueError: If no API key is provided or found in the environment.
|
|
970
|
+
"""
|
|
971
|
+
self.api_key = api_key or os.getenv("MEM0_API_KEY")
|
|
972
|
+
self.host = host or "https://api.mem0.ai"
|
|
973
|
+
self.org_id = org_id
|
|
974
|
+
self.project_id = project_id
|
|
975
|
+
self.user_id = get_user_id()
|
|
976
|
+
|
|
977
|
+
if not self.api_key:
|
|
978
|
+
raise ValueError("Mem0 API Key not provided. Please provide an API Key.")
|
|
979
|
+
|
|
980
|
+
# Create MD5 hash of API key for user_id
|
|
981
|
+
self.user_id = hashlib.md5(self.api_key.encode()).hexdigest()
|
|
982
|
+
|
|
983
|
+
if client is not None:
|
|
984
|
+
self.async_client = client
|
|
985
|
+
# Ensure the client has the correct base_url and headers
|
|
986
|
+
self.async_client.base_url = httpx.URL(self.host)
|
|
987
|
+
self.async_client.headers.update(
|
|
988
|
+
{
|
|
989
|
+
"Authorization": f"Token {self.api_key}",
|
|
990
|
+
"Mem0-User-ID": self.user_id,
|
|
991
|
+
}
|
|
992
|
+
)
|
|
993
|
+
else:
|
|
994
|
+
self.async_client = httpx.AsyncClient(
|
|
995
|
+
base_url=self.host,
|
|
996
|
+
headers={
|
|
997
|
+
"Authorization": f"Token {self.api_key}",
|
|
998
|
+
"Mem0-User-ID": self.user_id,
|
|
999
|
+
},
|
|
1000
|
+
timeout=300,
|
|
1001
|
+
)
|
|
1002
|
+
|
|
1003
|
+
self.user_email = self._validate_api_key()
|
|
1004
|
+
|
|
1005
|
+
# Initialize project manager
|
|
1006
|
+
self.project = AsyncProject(
|
|
1007
|
+
client=self.async_client,
|
|
1008
|
+
org_id=self.org_id,
|
|
1009
|
+
project_id=self.project_id,
|
|
1010
|
+
user_email=self.user_email,
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
capture_client_event("client.init", self, {"sync_type": "async"})
|
|
1014
|
+
|
|
1015
|
+
def _validate_api_key(self):
|
|
1016
|
+
"""Validate the API key by making a test request."""
|
|
1017
|
+
try:
|
|
1018
|
+
params = self._prepare_params()
|
|
1019
|
+
response = requests.get(
|
|
1020
|
+
f"{self.host}/v1/ping/",
|
|
1021
|
+
headers={
|
|
1022
|
+
"Authorization": f"Token {self.api_key}",
|
|
1023
|
+
"Mem0-User-ID": self.user_id,
|
|
1024
|
+
},
|
|
1025
|
+
params=params,
|
|
1026
|
+
)
|
|
1027
|
+
data = response.json()
|
|
1028
|
+
|
|
1029
|
+
response.raise_for_status()
|
|
1030
|
+
|
|
1031
|
+
if data.get("org_id") and data.get("project_id"):
|
|
1032
|
+
self.org_id = data.get("org_id")
|
|
1033
|
+
self.project_id = data.get("project_id")
|
|
1034
|
+
|
|
1035
|
+
return data.get("user_email")
|
|
1036
|
+
|
|
1037
|
+
except requests.exceptions.HTTPError as e:
|
|
1038
|
+
try:
|
|
1039
|
+
error_data = e.response.json()
|
|
1040
|
+
error_message = error_data.get("detail", str(e))
|
|
1041
|
+
except Exception:
|
|
1042
|
+
error_message = str(e)
|
|
1043
|
+
raise ValueError(f"Error: {error_message}")
|
|
1044
|
+
|
|
1045
|
+
def _prepare_payload(self, messages: List[Dict[str, str]], kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
1046
|
+
"""Prepare the payload for API requests.
|
|
1047
|
+
|
|
1048
|
+
Args:
|
|
1049
|
+
messages: The messages to include in the payload.
|
|
1050
|
+
kwargs: Additional keyword arguments to include in the payload.
|
|
1051
|
+
|
|
1052
|
+
Returns:
|
|
1053
|
+
A dictionary containing the prepared payload.
|
|
1054
|
+
"""
|
|
1055
|
+
payload = {}
|
|
1056
|
+
payload["messages"] = messages
|
|
1057
|
+
|
|
1058
|
+
payload.update({k: v for k, v in kwargs.items() if v is not None})
|
|
1059
|
+
return payload
|
|
1060
|
+
|
|
1061
|
+
def _prepare_params(self, kwargs: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
1062
|
+
"""Prepare query parameters for API requests.
|
|
1063
|
+
|
|
1064
|
+
Args:
|
|
1065
|
+
kwargs: Keyword arguments to include in the parameters.
|
|
1066
|
+
|
|
1067
|
+
Returns:
|
|
1068
|
+
A dictionary containing the prepared parameters.
|
|
1069
|
+
|
|
1070
|
+
Raises:
|
|
1071
|
+
ValueError: If either org_id or project_id is provided but not both.
|
|
1072
|
+
"""
|
|
1073
|
+
|
|
1074
|
+
if kwargs is None:
|
|
1075
|
+
kwargs = {}
|
|
1076
|
+
|
|
1077
|
+
# Add org_id and project_id if both are available
|
|
1078
|
+
if self.org_id and self.project_id:
|
|
1079
|
+
kwargs["org_id"] = self.org_id
|
|
1080
|
+
kwargs["project_id"] = self.project_id
|
|
1081
|
+
elif self.org_id or self.project_id:
|
|
1082
|
+
raise ValueError("Please provide both org_id and project_id")
|
|
1083
|
+
|
|
1084
|
+
return {k: v for k, v in kwargs.items() if v is not None}
|
|
1085
|
+
|
|
1086
|
+
async def __aenter__(self):
|
|
1087
|
+
return self
|
|
1088
|
+
|
|
1089
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
1090
|
+
await self.async_client.aclose()
|
|
1091
|
+
|
|
1092
|
+
@api_error_handler
|
|
1093
|
+
async def add(self, messages, **kwargs) -> Dict[str, Any]:
|
|
1094
|
+
# Handle different message input formats (align with OSS behavior)
|
|
1095
|
+
if isinstance(messages, str):
|
|
1096
|
+
messages = [{"role": "user", "content": messages}]
|
|
1097
|
+
elif isinstance(messages, dict):
|
|
1098
|
+
messages = [messages]
|
|
1099
|
+
elif not isinstance(messages, list):
|
|
1100
|
+
raise ValueError(
|
|
1101
|
+
f"messages must be str, dict, or list[dict], got {type(messages).__name__}"
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
kwargs = self._prepare_params(kwargs)
|
|
1105
|
+
|
|
1106
|
+
# Remove deprecated parameters
|
|
1107
|
+
if "output_format" in kwargs:
|
|
1108
|
+
warnings.warn(
|
|
1109
|
+
"output_format parameter is deprecated and ignored. All responses now use v1.1 format.",
|
|
1110
|
+
DeprecationWarning,
|
|
1111
|
+
stacklevel=2,
|
|
1112
|
+
)
|
|
1113
|
+
kwargs.pop("output_format")
|
|
1114
|
+
|
|
1115
|
+
# Set async_mode to True by default, but allow user override
|
|
1116
|
+
if "async_mode" not in kwargs:
|
|
1117
|
+
kwargs["async_mode"] = True
|
|
1118
|
+
|
|
1119
|
+
# Force v1.1 format for all add operations
|
|
1120
|
+
kwargs["output_format"] = "v1.1"
|
|
1121
|
+
payload = self._prepare_payload(messages, kwargs)
|
|
1122
|
+
response = await self.async_client.post("/v1/memories/", json=payload)
|
|
1123
|
+
response.raise_for_status()
|
|
1124
|
+
if "metadata" in kwargs:
|
|
1125
|
+
del kwargs["metadata"]
|
|
1126
|
+
capture_client_event("client.add", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
|
1127
|
+
return response.json()
|
|
1128
|
+
|
|
1129
|
+
@api_error_handler
|
|
1130
|
+
async def get(self, memory_id: str) -> Dict[str, Any]:
|
|
1131
|
+
params = self._prepare_params()
|
|
1132
|
+
response = await self.async_client.get(f"/v1/memories/{memory_id}/", params=params)
|
|
1133
|
+
response.raise_for_status()
|
|
1134
|
+
capture_client_event("client.get", self, {"memory_id": memory_id, "sync_type": "async"})
|
|
1135
|
+
return response.json()
|
|
1136
|
+
|
|
1137
|
+
@api_error_handler
|
|
1138
|
+
async def get_all(self, **kwargs) -> Dict[str, Any]:
|
|
1139
|
+
params = self._prepare_params(kwargs)
|
|
1140
|
+
params.pop("output_format", None) # Remove output_format for get operations
|
|
1141
|
+
params.pop("async_mode", None)
|
|
1142
|
+
|
|
1143
|
+
if "page" in params and "page_size" in params:
|
|
1144
|
+
query_params = {
|
|
1145
|
+
"page": params.pop("page"),
|
|
1146
|
+
"page_size": params.pop("page_size"),
|
|
1147
|
+
}
|
|
1148
|
+
response = await self.async_client.post("/v2/memories/", json=params, params=query_params)
|
|
1149
|
+
else:
|
|
1150
|
+
response = await self.async_client.post("/v2/memories/", json=params)
|
|
1151
|
+
response.raise_for_status()
|
|
1152
|
+
if "metadata" in kwargs:
|
|
1153
|
+
del kwargs["metadata"]
|
|
1154
|
+
capture_client_event(
|
|
1155
|
+
"client.get_all",
|
|
1156
|
+
self,
|
|
1157
|
+
{
|
|
1158
|
+
"api_version": "v2",
|
|
1159
|
+
"keys": list(kwargs.keys()),
|
|
1160
|
+
"sync_type": "async",
|
|
1161
|
+
},
|
|
1162
|
+
)
|
|
1163
|
+
result = response.json()
|
|
1164
|
+
|
|
1165
|
+
# Ensure v1.1 format (wrap raw list if needed)
|
|
1166
|
+
if isinstance(result, list):
|
|
1167
|
+
return {"results": result}
|
|
1168
|
+
return result
|
|
1169
|
+
|
|
1170
|
+
@api_error_handler
|
|
1171
|
+
async def search(self, query: str, **kwargs) -> Dict[str, Any]:
|
|
1172
|
+
payload = {"query": query}
|
|
1173
|
+
params = self._prepare_params(kwargs)
|
|
1174
|
+
params.pop("output_format", None) # Remove output_format for search operations
|
|
1175
|
+
params.pop("async_mode", None)
|
|
1176
|
+
|
|
1177
|
+
payload.update(params)
|
|
1178
|
+
|
|
1179
|
+
response = await self.async_client.post("/v2/memories/search/", json=payload)
|
|
1180
|
+
response.raise_for_status()
|
|
1181
|
+
if "metadata" in kwargs:
|
|
1182
|
+
del kwargs["metadata"]
|
|
1183
|
+
capture_client_event(
|
|
1184
|
+
"client.search",
|
|
1185
|
+
self,
|
|
1186
|
+
{
|
|
1187
|
+
"api_version": "v2",
|
|
1188
|
+
"keys": list(kwargs.keys()),
|
|
1189
|
+
"sync_type": "async",
|
|
1190
|
+
},
|
|
1191
|
+
)
|
|
1192
|
+
result = response.json()
|
|
1193
|
+
|
|
1194
|
+
# Ensure v1.1 format (wrap raw list if needed)
|
|
1195
|
+
if isinstance(result, list):
|
|
1196
|
+
return {"results": result}
|
|
1197
|
+
return result
|
|
1198
|
+
|
|
1199
|
+
@api_error_handler
|
|
1200
|
+
async def update(
|
|
1201
|
+
self, memory_id: str, text: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None
|
|
1202
|
+
) -> Dict[str, Any]:
|
|
1203
|
+
"""
|
|
1204
|
+
Update a memory by ID asynchronously.
|
|
1205
|
+
|
|
1206
|
+
Args:
|
|
1207
|
+
memory_id (str): Memory ID.
|
|
1208
|
+
text (str, optional): New content to update the memory with.
|
|
1209
|
+
metadata (dict, optional): Metadata to update in the memory.
|
|
1210
|
+
|
|
1211
|
+
Returns:
|
|
1212
|
+
Dict[str, Any]: The response from the server.
|
|
1213
|
+
|
|
1214
|
+
Example:
|
|
1215
|
+
>>> await client.update(memory_id="mem_123", text="Likes to play tennis on weekends")
|
|
1216
|
+
"""
|
|
1217
|
+
if text is None and metadata is None:
|
|
1218
|
+
raise ValueError("Either text or metadata must be provided for update.")
|
|
1219
|
+
|
|
1220
|
+
payload = {}
|
|
1221
|
+
if text is not None:
|
|
1222
|
+
payload["text"] = text
|
|
1223
|
+
if metadata is not None:
|
|
1224
|
+
payload["metadata"] = metadata
|
|
1225
|
+
|
|
1226
|
+
capture_client_event("client.update", self, {"memory_id": memory_id, "sync_type": "async"})
|
|
1227
|
+
params = self._prepare_params()
|
|
1228
|
+
response = await self.async_client.put(f"/v1/memories/{memory_id}/", json=payload, params=params)
|
|
1229
|
+
response.raise_for_status()
|
|
1230
|
+
return response.json()
|
|
1231
|
+
|
|
1232
|
+
@api_error_handler
|
|
1233
|
+
async def delete(self, memory_id: str) -> Dict[str, Any]:
|
|
1234
|
+
"""Delete a specific memory by ID.
|
|
1235
|
+
|
|
1236
|
+
Args:
|
|
1237
|
+
memory_id: The ID of the memory to delete.
|
|
1238
|
+
|
|
1239
|
+
Returns:
|
|
1240
|
+
A dictionary containing the API response.
|
|
1241
|
+
|
|
1242
|
+
Raises:
|
|
1243
|
+
ValidationError: If the input data is invalid.
|
|
1244
|
+
AuthenticationError: If authentication fails.
|
|
1245
|
+
RateLimitError: If rate limits are exceeded.
|
|
1246
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1247
|
+
NetworkError: If network connectivity issues occur.
|
|
1248
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1249
|
+
"""
|
|
1250
|
+
params = self._prepare_params()
|
|
1251
|
+
response = await self.async_client.delete(f"/v1/memories/{memory_id}/", params=params)
|
|
1252
|
+
response.raise_for_status()
|
|
1253
|
+
capture_client_event("client.delete", self, {"memory_id": memory_id, "sync_type": "async"})
|
|
1254
|
+
return response.json()
|
|
1255
|
+
|
|
1256
|
+
@api_error_handler
|
|
1257
|
+
async def delete_all(self, **kwargs) -> Dict[str, str]:
|
|
1258
|
+
"""Delete all memories, with optional filtering.
|
|
1259
|
+
|
|
1260
|
+
Args:
|
|
1261
|
+
**kwargs: Optional parameters for filtering (user_id, agent_id, app_id).
|
|
1262
|
+
|
|
1263
|
+
Returns:
|
|
1264
|
+
A dictionary containing the API response.
|
|
1265
|
+
|
|
1266
|
+
Raises:
|
|
1267
|
+
ValidationError: If the input data is invalid.
|
|
1268
|
+
AuthenticationError: If authentication fails.
|
|
1269
|
+
RateLimitError: If rate limits are exceeded.
|
|
1270
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1271
|
+
NetworkError: If network connectivity issues occur.
|
|
1272
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1273
|
+
"""
|
|
1274
|
+
params = self._prepare_params(kwargs)
|
|
1275
|
+
response = await self.async_client.delete("/v1/memories/", params=params)
|
|
1276
|
+
response.raise_for_status()
|
|
1277
|
+
capture_client_event("client.delete_all", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
|
1278
|
+
return response.json()
|
|
1279
|
+
|
|
1280
|
+
@api_error_handler
|
|
1281
|
+
async def history(self, memory_id: str) -> List[Dict[str, Any]]:
|
|
1282
|
+
"""Retrieve the history of a specific memory.
|
|
1283
|
+
|
|
1284
|
+
Args:
|
|
1285
|
+
memory_id: The ID of the memory to retrieve history for.
|
|
1286
|
+
|
|
1287
|
+
Returns:
|
|
1288
|
+
A list of dictionaries containing the memory history.
|
|
1289
|
+
|
|
1290
|
+
Raises:
|
|
1291
|
+
ValidationError: If the input data is invalid.
|
|
1292
|
+
AuthenticationError: If authentication fails.
|
|
1293
|
+
RateLimitError: If rate limits are exceeded.
|
|
1294
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1295
|
+
NetworkError: If network connectivity issues occur.
|
|
1296
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1297
|
+
"""
|
|
1298
|
+
params = self._prepare_params()
|
|
1299
|
+
response = await self.async_client.get(f"/v1/memories/{memory_id}/history/", params=params)
|
|
1300
|
+
response.raise_for_status()
|
|
1301
|
+
capture_client_event("client.history", self, {"memory_id": memory_id, "sync_type": "async"})
|
|
1302
|
+
return response.json()
|
|
1303
|
+
|
|
1304
|
+
@api_error_handler
|
|
1305
|
+
async def users(self) -> Dict[str, Any]:
|
|
1306
|
+
"""Get all users, agents, and sessions for which memories exist."""
|
|
1307
|
+
params = self._prepare_params()
|
|
1308
|
+
response = await self.async_client.get("/v1/entities/", params=params)
|
|
1309
|
+
response.raise_for_status()
|
|
1310
|
+
capture_client_event("client.users", self, {"sync_type": "async"})
|
|
1311
|
+
return response.json()
|
|
1312
|
+
|
|
1313
|
+
@api_error_handler
|
|
1314
|
+
async def delete_users(
|
|
1315
|
+
self,
|
|
1316
|
+
user_id: Optional[str] = None,
|
|
1317
|
+
agent_id: Optional[str] = None,
|
|
1318
|
+
app_id: Optional[str] = None,
|
|
1319
|
+
run_id: Optional[str] = None,
|
|
1320
|
+
) -> Dict[str, str]:
|
|
1321
|
+
"""Delete specific entities or all entities if no filters provided.
|
|
1322
|
+
|
|
1323
|
+
Args:
|
|
1324
|
+
user_id: Optional user ID to delete specific user
|
|
1325
|
+
agent_id: Optional agent ID to delete specific agent
|
|
1326
|
+
app_id: Optional app ID to delete specific app
|
|
1327
|
+
run_id: Optional run ID to delete specific run
|
|
1328
|
+
|
|
1329
|
+
Returns:
|
|
1330
|
+
Dict with success message
|
|
1331
|
+
|
|
1332
|
+
Raises:
|
|
1333
|
+
ValueError: If specified entity not found
|
|
1334
|
+
ValidationError: If the input data is invalid.
|
|
1335
|
+
AuthenticationError: If authentication fails.
|
|
1336
|
+
MemoryNotFoundError: If the entity doesn't exist.
|
|
1337
|
+
NetworkError: If network connectivity issues occur.
|
|
1338
|
+
"""
|
|
1339
|
+
|
|
1340
|
+
if user_id:
|
|
1341
|
+
to_delete = [{"type": "user", "name": user_id}]
|
|
1342
|
+
elif agent_id:
|
|
1343
|
+
to_delete = [{"type": "agent", "name": agent_id}]
|
|
1344
|
+
elif app_id:
|
|
1345
|
+
to_delete = [{"type": "app", "name": app_id}]
|
|
1346
|
+
elif run_id:
|
|
1347
|
+
to_delete = [{"type": "run", "name": run_id}]
|
|
1348
|
+
else:
|
|
1349
|
+
entities = await self.users()
|
|
1350
|
+
# Filter entities based on provided IDs using list comprehension
|
|
1351
|
+
to_delete = [{"type": entity["type"], "name": entity["name"]} for entity in entities["results"]]
|
|
1352
|
+
|
|
1353
|
+
params = self._prepare_params()
|
|
1354
|
+
|
|
1355
|
+
if not to_delete:
|
|
1356
|
+
raise ValueError("No entities to delete")
|
|
1357
|
+
|
|
1358
|
+
# Delete entities and check response immediately
|
|
1359
|
+
for entity in to_delete:
|
|
1360
|
+
response = await self.async_client.delete(f"/v2/entities/{entity['type']}/{entity['name']}/", params=params)
|
|
1361
|
+
response.raise_for_status()
|
|
1362
|
+
|
|
1363
|
+
capture_client_event(
|
|
1364
|
+
"client.delete_users",
|
|
1365
|
+
self,
|
|
1366
|
+
{
|
|
1367
|
+
"user_id": user_id,
|
|
1368
|
+
"agent_id": agent_id,
|
|
1369
|
+
"app_id": app_id,
|
|
1370
|
+
"run_id": run_id,
|
|
1371
|
+
"sync_type": "async",
|
|
1372
|
+
},
|
|
1373
|
+
)
|
|
1374
|
+
return {
|
|
1375
|
+
"message": "Entity deleted successfully."
|
|
1376
|
+
if (user_id or agent_id or app_id or run_id)
|
|
1377
|
+
else "All users, agents, apps and runs deleted."
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
@api_error_handler
|
|
1381
|
+
async def reset(self) -> Dict[str, str]:
|
|
1382
|
+
"""Reset the client by deleting all users and memories.
|
|
1383
|
+
|
|
1384
|
+
This method deletes all users, agents, sessions, and memories
|
|
1385
|
+
associated with the client.
|
|
1386
|
+
|
|
1387
|
+
Returns:
|
|
1388
|
+
Dict[str, str]: Message client reset successful.
|
|
1389
|
+
|
|
1390
|
+
Raises:
|
|
1391
|
+
ValidationError: If the input data is invalid.
|
|
1392
|
+
AuthenticationError: If authentication fails.
|
|
1393
|
+
RateLimitError: If rate limits are exceeded.
|
|
1394
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1395
|
+
NetworkError: If network connectivity issues occur.
|
|
1396
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1397
|
+
"""
|
|
1398
|
+
await self.delete_users()
|
|
1399
|
+
capture_client_event("client.reset", self, {"sync_type": "async"})
|
|
1400
|
+
return {"message": "Client reset successful. All users and memories deleted."}
|
|
1401
|
+
|
|
1402
|
+
@api_error_handler
|
|
1403
|
+
async def batch_update(self, memories: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
1404
|
+
"""Batch update memories.
|
|
1405
|
+
|
|
1406
|
+
Args:
|
|
1407
|
+
memories: List of memory dictionaries to update. Each dictionary must contain:
|
|
1408
|
+
- memory_id (str): ID of the memory to update
|
|
1409
|
+
- text (str, optional): New text content for the memory
|
|
1410
|
+
- metadata (dict, optional): New metadata for the memory
|
|
1411
|
+
|
|
1412
|
+
Returns:
|
|
1413
|
+
Dict[str, Any]: The response from the server.
|
|
1414
|
+
|
|
1415
|
+
Raises:
|
|
1416
|
+
ValidationError: If the input data is invalid.
|
|
1417
|
+
AuthenticationError: If authentication fails.
|
|
1418
|
+
RateLimitError: If rate limits are exceeded.
|
|
1419
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1420
|
+
NetworkError: If network connectivity issues occur.
|
|
1421
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1422
|
+
"""
|
|
1423
|
+
response = await self.async_client.put("/v1/batch/", json={"memories": memories})
|
|
1424
|
+
response.raise_for_status()
|
|
1425
|
+
|
|
1426
|
+
capture_client_event("client.batch_update", self, {"sync_type": "async"})
|
|
1427
|
+
return response.json()
|
|
1428
|
+
|
|
1429
|
+
@api_error_handler
|
|
1430
|
+
async def batch_delete(self, memories: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
1431
|
+
"""Batch delete memories.
|
|
1432
|
+
|
|
1433
|
+
Args:
|
|
1434
|
+
memories: List of memory dictionaries to delete. Each dictionary
|
|
1435
|
+
must contain:
|
|
1436
|
+
- memory_id (str): ID of the memory to delete
|
|
1437
|
+
|
|
1438
|
+
Returns:
|
|
1439
|
+
str: Message indicating the success of the batch deletion.
|
|
1440
|
+
|
|
1441
|
+
Raises:
|
|
1442
|
+
ValidationError: If the input data is invalid.
|
|
1443
|
+
AuthenticationError: If authentication fails.
|
|
1444
|
+
RateLimitError: If rate limits are exceeded.
|
|
1445
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1446
|
+
NetworkError: If network connectivity issues occur.
|
|
1447
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1448
|
+
"""
|
|
1449
|
+
response = await self.async_client.request("DELETE", "/v1/batch/", json={"memories": memories})
|
|
1450
|
+
response.raise_for_status()
|
|
1451
|
+
|
|
1452
|
+
capture_client_event("client.batch_delete", self, {"sync_type": "async"})
|
|
1453
|
+
return response.json()
|
|
1454
|
+
|
|
1455
|
+
@api_error_handler
|
|
1456
|
+
async def create_memory_export(self, schema: str, **kwargs) -> Dict[str, Any]:
|
|
1457
|
+
"""Create a memory export with the provided schema.
|
|
1458
|
+
|
|
1459
|
+
Args:
|
|
1460
|
+
schema: JSON schema defining the export structure
|
|
1461
|
+
**kwargs: Optional filters like user_id, run_id, etc.
|
|
1462
|
+
|
|
1463
|
+
Returns:
|
|
1464
|
+
Dict containing export request ID and status message
|
|
1465
|
+
"""
|
|
1466
|
+
response = await self.async_client.post("/v1/exports/", json={"schema": schema, **self._prepare_params(kwargs)})
|
|
1467
|
+
response.raise_for_status()
|
|
1468
|
+
capture_client_event(
|
|
1469
|
+
"client.create_memory_export", self, {"schema": schema, "keys": list(kwargs.keys()), "sync_type": "async"}
|
|
1470
|
+
)
|
|
1471
|
+
return response.json()
|
|
1472
|
+
|
|
1473
|
+
@api_error_handler
|
|
1474
|
+
async def get_memory_export(self, **kwargs) -> Dict[str, Any]:
|
|
1475
|
+
"""Get a memory export.
|
|
1476
|
+
|
|
1477
|
+
Args:
|
|
1478
|
+
**kwargs: Filters like user_id to get specific export
|
|
1479
|
+
|
|
1480
|
+
Returns:
|
|
1481
|
+
Dict containing the exported data
|
|
1482
|
+
"""
|
|
1483
|
+
response = await self.async_client.post("/v1/exports/get/", json=self._prepare_params(kwargs))
|
|
1484
|
+
response.raise_for_status()
|
|
1485
|
+
capture_client_event("client.get_memory_export", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
|
|
1486
|
+
return response.json()
|
|
1487
|
+
|
|
1488
|
+
@api_error_handler
|
|
1489
|
+
async def get_summary(self, filters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
1490
|
+
"""Get the summary of a memory export.
|
|
1491
|
+
|
|
1492
|
+
Args:
|
|
1493
|
+
filters: Optional filters to apply to the summary request
|
|
1494
|
+
|
|
1495
|
+
Returns:
|
|
1496
|
+
Dict containing the export status and summary data
|
|
1497
|
+
"""
|
|
1498
|
+
|
|
1499
|
+
response = await self.async_client.post("/v1/summary/", json=self._prepare_params({"filters": filters}))
|
|
1500
|
+
response.raise_for_status()
|
|
1501
|
+
capture_client_event("client.get_summary", self, {"sync_type": "async"})
|
|
1502
|
+
return response.json()
|
|
1503
|
+
|
|
1504
|
+
@api_error_handler
|
|
1505
|
+
async def get_project(self, fields: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
1506
|
+
"""Get instructions or categories for the current project.
|
|
1507
|
+
|
|
1508
|
+
Args:
|
|
1509
|
+
fields: List of fields to retrieve
|
|
1510
|
+
|
|
1511
|
+
Returns:
|
|
1512
|
+
Dictionary containing the requested fields.
|
|
1513
|
+
|
|
1514
|
+
Raises:
|
|
1515
|
+
ValidationError: If the input data is invalid.
|
|
1516
|
+
AuthenticationError: If authentication fails.
|
|
1517
|
+
RateLimitError: If rate limits are exceeded.
|
|
1518
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1519
|
+
NetworkError: If network connectivity issues occur.
|
|
1520
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1521
|
+
ValueError: If org_id or project_id are not set.
|
|
1522
|
+
"""
|
|
1523
|
+
logger.warning(
|
|
1524
|
+
"get_project() method is going to be deprecated in version v1.0 of the package. Please use the client.project.get() method instead."
|
|
1525
|
+
)
|
|
1526
|
+
if not (self.org_id and self.project_id):
|
|
1527
|
+
raise ValueError("org_id and project_id must be set to access instructions or categories")
|
|
1528
|
+
|
|
1529
|
+
params = self._prepare_params({"fields": fields})
|
|
1530
|
+
response = await self.async_client.get(
|
|
1531
|
+
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
|
1532
|
+
params=params,
|
|
1533
|
+
)
|
|
1534
|
+
response.raise_for_status()
|
|
1535
|
+
capture_client_event("client.get_project", self, {"fields": fields, "sync_type": "async"})
|
|
1536
|
+
return response.json()
|
|
1537
|
+
|
|
1538
|
+
@api_error_handler
|
|
1539
|
+
async def update_project(
|
|
1540
|
+
self,
|
|
1541
|
+
custom_instructions: Optional[str] = None,
|
|
1542
|
+
custom_categories: Optional[List[str]] = None,
|
|
1543
|
+
retrieval_criteria: Optional[List[Dict[str, Any]]] = None,
|
|
1544
|
+
enable_graph: Optional[bool] = None,
|
|
1545
|
+
version: Optional[str] = None,
|
|
1546
|
+
) -> Dict[str, Any]:
|
|
1547
|
+
"""Update the project settings.
|
|
1548
|
+
|
|
1549
|
+
Args:
|
|
1550
|
+
custom_instructions: New instructions for the project
|
|
1551
|
+
custom_categories: New categories for the project
|
|
1552
|
+
retrieval_criteria: New retrieval criteria for the project
|
|
1553
|
+
enable_graph: Enable or disable the graph for the project
|
|
1554
|
+
version: Version of the project
|
|
1555
|
+
|
|
1556
|
+
Returns:
|
|
1557
|
+
Dictionary containing the API response.
|
|
1558
|
+
|
|
1559
|
+
Raises:
|
|
1560
|
+
ValidationError: If the input data is invalid.
|
|
1561
|
+
AuthenticationError: If authentication fails.
|
|
1562
|
+
RateLimitError: If rate limits are exceeded.
|
|
1563
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1564
|
+
NetworkError: If network connectivity issues occur.
|
|
1565
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1566
|
+
ValueError: If org_id or project_id are not set.
|
|
1567
|
+
"""
|
|
1568
|
+
logger.warning(
|
|
1569
|
+
"update_project() method is going to be deprecated in version v1.0 of the package. Please use the client.project.update() method instead."
|
|
1570
|
+
)
|
|
1571
|
+
if not (self.org_id and self.project_id):
|
|
1572
|
+
raise ValueError("org_id and project_id must be set to update instructions or categories")
|
|
1573
|
+
|
|
1574
|
+
if (
|
|
1575
|
+
custom_instructions is None
|
|
1576
|
+
and custom_categories is None
|
|
1577
|
+
and retrieval_criteria is None
|
|
1578
|
+
and enable_graph is None
|
|
1579
|
+
and version is None
|
|
1580
|
+
):
|
|
1581
|
+
raise ValueError(
|
|
1582
|
+
"Currently we only support updating custom_instructions or custom_categories or retrieval_criteria, so you must provide at least one of them"
|
|
1583
|
+
)
|
|
1584
|
+
|
|
1585
|
+
payload = self._prepare_params(
|
|
1586
|
+
{
|
|
1587
|
+
"custom_instructions": custom_instructions,
|
|
1588
|
+
"custom_categories": custom_categories,
|
|
1589
|
+
"retrieval_criteria": retrieval_criteria,
|
|
1590
|
+
"enable_graph": enable_graph,
|
|
1591
|
+
"version": version,
|
|
1592
|
+
}
|
|
1593
|
+
)
|
|
1594
|
+
response = await self.async_client.patch(
|
|
1595
|
+
f"/api/v1/orgs/organizations/{self.org_id}/projects/{self.project_id}/",
|
|
1596
|
+
json=payload,
|
|
1597
|
+
)
|
|
1598
|
+
response.raise_for_status()
|
|
1599
|
+
capture_client_event(
|
|
1600
|
+
"client.update_project",
|
|
1601
|
+
self,
|
|
1602
|
+
{
|
|
1603
|
+
"custom_instructions": custom_instructions,
|
|
1604
|
+
"custom_categories": custom_categories,
|
|
1605
|
+
"retrieval_criteria": retrieval_criteria,
|
|
1606
|
+
"enable_graph": enable_graph,
|
|
1607
|
+
"version": version,
|
|
1608
|
+
"sync_type": "async",
|
|
1609
|
+
},
|
|
1610
|
+
)
|
|
1611
|
+
return response.json()
|
|
1612
|
+
|
|
1613
|
+
async def chat(self):
|
|
1614
|
+
"""Start a chat with the Mem0 AI. (Not implemented)
|
|
1615
|
+
|
|
1616
|
+
Raises:
|
|
1617
|
+
NotImplementedError: This method is not implemented yet.
|
|
1618
|
+
"""
|
|
1619
|
+
raise NotImplementedError("Chat is not implemented yet")
|
|
1620
|
+
|
|
1621
|
+
@api_error_handler
|
|
1622
|
+
async def get_webhooks(self, project_id: str) -> Dict[str, Any]:
|
|
1623
|
+
"""Get webhooks configuration for the project.
|
|
1624
|
+
|
|
1625
|
+
Args:
|
|
1626
|
+
project_id: The ID of the project to get webhooks for.
|
|
1627
|
+
|
|
1628
|
+
Returns:
|
|
1629
|
+
Dictionary containing webhook details.
|
|
1630
|
+
|
|
1631
|
+
Raises:
|
|
1632
|
+
ValidationError: If the input data is invalid.
|
|
1633
|
+
AuthenticationError: If authentication fails.
|
|
1634
|
+
RateLimitError: If rate limits are exceeded.
|
|
1635
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1636
|
+
NetworkError: If network connectivity issues occur.
|
|
1637
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1638
|
+
ValueError: If project_id is not set.
|
|
1639
|
+
"""
|
|
1640
|
+
|
|
1641
|
+
response = await self.async_client.get(f"api/v1/webhooks/projects/{project_id}/")
|
|
1642
|
+
response.raise_for_status()
|
|
1643
|
+
capture_client_event("client.get_webhook", self, {"sync_type": "async"})
|
|
1644
|
+
return response.json()
|
|
1645
|
+
|
|
1646
|
+
@api_error_handler
|
|
1647
|
+
async def create_webhook(self, url: str, name: str, project_id: str, event_types: List[str]) -> Dict[str, Any]:
|
|
1648
|
+
"""Create a webhook for the current project.
|
|
1649
|
+
|
|
1650
|
+
Args:
|
|
1651
|
+
url: The URL to send the webhook to.
|
|
1652
|
+
name: The name of the webhook.
|
|
1653
|
+
event_types: List of event types to trigger the webhook for.
|
|
1654
|
+
|
|
1655
|
+
Returns:
|
|
1656
|
+
Dictionary containing the created webhook details.
|
|
1657
|
+
|
|
1658
|
+
Raises:
|
|
1659
|
+
ValidationError: If the input data is invalid.
|
|
1660
|
+
AuthenticationError: If authentication fails.
|
|
1661
|
+
RateLimitError: If rate limits are exceeded.
|
|
1662
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1663
|
+
NetworkError: If network connectivity issues occur.
|
|
1664
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1665
|
+
ValueError: If project_id is not set.
|
|
1666
|
+
"""
|
|
1667
|
+
|
|
1668
|
+
payload = {"url": url, "name": name, "event_types": event_types}
|
|
1669
|
+
response = await self.async_client.post(f"api/v1/webhooks/projects/{project_id}/", json=payload)
|
|
1670
|
+
response.raise_for_status()
|
|
1671
|
+
capture_client_event("client.create_webhook", self, {"sync_type": "async"})
|
|
1672
|
+
return response.json()
|
|
1673
|
+
|
|
1674
|
+
@api_error_handler
|
|
1675
|
+
async def update_webhook(
|
|
1676
|
+
self,
|
|
1677
|
+
webhook_id: int,
|
|
1678
|
+
name: Optional[str] = None,
|
|
1679
|
+
url: Optional[str] = None,
|
|
1680
|
+
event_types: Optional[List[str]] = None,
|
|
1681
|
+
) -> Dict[str, Any]:
|
|
1682
|
+
"""Update a webhook configuration.
|
|
1683
|
+
|
|
1684
|
+
Args:
|
|
1685
|
+
webhook_id: ID of the webhook to update
|
|
1686
|
+
name: Optional new name for the webhook
|
|
1687
|
+
url: Optional new URL for the webhook
|
|
1688
|
+
event_types: Optional list of event types to trigger the webhook for.
|
|
1689
|
+
|
|
1690
|
+
Returns:
|
|
1691
|
+
Dictionary containing the updated webhook details.
|
|
1692
|
+
|
|
1693
|
+
Raises:
|
|
1694
|
+
ValidationError: If the input data is invalid.
|
|
1695
|
+
AuthenticationError: If authentication fails.
|
|
1696
|
+
RateLimitError: If rate limits are exceeded.
|
|
1697
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1698
|
+
NetworkError: If network connectivity issues occur.
|
|
1699
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1700
|
+
"""
|
|
1701
|
+
|
|
1702
|
+
payload = {k: v for k, v in {"name": name, "url": url, "event_types": event_types}.items() if v is not None}
|
|
1703
|
+
response = await self.async_client.put(f"api/v1/webhooks/{webhook_id}/", json=payload)
|
|
1704
|
+
response.raise_for_status()
|
|
1705
|
+
capture_client_event("client.update_webhook", self, {"webhook_id": webhook_id, "sync_type": "async"})
|
|
1706
|
+
return response.json()
|
|
1707
|
+
|
|
1708
|
+
@api_error_handler
|
|
1709
|
+
async def delete_webhook(self, webhook_id: int) -> Dict[str, str]:
|
|
1710
|
+
"""Delete a webhook configuration.
|
|
1711
|
+
|
|
1712
|
+
Args:
|
|
1713
|
+
webhook_id: ID of the webhook to delete
|
|
1714
|
+
|
|
1715
|
+
Returns:
|
|
1716
|
+
Dictionary containing success message.
|
|
1717
|
+
|
|
1718
|
+
Raises:
|
|
1719
|
+
ValidationError: If the input data is invalid.
|
|
1720
|
+
AuthenticationError: If authentication fails.
|
|
1721
|
+
RateLimitError: If rate limits are exceeded.
|
|
1722
|
+
MemoryQuotaExceededError: If memory quota is exceeded.
|
|
1723
|
+
NetworkError: If network connectivity issues occur.
|
|
1724
|
+
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
|
|
1725
|
+
"""
|
|
1726
|
+
|
|
1727
|
+
response = await self.async_client.delete(f"api/v1/webhooks/{webhook_id}/")
|
|
1728
|
+
response.raise_for_status()
|
|
1729
|
+
capture_client_event("client.delete_webhook", self, {"webhook_id": webhook_id, "sync_type": "async"})
|
|
1730
|
+
return response.json()
|
|
1731
|
+
|
|
1732
|
+
@api_error_handler
|
|
1733
|
+
async def feedback(
|
|
1734
|
+
self, memory_id: str, feedback: Optional[str] = None, feedback_reason: Optional[str] = None
|
|
1735
|
+
) -> Dict[str, str]:
|
|
1736
|
+
VALID_FEEDBACK_VALUES = {"POSITIVE", "NEGATIVE", "VERY_NEGATIVE"}
|
|
1737
|
+
|
|
1738
|
+
feedback = feedback.upper() if feedback else None
|
|
1739
|
+
if feedback is not None and feedback not in VALID_FEEDBACK_VALUES:
|
|
1740
|
+
raise ValueError(f"feedback must be one of {', '.join(VALID_FEEDBACK_VALUES)} or None")
|
|
1741
|
+
|
|
1742
|
+
data = {"memory_id": memory_id, "feedback": feedback, "feedback_reason": feedback_reason}
|
|
1743
|
+
|
|
1744
|
+
response = await self.async_client.post("/v1/feedback/", json=data)
|
|
1745
|
+
response.raise_for_status()
|
|
1746
|
+
capture_client_event("client.feedback", self, data, {"sync_type": "async"})
|
|
1747
|
+
return response.json()
|