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