diff --git a/agentops/instrumentation/__init__.py b/agentops/instrumentation/__init__.py
index e45995144..84ace1d6d 100644
--- a/agentops/instrumentation/__init__.py
+++ b/agentops/instrumentation/__init__.py
@@ -67,6 +67,12 @@ class InstrumentorConfig(TypedDict):
"min_version": "0.1.0",
"package_name": "google-genai", # Actual pip package name
},
+ "mem0": {
+ "module_name": "agentops.instrumentation.mem0",
+ "class_name": "Mem0Instrumentor",
+ "min_version": "0.1.0",
+ "package_name": "mem0ai",
+ },
}
# Configuration for utility instrumentors
diff --git a/agentops/instrumentation/mem0/__init__.py b/agentops/instrumentation/mem0/__init__.py
new file mode 100644
index 000000000..ababf20c9
--- /dev/null
+++ b/agentops/instrumentation/mem0/__init__.py
@@ -0,0 +1,53 @@
+"""Mem0 instrumentation library for AgentOps.
+
+This package provides instrumentation for the Mem0 memory management system,
+capturing telemetry data for memory operations.
+"""
+
+import logging
+
+# Import memory operation wrappers
+from .memory import (
+ mem0_add_wrapper,
+ mem0_search_wrapper,
+ mem0_get_all_wrapper,
+ mem0_get_wrapper,
+ mem0_delete_wrapper,
+ mem0_update_wrapper,
+ mem0_delete_all_wrapper,
+ mem0_history_wrapper,
+)
+
+
+def get_version() -> str:
+ try:
+ from importlib.metadata import version
+
+ return version("mem0ai")
+ except ImportError:
+ logger.debug("Could not find Mem0 SDK version")
+ return "unknown"
+
+
+LIBRARY_NAME = "agentops.instrumentation.mem0"
+LIBRARY_VERSION = "1.0.0"
+
+logger = logging.getLogger(__name__)
+
+# Import after defining constants to avoid circular imports
+from agentops.instrumentation.mem0.instrumentor import Mem0Instrumentor # noqa: E402
+
+__all__ = [
+ "LIBRARY_NAME",
+ "LIBRARY_VERSION",
+ "Mem0Instrumentor",
+ # Memory operation wrappers
+ "mem0_add_wrapper",
+ "mem0_search_wrapper",
+ "mem0_get_all_wrapper",
+ "mem0_get_wrapper",
+ "mem0_delete_wrapper",
+ "mem0_update_wrapper",
+ "mem0_delete_all_wrapper",
+ "mem0_history_wrapper",
+]
diff --git a/agentops/instrumentation/mem0/common.py b/agentops/instrumentation/mem0/common.py
new file mode 100644
index 000000000..6fda783a5
--- /dev/null
+++ b/agentops/instrumentation/mem0/common.py
@@ -0,0 +1,377 @@
+"""Common utilities and base wrapper functions for Mem0 instrumentation."""
+
+from typing import Dict, Any
+from opentelemetry import context as context_api
+from opentelemetry.trace import SpanKind, Status, StatusCode
+
+from agentops.instrumentation.common.attributes import AttributeMap
+from agentops.semconv import SpanAttributes, LLMRequestTypeValues
+
+
+def get_common_attributes() -> AttributeMap:
+ """Get common instrumentation attributes for Mem0 operations.
+
+ Returns:
+ Dictionary of common Mem0 attributes
+ """
+ attributes = {}
+ attributes[SpanAttributes.LLM_SYSTEM] = "Mem0"
+ return attributes
+
+
+def _extract_common_kwargs_attributes(kwargs: Dict[str, Any]) -> AttributeMap:
+ """Extract common attributes from kwargs that apply to multiple operations.
+
+ Args:
+ kwargs: Keyword arguments from the method call
+
+ Returns:
+ Dictionary of extracted common attributes
+ """
+ attributes = {}
+
+ # Extract user/agent/run IDs
+ for id_type in ["user_id", "agent_id", "run_id"]:
+ if id_type in kwargs and kwargs[id_type]:
+ # Use the new mem0-specific attributes
+ if id_type == "user_id":
+ attributes["mem0.user_id"] = str(kwargs[id_type])
+ elif id_type == "agent_id":
+ attributes["mem0.agent_id"] = str(kwargs[id_type])
+ elif id_type == "run_id":
+ attributes["mem0.run_id"] = str(kwargs[id_type])
+
+ # Extract metadata
+ if "metadata" in kwargs:
+ metadata = kwargs["metadata"]
+ if isinstance(metadata, dict):
+ for key, value in metadata.items():
+ attributes[f"mem0.metadata.{key}"] = str(value)
+
+ return attributes
+
+
+def _extract_memory_response_attributes(return_value: Any) -> AttributeMap:
+ """Extract attributes from memory operation response.
+
+ Args:
+ return_value: The response from the memory operation
+
+ Returns:
+ Dictionary of extracted response attributes
+ """
+ attributes = {}
+
+ if return_value:
+ if isinstance(return_value, dict):
+ # Check if this is an update/delete response (simple message format)
+ if "message" in return_value and len(return_value) == 1:
+ # Handle update/delete operation response
+ attributes["mem0.operation.message"] = return_value["message"]
+ return attributes
+
+ # Check if this is a single memory object (like from get method)
+ if "id" in return_value and "memory" in return_value and "results" not in return_value:
+ # Handle single memory object
+ attributes["mem0.memory_id"] = return_value["id"]
+ attributes["mem0.memory.0.id"] = return_value["id"]
+ attributes["mem0.memory.0.content"] = return_value["memory"]
+ attributes["mem0.results_count"] = 1
+
+ # Extract hash
+ if "hash" in return_value:
+ attributes["mem0.memory.0.hash"] = return_value["hash"]
+
+ # Extract score (might be None for get operations)
+ if "score" in return_value and return_value["score"] is not None:
+ attributes["mem0.memory.0.score"] = str(return_value["score"])
+
+ # Extract metadata
+ if "metadata" in return_value and isinstance(return_value["metadata"], dict):
+ for key, value in return_value["metadata"].items():
+ attributes[f"mem0.memory.0.metadata.{key}"] = str(value)
+
+ # Extract timestamps
+ if "created_at" in return_value:
+ attributes["mem0.memory.0.created_at"] = return_value["created_at"]
+
+ if "updated_at" in return_value and return_value["updated_at"]:
+ attributes["mem0.memory.0.updated_at"] = return_value["updated_at"]
+
+ # Extract user_id
+ if "user_id" in return_value:
+ attributes["mem0.memory.0.user_id"] = return_value["user_id"]
+ attributes["mem0.user_ids"] = return_value["user_id"]
+
+ return attributes
+
+ # Extract status if present
+ if "status" in return_value:
+ attributes["mem0.status"] = str(return_value["status"])
+
+ # Extract results array - this is the main structure from mem0 (add/search operations)
+ if "results" in return_value and isinstance(return_value["results"], list):
+ results = return_value["results"]
+ attributes["mem0.results_count"] = len(results)
+
+ # Extract event types
+ event_types = set()
+ memory_ids = []
+ memory_contents = []
+ scores = []
+ user_ids = set()
+
+ for i, result in enumerate(results):
+ if isinstance(result, dict):
+ # Extract event type
+ if "event" in result:
+ event_types.add(result["event"])
+
+ # Extract memory ID
+ if "id" in result:
+ memory_ids.append(result["id"])
+ # Set individual memory ID attributes
+ attributes[f"mem0.memory.{i}.id"] = result["id"]
+
+ # Extract memory content
+ if "memory" in result:
+ memory_contents.append(result["memory"])
+ # Set individual memory content attributes
+ attributes[f"mem0.memory.{i}.content"] = result["memory"]
+
+ # Extract event for individual result
+ if "event" in result:
+ attributes[f"mem0.memory.{i}.event"] = result["event"]
+
+ # Extract hash
+ if "hash" in result:
+ attributes[f"mem0.memory.{i}.hash"] = result["hash"]
+
+ # Extract score (for search results)
+ if "score" in result:
+ scores.append(result["score"])
+ attributes[f"mem0.memory.{i}.score"] = str(result["score"])
+
+ # Extract metadata
+ if "metadata" in result and isinstance(result["metadata"], dict):
+ for key, value in result["metadata"].items():
+ attributes[f"mem0.memory.{i}.metadata.{key}"] = str(value)
+
+ # Extract timestamps
+ if "created_at" in result:
+ attributes[f"mem0.memory.{i}.created_at"] = result["created_at"]
+
+ if "updated_at" in result and result["updated_at"]:
+ attributes[f"mem0.memory.{i}.updated_at"] = result["updated_at"]
+
+ # Extract user_id
+ if "user_id" in result:
+ user_ids.add(result["user_id"])
+ attributes[f"mem0.memory.{i}.user_id"] = result["user_id"]
+
+ # Set aggregated attributes
+ if event_types:
+ attributes["mem0.event_types"] = ",".join(event_types)
+
+ if memory_ids:
+ # Set primary memory ID (first one) as the main memory ID
+ attributes["mem0.memory_id"] = memory_ids[0]
+ # Set all memory IDs as a comma-separated list
+ attributes["mem0.memory.ids"] = ",".join(memory_ids)
+
+ if memory_contents:
+ # Set all memory contents as a combined attribute
+ attributes["mem0.memory.contents"] = " | ".join(memory_contents)
+
+ if scores:
+ # Set average and max scores for search results
+ attributes["mem0.search.avg_score"] = str(sum(scores) / len(scores))
+ attributes["mem0.search.max_score"] = str(max(scores))
+ attributes["mem0.search.min_score"] = str(min(scores))
+
+ if user_ids:
+ # Set user IDs (typically should be the same user for all results)
+ attributes["mem0.user_ids"] = ",".join(user_ids)
+
+ # Extract relations count if present (for backward compatibility)
+ if "relations" in return_value:
+ attributes["mem0.relations_count"] = len(return_value["relations"])
+
+ elif isinstance(return_value, list):
+ # For operations that return lists directly (like search, get_all)
+ attributes["mem0.results_count"] = len(return_value)
+
+ # If it's a list of memory objects, extract similar attributes
+ for i, item in enumerate(return_value):
+ if isinstance(item, dict):
+ if "id" in item:
+ attributes[f"mem0.memory.{i}.id"] = item["id"]
+ if "memory" in item:
+ attributes[f"mem0.memory.{i}.content"] = item["memory"]
+ if "event" in item:
+ attributes[f"mem0.memory.{i}.event"] = item["event"]
+ if "hash" in item:
+ attributes[f"mem0.memory.{i}.hash"] = item["hash"]
+ if "score" in item:
+ attributes[f"mem0.memory.{i}.score"] = str(item["score"])
+ if "user_id" in item:
+ attributes[f"mem0.memory.{i}.user_id"] = item["user_id"]
+
+ return attributes
+
+
+def create_mem0_wrapper(operation_name: str, attribute_extractor):
+ """Create a wrapper function for Mem0 operations that ensures proper span hierarchy.
+
+ This function creates wrappers that explicitly use the current context to ensure
+ mem0 spans are properly nested within the current AgentOps session or OpenAI spans.
+
+ Args:
+ operation_name: Name of the mem0 operation (add, search, etc.)
+ attribute_extractor: Function to extract attributes for this operation
+
+ Returns:
+ A wrapper function that creates properly nested spans
+ """
+
+ def wrapper(tracer):
+ def actual_wrapper(wrapped, instance, args, kwargs):
+ # Skip instrumentation if suppressed
+ from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
+
+ if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):
+ return wrapped(*args, **kwargs)
+
+ # Get current context to ensure proper parent-child relationship
+ current_context = context_api.get_current()
+ span = tracer.start_span(
+ f"mem0.memory.{operation_name}",
+ context=current_context,
+ kind=SpanKind.CLIENT,
+ attributes={SpanAttributes.LLM_REQUEST_TYPE: LLMRequestTypeValues.CHAT.value},
+ )
+
+ return_value = None
+ try:
+ # Add the input attributes to the span before execution
+ attributes = attribute_extractor(args=args, kwargs=kwargs)
+ for key, value in attributes.items():
+ span.set_attribute(key, value)
+
+ return_value = wrapped(*args, **kwargs)
+ # Add the output attributes to the span after execution
+ attributes = attribute_extractor(return_value=return_value)
+ for key, value in attributes.items():
+ span.set_attribute(key, value)
+
+ span.set_status(Status(StatusCode.OK))
+ except Exception as e:
+ # Add everything we have in the case of an error
+ attributes = attribute_extractor(args=args, kwargs=kwargs, return_value=return_value)
+ for key, value in attributes.items():
+ span.set_attribute(key, value)
+
+ span.record_exception(e)
+ span.set_status(Status(StatusCode.ERROR, str(e)))
+ raise
+ finally:
+ span.end()
+
+ return return_value
+
+ return actual_wrapper
+
+ return wrapper
+
+
+def create_async_mem0_wrapper(operation_name: str, attribute_extractor):
+ """Create an async wrapper function for Mem0 operations that ensures proper span hierarchy.
+
+ This function creates async wrappers that explicitly use the current context to ensure
+ mem0 spans are properly nested within the current AgentOps session or OpenAI spans.
+
+ Args:
+ operation_name: Name of the mem0 operation (add, search, etc.)
+ attribute_extractor: Function to extract attributes for this operation
+
+ Returns:
+ An async wrapper function that creates properly nested spans
+ """
+
+ def wrapper(tracer):
+ def actual_wrapper(wrapped, instance, args, kwargs):
+ # Skip instrumentation if suppressed
+ from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
+
+ if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):
+ return wrapped(*args, **kwargs)
+
+ async def async_wrapper():
+ # Get current context to ensure proper parent-child relationship
+ current_context = context_api.get_current()
+ span = tracer.start_span(
+ f"mem0.AsyncMemory.{operation_name}",
+ context=current_context,
+ kind=SpanKind.CLIENT,
+ attributes={SpanAttributes.LLM_REQUEST_TYPE: LLMRequestTypeValues.CHAT.value},
+ )
+
+ return_value = None
+ try:
+ # Add the input attributes to the span before execution
+ attributes = attribute_extractor(args=args, kwargs=kwargs)
+ for key, value in attributes.items():
+ span.set_attribute(key, value)
+
+ return_value = await wrapped(*args, **kwargs)
+
+ # Add the output attributes to the span after execution
+ attributes = attribute_extractor(return_value=return_value)
+ for key, value in attributes.items():
+ span.set_attribute(key, value)
+
+ span.set_status(Status(StatusCode.OK))
+ except Exception as e:
+ # Add everything we have in the case of an error
+ attributes = attribute_extractor(args=args, kwargs=kwargs, return_value=return_value)
+ for key, value in attributes.items():
+ span.set_attribute(key, value)
+
+ span.record_exception(e)
+ span.set_status(Status(StatusCode.ERROR, str(e)))
+ raise
+ finally:
+ span.end()
+
+ return return_value
+
+ return async_wrapper()
+
+ return actual_wrapper
+
+ return wrapper
+
+
+def create_universal_mem0_wrapper(operation_name: str, attribute_extractor):
+ """Create a universal wrapper that handles both sync and async methods.
+
+ This function detects whether the wrapped method is async and applies the appropriate wrapper.
+ """
+
+ def wrapper(tracer):
+ def actual_wrapper(wrapped, instance, args, kwargs):
+ import asyncio
+
+ # Check if the wrapped function is async
+ if asyncio.iscoroutinefunction(wrapped):
+ # Use async wrapper
+ async_wrapper_func = create_async_mem0_wrapper(operation_name, attribute_extractor)
+ return async_wrapper_func(tracer)(wrapped, instance, args, kwargs)
+ else:
+ # Use sync wrapper
+ sync_wrapper_func = create_mem0_wrapper(operation_name, attribute_extractor)
+ return sync_wrapper_func(tracer)(wrapped, instance, args, kwargs)
+
+ return actual_wrapper
+
+ return wrapper
diff --git a/agentops/instrumentation/mem0/instrumentor.py b/agentops/instrumentation/mem0/instrumentor.py
new file mode 100644
index 000000000..51a0dac60
--- /dev/null
+++ b/agentops/instrumentation/mem0/instrumentor.py
@@ -0,0 +1,277 @@
+from typing import Collection
+from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
+from opentelemetry.trace import get_tracer
+from opentelemetry.metrics import get_meter
+from wrapt import wrap_function_wrapper
+
+from agentops.instrumentation.mem0 import LIBRARY_NAME, LIBRARY_VERSION
+from agentops.logging import logger
+
+# Import from refactored structure
+from .memory import (
+ mem0_add_wrapper,
+ mem0_search_wrapper,
+ mem0_get_all_wrapper,
+ mem0_delete_wrapper,
+ mem0_update_wrapper,
+ mem0_get_wrapper,
+ mem0_delete_all_wrapper,
+ mem0_history_wrapper,
+)
+
+from agentops.semconv import Meters
+
+# Methods to wrap for instrumentation using specialized wrappers
+WRAPPER_METHODS = [
+ # Sync Memory class methods
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.add",
+ "wrapper": mem0_add_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.search",
+ "wrapper": mem0_search_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.get_all",
+ "wrapper": mem0_get_all_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.get",
+ "wrapper": mem0_get_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.delete",
+ "wrapper": mem0_delete_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.delete_all",
+ "wrapper": mem0_delete_all_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.update",
+ "wrapper": mem0_update_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "Memory.history",
+ "wrapper": mem0_history_wrapper,
+ },
+ # MemoryClient class methods
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.add",
+ "wrapper": mem0_add_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.search",
+ "wrapper": mem0_search_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.get_all",
+ "wrapper": mem0_get_all_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.get",
+ "wrapper": mem0_get_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.delete",
+ "wrapper": mem0_delete_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.delete_all",
+ "wrapper": mem0_delete_all_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "MemoryClient.update",
+ "wrapper": mem0_update_wrapper,
+ },
+ # AsyncMemoryClient class methods
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.add",
+ "wrapper": mem0_add_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.search",
+ "wrapper": mem0_search_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.get_all",
+ "wrapper": mem0_get_all_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.get",
+ "wrapper": mem0_get_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.delete",
+ "wrapper": mem0_delete_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.delete_all",
+ "wrapper": mem0_delete_all_wrapper,
+ },
+ {
+ "package": "mem0.client.main",
+ "class_method": "AsyncMemoryClient.update",
+ "wrapper": mem0_update_wrapper,
+ },
+ # AsyncMemory class methods
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.add",
+ "wrapper": mem0_add_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.search",
+ "wrapper": mem0_search_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.get_all",
+ "wrapper": mem0_get_all_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.get",
+ "wrapper": mem0_get_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.delete",
+ "wrapper": mem0_delete_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.delete_all",
+ "wrapper": mem0_delete_all_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.update",
+ "wrapper": mem0_update_wrapper,
+ },
+ {
+ "package": "mem0.memory.main",
+ "class_method": "AsyncMemory.history",
+ "wrapper": mem0_history_wrapper,
+ },
+]
+
+
+class Mem0Instrumentor(BaseInstrumentor):
+ """An instrumentor for Mem0's client library.
+
+ This class provides instrumentation for Mem0's memory operations by wrapping key methods
+ in the Memory, AsyncMemory, MemoryClient, and AsyncMemoryClient classes. It captures
+ telemetry data for memory operations including add, search, get, delete, delete_all,
+ update, and history operations.
+
+ The instrumentor gracefully handles missing optional dependencies - if a provider's
+ package is not installed, it will be skipped without causing errors.
+
+ It captures metrics including operation duration, memory counts, and exceptions.
+ """
+
+ def instrumentation_dependencies(self) -> Collection[str]:
+ """Return packages required for instrumentation.
+
+ Returns:
+ A collection of package specifications required for this instrumentation.
+ """
+ return ["mem0ai >= 0.1.10"]
+
+ def _instrument(self, **kwargs):
+ """Instrument the Mem0 Memory API.
+
+ This method wraps the key methods in the Mem0 Memory client to capture
+ telemetry data for memory operations. It sets up tracers, meters, and wraps the
+ appropriate methods for instrumentation.
+
+ Args:
+ **kwargs: Configuration options for instrumentation.
+ """
+ super()._instrument(**kwargs)
+ logger.debug("Starting Mem0 instrumentation...")
+
+ tracer_provider = kwargs.get("tracer_provider")
+ tracer = get_tracer(LIBRARY_NAME, LIBRARY_VERSION, tracer_provider)
+
+ meter_provider = kwargs.get("meter_provider")
+ meter = get_meter(LIBRARY_NAME, LIBRARY_VERSION, meter_provider)
+
+ # Create metrics for memory operations
+ meter.create_histogram(
+ name=Meters.LLM_OPERATION_DURATION,
+ unit="s",
+ description="Mem0 memory operation duration",
+ )
+
+ meter.create_counter(
+ name=Meters.LLM_COMPLETIONS_EXCEPTIONS,
+ unit="time",
+ description="Number of exceptions occurred during Mem0 operations",
+ )
+
+ meter.create_histogram(
+ name="mem0.memory.count",
+ unit="memory",
+ description="Number of memories processed in Mem0 operations",
+ )
+
+ # Use specialized wrappers that ensure proper context hierarchy
+ for method_config in WRAPPER_METHODS:
+ try:
+ package = method_config["package"]
+ class_method = method_config["class_method"]
+ wrapper_func = method_config["wrapper"]
+ wrap_function_wrapper(package, class_method, wrapper_func(tracer))
+ except (AttributeError, ModuleNotFoundError) as e:
+ # Use debug level for missing optional packages instead of error
+ # since LLM providers are optional dependencies
+ logger.debug(f"Skipping {package}.{class_method} - package not installed: {e}")
+ except Exception as e:
+ # Log unexpected errors as warnings
+ logger.warning(f"Unexpected error wrapping {package}.{class_method}: {e}")
+ logger.debug("Mem0 instrumentation completed")
+
+ def _uninstrument(self, **kwargs):
+ """Remove instrumentation from Mem0 Memory API.
+
+ This method unwraps all methods that were wrapped during instrumentation,
+ restoring the original behavior of the Mem0 Memory API.
+
+ Args:
+ **kwargs: Configuration options for uninstrumentation.
+ """
+ # Unwrap specialized methods
+ from opentelemetry.instrumentation.utils import unwrap
+
+ for method_config in WRAPPER_METHODS:
+ try:
+ package = method_config["package"]
+ class_method = method_config["class_method"]
+ unwrap(package, class_method)
+ except Exception as e:
+ logger.debug(f"Failed to unwrap {package}.{class_method}: {e}")
diff --git a/agentops/instrumentation/mem0/memory.py b/agentops/instrumentation/mem0/memory.py
new file mode 100644
index 000000000..04932c1c9
--- /dev/null
+++ b/agentops/instrumentation/mem0/memory.py
@@ -0,0 +1,430 @@
+"""Memory operation attribute extractors and wrappers for Mem0 instrumentation."""
+
+from typing import Optional, Tuple, Dict, Any
+
+from agentops.instrumentation.common.attributes import AttributeMap
+from agentops.semconv import SpanAttributes, LLMRequestTypeValues, MessageAttributes
+from .common import (
+ get_common_attributes,
+ _extract_common_kwargs_attributes,
+ _extract_memory_response_attributes,
+ create_universal_mem0_wrapper,
+)
+
+
+def get_add_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's add method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ print(f"args: {args}")
+ print(f"kwargs: {kwargs}")
+ print(f"return_value: {return_value}")
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "add"
+ attributes[SpanAttributes.LLM_REQUEST_TYPE] = LLMRequestTypeValues.CHAT.value
+
+ # Extract message content from args
+ if args and len(args) > 0:
+ messages = args[0]
+ # Get user_id from kwargs for speaker, default to "user" if not found
+ speaker = kwargs.get("user_id", "user") if kwargs else "user"
+
+ if isinstance(messages, str):
+ attributes["mem0.message"] = messages
+ # Set as prompt for consistency with LLM patterns
+ attributes[MessageAttributes.PROMPT_CONTENT.format(i=0)] = messages
+ attributes[MessageAttributes.PROMPT_SPEAKER.format(i=0)] = speaker
+ elif isinstance(messages, list):
+ attributes["mem0.message_count"] = len(messages)
+ # Extract message types if available
+ message_types = set()
+ for i, msg in enumerate(messages):
+ if isinstance(msg, dict):
+ if "role" in msg:
+ message_types.add(msg["role"])
+ attributes[MessageAttributes.PROMPT_ROLE.format(i=i)] = msg["role"]
+ if "content" in msg:
+ attributes[MessageAttributes.PROMPT_CONTENT.format(i=i)] = msg["content"]
+ # Set speaker for each message
+ attributes[MessageAttributes.PROMPT_SPEAKER.format(i=i)] = speaker
+ else:
+ # String message
+ attributes[MessageAttributes.PROMPT_CONTENT.format(i=i)] = str(msg)
+
+ attributes[MessageAttributes.PROMPT_SPEAKER.format(i=i)] = speaker
+ if message_types:
+ attributes["mem0.message_types"] = ",".join(message_types)
+
+ # Extract kwargs attributes
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract memory type
+ if "memory_type" in kwargs:
+ attributes["mem0.memory_type"] = str(kwargs["memory_type"])
+
+ # Extract inference flag
+ if "infer" in kwargs:
+ attributes[SpanAttributes.MEM0_INFER] = str(kwargs["infer"])
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_search_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's search method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ print(f"get_search_attributes args: {args}")
+ print(f"get_search_attributes kwargs: {kwargs}")
+ print(f"get_search_attributes return_value: {return_value}")
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "search"
+ attributes[SpanAttributes.LLM_REQUEST_TYPE] = LLMRequestTypeValues.CHAT.value
+
+ # Extract search query from args
+ if args and len(args) > 0:
+ query = args[0]
+ # Get user_id from kwargs for speaker, default to "user" if not found
+ speaker = kwargs.get("user_id", "user") if kwargs else "user"
+
+ if isinstance(query, str):
+ attributes["mem0.message"] = query
+ # Set as prompt for consistency
+ attributes[MessageAttributes.PROMPT_CONTENT.format(i=0)] = query
+ attributes[MessageAttributes.PROMPT_SPEAKER.format(i=0)] = speaker
+
+ # Extract kwargs attributes
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract memory type
+ if "memory_type" in kwargs:
+ attributes["mem0.memory_type"] = str(kwargs["memory_type"])
+
+ # Extract limit parameter
+ if "limit" in kwargs:
+ attributes["mem0.search.limit"] = str(kwargs["limit"])
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_get_all_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's get_all method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "get_all"
+
+ # Extract kwargs attributes
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract memory type
+ if "memory_type" in kwargs:
+ attributes["mem0.memory_type"] = str(kwargs["memory_type"])
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_get_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's get method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "get"
+
+ # Extract memory ID from args
+ if args and len(args) > 0:
+ memory_id = args[0]
+ if memory_id:
+ attributes["mem0.memory_id"] = str(memory_id)
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_delete_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's delete method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "delete"
+
+ # Extract memory ID from args if available
+ if args and len(args) > 0:
+ memory_id = args[0]
+ if memory_id:
+ attributes["mem0.memory_id"] = str(memory_id)
+
+ # Extract kwargs attributes if available
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract memory type
+ if "memory_type" in kwargs:
+ attributes["mem0.memory_type"] = str(kwargs["memory_type"])
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_update_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's update method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "update"
+
+ # Extract memory ID from args (if available)
+ if args and len(args) > 0:
+ memory_id = args[0]
+ if memory_id:
+ attributes["mem0.memory_id"] = str(memory_id)
+
+ # Extract data from args (if available)
+ if args and len(args) > 1:
+ data = args[1]
+ if isinstance(data, str):
+ attributes["mem0.message"] = data
+ # Set as prompt for consistency
+ attributes[MessageAttributes.PROMPT_CONTENT.format(i=0)] = data
+ elif isinstance(data, dict):
+ # Handle case where data is a dictionary with "memory" key
+ if "memory" in data:
+ memory_text = data["memory"]
+ attributes["mem0.message"] = memory_text
+ # Set as prompt for consistency
+ attributes[MessageAttributes.PROMPT_CONTENT.format(i=0)] = memory_text
+
+ # Extract metadata from data dict if present
+ if "metadata" in data and isinstance(data["metadata"], dict):
+ for key, value in data["metadata"].items():
+ attributes[f"mem0.metadata.{key}"] = str(value)
+
+ # Extract kwargs attributes (if available)
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract memory type
+ if "memory_type" in kwargs:
+ attributes["mem0.memory_type"] = str(kwargs["memory_type"])
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_delete_all_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's delete_all method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "delete_all"
+
+ # Extract kwargs attributes if available
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract memory type
+ if "memory_type" in kwargs:
+ attributes["mem0.memory_type"] = str(kwargs["memory_type"])
+
+ # Extract user_id for tracking which user's memories are being deleted
+ if "user_id" in kwargs:
+ attributes["mem0.delete_all.user_id"] = str(kwargs["user_id"])
+
+ # Extract agent_id for tracking which agent's memories are being deleted
+ if "agent_id" in kwargs:
+ attributes["mem0.delete_all.agent_id"] = str(kwargs["agent_id"])
+
+ # Extract run_id for tracking which run's memories are being deleted
+ if "run_id" in kwargs:
+ attributes["mem0.delete_all.run_id"] = str(kwargs["run_id"])
+
+ # Extract response attributes
+ if return_value:
+ attributes.update(_extract_memory_response_attributes(return_value))
+
+ return attributes
+
+
+def get_history_attributes(
+ args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None, return_value: Optional[Any] = None
+) -> AttributeMap:
+ """Extract attributes for Mem0's history method.
+
+ Args:
+ args: Positional arguments to the method
+ kwargs: Keyword arguments to the method
+ return_value: Return value from the method
+
+ Returns:
+ Dictionary of extracted attributes
+ """
+ attributes = get_common_attributes()
+ attributes[SpanAttributes.OPERATION_NAME] = "history"
+
+ # Extract memory ID from args
+ if args and len(args) > 0:
+ memory_id = args[0]
+ if memory_id:
+ attributes["mem0.memory_id"] = str(memory_id)
+
+ # Extract kwargs attributes if available
+ if kwargs:
+ attributes.update(_extract_common_kwargs_attributes(kwargs))
+
+ # Extract history data from return value
+ if return_value and isinstance(return_value, list):
+ attributes["mem0.history.count"] = len(return_value)
+
+ # Extract event types and other details from history entries
+ event_types = set()
+ actor_ids = set()
+ roles = set()
+
+ for i, entry in enumerate(return_value):
+ if isinstance(entry, dict):
+ # Extract event type
+ if "event" in entry:
+ event_types.add(entry["event"])
+ attributes[f"mem0.history.{i}.event"] = entry["event"]
+
+ # Extract memory content changes
+ if "old_memory" in entry:
+ if entry["old_memory"]:
+ attributes[f"mem0.history.{i}.old_memory"] = entry["old_memory"]
+
+ if "new_memory" in entry:
+ if entry["new_memory"]:
+ attributes[f"mem0.history.{i}.new_memory"] = entry["new_memory"]
+
+ # Extract timestamps
+ if "created_at" in entry:
+ attributes[f"mem0.history.{i}.created_at"] = entry["created_at"]
+
+ if "updated_at" in entry and entry["updated_at"]:
+ attributes[f"mem0.history.{i}.updated_at"] = entry["updated_at"]
+
+ # Extract actor information
+ if "actor_id" in entry and entry["actor_id"]:
+ actor_ids.add(entry["actor_id"])
+ attributes[f"mem0.history.{i}.actor_id"] = entry["actor_id"]
+
+ if "role" in entry and entry["role"]:
+ roles.add(entry["role"])
+ attributes[f"mem0.history.{i}.role"] = entry["role"]
+
+ # Extract deletion status
+ if "is_deleted" in entry:
+ attributes[f"mem0.history.{i}.is_deleted"] = str(entry["is_deleted"])
+
+ # Extract history entry ID
+ if "id" in entry:
+ attributes[f"mem0.history.{i}.id"] = entry["id"]
+
+ # Set aggregated attributes
+ if event_types:
+ attributes["mem0.history.event_types"] = ",".join(event_types)
+
+ if actor_ids:
+ attributes["mem0.history.actor_ids"] = ",".join(actor_ids)
+
+ if roles:
+ attributes["mem0.history.roles"] = ",".join(roles)
+
+ return attributes
+
+
+# Create universal Mem0 wrappers that work for both sync and async operations
+mem0_add_wrapper = create_universal_mem0_wrapper("add", get_add_attributes)
+mem0_search_wrapper = create_universal_mem0_wrapper("search", get_search_attributes)
+mem0_get_all_wrapper = create_universal_mem0_wrapper("get_all", get_get_all_attributes)
+mem0_get_wrapper = create_universal_mem0_wrapper("get", get_get_attributes)
+mem0_delete_wrapper = create_universal_mem0_wrapper("delete", get_delete_attributes)
+mem0_update_wrapper = create_universal_mem0_wrapper("update", get_update_attributes)
+mem0_delete_all_wrapper = create_universal_mem0_wrapper("delete_all", get_delete_all_attributes)
+mem0_history_wrapper = create_universal_mem0_wrapper("history", get_history_attributes)
diff --git a/docs/images/external/mem0/mem0.png b/docs/images/external/mem0/mem0.png
new file mode 100644
index 000000000..c71241f42
Binary files /dev/null and b/docs/images/external/mem0/mem0.png differ
diff --git a/docs/mint.json b/docs/mint.json
index ae2a86df8..38041ae06 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -176,6 +176,7 @@
"v2/integrations/google_generative_ai",
"v2/integrations/langchain",
"v2/integrations/litellm",
+ "v2/integrations/mem0",
"v2/integrations/openai",
"v2/integrations/agents_sdk",
"v2/integrations/smolagents",
diff --git a/docs/v2/examples/examples.mdx b/docs/v2/examples/examples.mdx
index c7aca55a7..545d10386 100644
--- a/docs/v2/examples/examples.mdx
+++ b/docs/v2/examples/examples.mdx
@@ -68,6 +68,10 @@ description: 'Examples of AgentOps with various integrations'
LangChain callback handler integration
+ } iconType="image" href="/v2/examples/mem0">
+ Comprehensive memory operations with Mem0ai
+
+
} iconType="image" href="/v2/integrations/smolagents">
Track HuggingFace's smolagents with AgentOps seamlessly
diff --git a/docs/v2/examples/mem0.mdx b/docs/v2/examples/mem0.mdx
new file mode 100644
index 000000000..1dc8dc781
--- /dev/null
+++ b/docs/v2/examples/mem0.mdx
@@ -0,0 +1,239 @@
+---
+title: 'Mem0 Example'
+description: 'Memory Operations with Mem0'
+---
+{/* SOURCE_FILE: examples/mem0/mem0_memory_example.ipynb */}
+
+_View Notebook on Github_
+
+# Memory Operations with Mem0
+
+This example demonstrates how to use Mem0's memory management capabilities with both synchronous and asynchronous operations to store, search, and manage conversational context and user preferences.
+
+## Overview
+
+This example showcases practical memory management operations where we:
+
+1. **Initialize Mem0 Memory instances** for both sync and async operations
+2. **Store conversation history** and user preferences with metadata
+3. **Search memories** using natural language queries
+4. **Compare performance** between synchronous and asynchronous memory operations
+
+By using async operations, you can perform multiple memory operations simultaneously instead of waiting for each one to complete sequentially. This is particularly beneficial when dealing with multiple memory additions or searches.
+
+
+
+
+## Setup and Imports
+## Installation
+
+ ```bash pip
+ pip install agentops mem0ai python-dotenv
+ ```
+ ```bash poetry
+ poetry add agentops mem0ai python-dotenv
+ ```
+ ```bash uv
+ uv add agentops mem0ai python-dotenv
+ ```
+
+
+
+Import the required libraries for local memory management with Mem0. We'll use both Memory and AsyncMemory classes to demonstrate different execution patterns for memory operations.
+
+
+
+```python
+
+from mem0 import Memory, AsyncMemory
+import os
+import asyncio
+import logging
+from dotenv import load_dotenv
+import agentops
+```
+
+## Environment Configuration
+
+Set up environment variables for API keys. These are essential for authenticating with AgentOps for tracing and OpenAI for the language model used in memory operations.
+
+
+
+```python
+os.environ["AGENTOPS_API_KEY"] = os.getenv("AGENTOPS_API_KEY")
+os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
+```
+
+## Configuration and Sample Data
+
+Set up the configuration for local memory storage and define sample user data. The configuration specifies the LLM provider and model settings for processing memories.
+
+
+
+```python
+local_config = {
+ "llm": {
+ "provider": "openai",
+ "config": {
+ "model": "gpt-4o-mini",
+ "temperature": 0.1,
+ "max_tokens": 2000,
+ },
+ }
+}
+user_id = "alice_demo"
+agent_id = "assistant_demo"
+run_id = "session_001"
+
+sample_messages = [
+ {"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
+ {"role": "assistant", "content": "How about a thriller? They can be quite engaging."},
+ {"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
+ {
+ "role": "assistant",
+ "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.",
+ },
+]
+
+sample_preferences = [
+ "I prefer dark roast coffee over light roast",
+ "I exercise every morning at 6 AM",
+ "I'm vegetarian and avoid all meat products",
+ "I love reading science fiction novels",
+ "I work in software engineering",
+]
+```
+
+## Synchronous Memory Operations
+
+This function demonstrates sequential memory operations using the synchronous Memory class. While straightforward to implement, each operation must complete before the next begins, which can impact performance.
+
+
+
+```python
+def demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id):
+ """
+ Demonstrate synchronous Memory class operations.
+ """
+
+ agentops.start_trace("mem0_memory_example", tags=["mem0_memory_example"])
+ try:
+
+ memory = Memory.from_config(local_config)
+
+ result = memory.add(
+ sample_messages, user_id=user_id, metadata={"category": "movie_preferences", "session": "demo"}
+ )
+
+ for i, preference in enumerate(sample_preferences):
+ result = memory.add(preference, user_id=user_id, metadata={"type": "preference", "index": i})
+
+ search_queries = [
+ "What movies does the user like?",
+ "What are the user's food preferences?",
+ "When does the user exercise?",
+ ]
+
+ for query in search_queries:
+ results = memory.search(query, user_id=user_id)
+
+ if results and "results" in results:
+ for j, result in enumerate(results):
+ print(f"Result {j+1}: {result.get('memory', 'N/A')}")
+ else:
+ print("No results found")
+
+ all_memories = memory.get_all(user_id=user_id)
+ if all_memories and "results" in all_memories:
+ print(f"Total memories: {len(all_memories['results'])}")
+
+ delete_all_result = memory.delete_all(user_id=user_id)
+ print(f"Delete all result: {delete_all_result}")
+
+ agentops.end_trace(end_state="success")
+ except Exception as e:
+ agentops.end_trace(end_state="error")
+```
+
+## Asynchronous Memory Operations
+
+This function showcases concurrent memory operations using AsyncMemory. By leveraging asyncio.gather(), multiple operations execute simultaneously, significantly reducing total execution time for I/O-bound tasks.
+
+
+
+```python
+async def demonstrate_async_memory(local_config, sample_messages, sample_preferences, user_id):
+ """
+ Demonstrate asynchronous Memory class operations with concurrent execution.
+ """
+
+ agentops.start_trace("mem0_memory_async_example", tags=["mem0_memory_async_example"])
+ try:
+
+ async_memory = await AsyncMemory.from_config(local_config)
+
+ result = await async_memory.add(
+ sample_messages, user_id=user_id, metadata={"category": "async_movie_preferences", "session": "async_demo"}
+ )
+
+ async def add_preference(preference, index):
+ """Helper function to add a single preference asynchronously."""
+ return await async_memory.add(
+ preference, user_id=user_id, metadata={"type": "async_preference", "index": index}
+ )
+
+ tasks = [add_preference(pref, i) for i, pref in enumerate(sample_preferences)]
+ results = await asyncio.gather(*tasks)
+ for i, result in enumerate(results):
+ print(f"Added async preference {i+1}: {result}")
+
+ search_queries = [
+ "What movies does the user like?",
+ "What are the user's dietary restrictions?",
+ "What does the user do for work?",
+ ]
+
+ async def search_memory(query):
+ """Helper function to perform async memory search."""
+ return await async_memory.search(query, user_id=user_id), query
+
+ search_tasks = [search_memory(query) for query in search_queries]
+ search_results = await asyncio.gather(*search_tasks)
+
+ for result, query in search_results:
+ if result and "results" in result:
+ for j, res in enumerate(result["results"]):
+ print(f"Result {j+1}: {res.get('memory', 'N/A')}")
+ else:
+ print("No results found")
+
+ all_memories = await async_memory.get_all(user_id=user_id)
+ if all_memories and "results" in all_memories:
+ print(f"Total async memories: {len(all_memories['results'])}")
+
+ delete_all_result = await async_memory.delete_all(user_id=user_id)
+ print(f"Delete all result: {delete_all_result}")
+
+ agentops.end_trace(end_state="success")
+
+ except Exception as e:
+ agentops.end_trace(end_state="error")
+```
+
+## Execute Demonstrations
+
+Run both synchronous and asynchronous demonstrations to compare their execution patterns and performance. The async version demonstrates the benefits of concurrent execution for multiple memory operations.
+
+
+
+```python
+# Execute both sync and async demonstrations
+demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id)
+await demonstrate_async_memory(local_config, sample_messages, sample_preferences, user_id)
+```
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v2/integrations/mem0.mdx b/docs/v2/integrations/mem0.mdx
new file mode 100644
index 000000000..562ed3b49
--- /dev/null
+++ b/docs/v2/integrations/mem0.mdx
@@ -0,0 +1,153 @@
+---
+title: 'Mem0'
+description: 'Track and monitor Mem0 memory operations with AgentOps'
+---
+
+[Mem0](https://mem0.ai/) provides a smart memory layer for AI applications, enabling personalized interactions by remembering user preferences, conversation history, and context across sessions.
+
+## Why Track Mem0 with AgentOps?
+
+When building memory-powered AI applications, you need visibility into:
+- **Memory Operations**: Track when memories are created, updated, or retrieved
+- **Search Performance**: Monitor how effectively your AI finds relevant memories
+- **Memory Usage Patterns**: Understand what information is being stored and accessed
+- **Error Tracking**: Identify issues with memory storage or retrieval
+- **Cost Analysis**: Track API calls to both Mem0 and your LLM provider
+
+AgentOps automatically instruments Mem0 to provide complete observability of your memory operations.
+
+## Installation
+
+
+```bash pip
+pip install agentops mem0ai python-dotenv
+```
+
+```bash poetry
+poetry add agentops mem0ai python-dotenv
+```
+
+```bash uv
+uv add agentops mem0ai python-dotenv
+```
+
+
+## Environment Configuration
+
+Load environment variables and set up API keys. The MEM0_API_KEY is only required if you're using the cloud-based MemoryClient.
+
+ ```bash Export to CLI
+ export AGENTOPS_API_KEY="your_agentops_api_key_here"
+ export OPENAI_API_KEY="your_openai_api_key_here"
+ ```
+ ```txt Set in .env file
+ AGENTOPS_API_KEY="your_agentops_api_key_here"
+ OPENAI_API_KEY="your_openai_api_key_here"
+ ```
+
+
+## Tracking Memory Operations
+
+
+```python Local Memory
+import agentops
+from mem0 import Memory
+
+# Start a trace to group related operations
+agentops.start_trace("user_preference_learning",tags=["mem0_memory_example"])
+
+try:
+ # Initialize Memory - AgentOps tracks the configuration
+ memory = Memory.from_config({
+ "llm": {
+ "provider": "openai",
+ "config": {
+ "model": "gpt-4o-mini",
+ "temperature": 0.1
+ }
+ }
+ })
+
+ # Add memories - AgentOps tracks each operation
+ memory.add(
+ "I prefer morning meetings and dark roast coffee",
+ user_id="user_123",
+ metadata={"category": "preferences"}
+ )
+
+ # Search memories - AgentOps tracks search queries and results
+ results = memory.search(
+ "What are the user's meeting preferences?",
+ user_id="user_123"
+ )
+
+ # End trace - AgentOps aggregates all operations
+ agentops.end_trace(end_state="success")
+
+except Exception as e:
+ agentops.end_trace(end_state="error")
+```
+
+```python Cloud Memory
+import agentops
+from mem0 import MemoryClient
+
+# Start trace for cloud operations
+agentops.start_trace("cloud_memory_sync",tags=["mem0_memoryclient_example"])
+
+try:
+ # Initialize MemoryClient - AgentOps tracks API authentication
+ client = MemoryClient(api_key="your_mem0_api_key")
+
+ # Batch add memories - AgentOps tracks bulk operations
+ messages = [
+ {"role": "user", "content": "I work in software engineering"},
+ {"role": "user", "content": "I prefer Python over Java"},
+ ]
+
+ client.add(messages, user_id="user_123")
+
+ # Search with filters - AgentOps tracks complex queries
+ filters = {"AND": [{"user_id": "user_123"}]}
+ results = client.search(
+ query="What programming languages does the user know?",
+ filters=filters,
+ version="v2"
+ )
+
+ # End trace - AgentOps aggregates all operations
+ agentops.end_trace(end_state="success")
+
+except Exception as e:
+ agentops.end_trace(end_state="error")
+```
+
+
+## What You'll See in AgentOps
+
+When using Mem0 with AgentOps, your dashboard will show:
+
+1. **Memory Operation Timeline**: Visual flow of all memory operations
+2. **Search Analytics**: Query patterns and retrieval effectiveness
+3. **Memory Growth**: Track how user memories accumulate over time
+4. **Performance Metrics**: Latency for adds, searches, and retrievals
+5. **Error Tracking**: Failed operations with full error context
+6. **Cost Attribution**: Token usage for memory extraction and searches
+
+## Examples
+
+
+
+ Simple example showing memory storage and retrieval with AgentOps tracking
+
+
+
+ Track concurrent memory operations with async/await patterns
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v2/introduction.mdx b/docs/v2/introduction.mdx
index 1accbb705..f697c2fec 100644
--- a/docs/v2/introduction.mdx
+++ b/docs/v2/introduction.mdx
@@ -23,6 +23,7 @@ description: "AgentOps is the developer favorite platform for testing, debugging
} iconType="image" href="/v2/integrations/litellm" />
} iconType="image" href="/v2/integrations/ibm_watsonx_ai" />
} iconType="image" href="/v2/integrations/xai" />
+ } iconType="image" href="/v2/integrations/mem0" />
### Agent Frameworks
diff --git a/examples/generate_documentation.py b/examples/generate_documentation.py
index 255f4d275..532bfd28c 100644
--- a/examples/generate_documentation.py
+++ b/examples/generate_documentation.py
@@ -104,7 +104,7 @@ def generate_mdx_content(notebook_path, processed_content, frontmatter=None):
if not frontmatter:
# Generate new frontmatter
folder_name = Path(notebook_path).parent.name
- title = folder_name.replace("_", " ").title()
+ title = f"{folder_name.replace('_', ' ').title()} Example"
# Extract description from first heading or use default
description = f"{title} example using AgentOps"
diff --git a/examples/mem0/mem0_memory_example.ipynb b/examples/mem0/mem0_memory_example.ipynb
new file mode 100644
index 000000000..5c21180bc
--- /dev/null
+++ b/examples/mem0/mem0_memory_example.ipynb
@@ -0,0 +1,322 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "4695c5e8",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# Memory Operations with Mem0\n",
+ "\n",
+ "This example demonstrates how to use Mem0's memory management capabilities with both synchronous and asynchronous operations to store, search, and manage conversational context and user preferences.\n",
+ "\n",
+ "## Overview\n",
+ "\n",
+ "This example showcases practical memory management operations where we:\n",
+ "\n",
+ "1. **Initialize Mem0 Memory instances** for both sync and async operations\n",
+ "2. **Store conversation history** and user preferences with metadata\n",
+ "3. **Search memories** using natural language queries\n",
+ "4. **Compare performance** between synchronous and asynchronous memory operations\n",
+ "\n",
+ "By using async operations, you can perform multiple memory operations simultaneously instead of waiting for each one to complete sequentially. This is particularly beneficial when dealing with multiple memory additions or searches.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "443ab37e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Install the required dependencies:\n",
+ "%pip install agentops\n",
+ "%pip install mem0ai\n",
+ "%pip install python-dotenv"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "994a6771",
+ "metadata": {},
+ "source": [
+ "## Setup and Imports\n",
+ "\n",
+ "Import the required libraries for local memory management with Mem0. We'll use both Memory and AsyncMemory classes to demonstrate different execution patterns for memory operations.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "id": "dbc4d41d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "from mem0 import Memory, AsyncMemory\n",
+ "import os\n",
+ "import asyncio\n",
+ "import logging\n",
+ "from dotenv import load_dotenv\n",
+ "import agentops"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "970fc737",
+ "metadata": {},
+ "source": [
+ "## Environment Configuration\n",
+ "\n",
+ "Set up environment variables for API keys. These are essential for authenticating with AgentOps for tracing and OpenAI for the language model used in memory operations.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "502e4b22",
+ "metadata": {
+ "lines_to_next_cell": 1
+ },
+ "outputs": [],
+ "source": [
+ "os.environ[\"AGENTOPS_API_KEY\"] = os.getenv(\"AGENTOPS_API_KEY\")\n",
+ "os.environ[\"OPENAI_API_KEY\"] = os.getenv(\"OPENAI_API_KEY\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "91bb29c1",
+ "metadata": {},
+ "source": [
+ "## Configuration and Sample Data\n",
+ "\n",
+ "Set up the configuration for local memory storage and define sample user data. The configuration specifies the LLM provider and model settings for processing memories.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "id": "6a99b32f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "local_config = {\n",
+ " \"llm\": {\n",
+ " \"provider\": \"openai\",\n",
+ " \"config\": {\n",
+ " \"model\": \"gpt-4o-mini\",\n",
+ " \"temperature\": 0.1,\n",
+ " \"max_tokens\": 2000,\n",
+ " },\n",
+ " }\n",
+ "}\n",
+ "user_id = \"alice_demo\"\n",
+ "agent_id = \"assistant_demo\"\n",
+ "run_id = \"session_001\"\n",
+ "\n",
+ "sample_messages = [\n",
+ " {\"role\": \"user\", \"content\": \"I'm planning to watch a movie tonight. Any recommendations?\"},\n",
+ " {\"role\": \"assistant\", \"content\": \"How about a thriller? They can be quite engaging.\"},\n",
+ " {\"role\": \"user\", \"content\": \"I'm not a big fan of thriller movies but I love sci-fi movies.\"},\n",
+ " {\n",
+ " \"role\": \"assistant\",\n",
+ " \"content\": \"Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.\",\n",
+ " },\n",
+ "]\n",
+ "\n",
+ "sample_preferences = [\n",
+ " \"I prefer dark roast coffee over light roast\",\n",
+ " \"I exercise every morning at 6 AM\",\n",
+ " \"I'm vegetarian and avoid all meat products\",\n",
+ " \"I love reading science fiction novels\",\n",
+ " \"I work in software engineering\",\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2def0966",
+ "metadata": {},
+ "source": [
+ "## Synchronous Memory Operations\n",
+ "\n",
+ "This function demonstrates sequential memory operations using the synchronous Memory class. While straightforward to implement, each operation must complete before the next begins, which can impact performance.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 76,
+ "id": "b863e6ed",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id):\n",
+ " \"\"\"\n",
+ " Demonstrate synchronous Memory class operations.\n",
+ " \"\"\"\n",
+ "\n",
+ " agentops.start_trace(\"mem0_memory_example\", tags=[\"mem0_memory_example\"])\n",
+ " try:\n",
+ " \n",
+ " memory = Memory.from_config(local_config)\n",
+ "\n",
+ " result = memory.add(\n",
+ " sample_messages, user_id=user_id, metadata={\"category\": \"movie_preferences\", \"session\": \"demo\"}\n",
+ " )\n",
+ "\n",
+ " for i, preference in enumerate(sample_preferences):\n",
+ " result = memory.add(preference, user_id=user_id, metadata={\"type\": \"preference\", \"index\": i})\n",
+ " \n",
+ " search_queries = [\n",
+ " \"What movies does the user like?\",\n",
+ " \"What are the user's food preferences?\",\n",
+ " \"When does the user exercise?\",\n",
+ " ]\n",
+ "\n",
+ " for query in search_queries:\n",
+ " results = memory.search(query, user_id=user_id)\n",
+ " \n",
+ " if results and \"results\" in results:\n",
+ " for j, result in enumerate(results): \n",
+ " print(f\"Result {j+1}: {result.get('memory', 'N/A')}\")\n",
+ " else:\n",
+ " print(\"No results found\")\n",
+ "\n",
+ " all_memories = memory.get_all(user_id=user_id)\n",
+ " if all_memories and \"results\" in all_memories:\n",
+ " print(f\"Total memories: {len(all_memories['results'])}\")\n",
+ "\n",
+ " delete_all_result = memory.delete_all(user_id=user_id)\n",
+ " print(f\"Delete all result: {delete_all_result}\")\n",
+ "\n",
+ " agentops.end_trace(end_state=\"success\")\n",
+ " except Exception as e:\n",
+ " agentops.end_trace(end_state=\"error\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "66965f64",
+ "metadata": {},
+ "source": [
+ "## Asynchronous Memory Operations\n",
+ "\n",
+ "This function showcases concurrent memory operations using AsyncMemory. By leveraging asyncio.gather(), multiple operations execute simultaneously, significantly reducing total execution time for I/O-bound tasks.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 77,
+ "id": "eae41613",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "async def demonstrate_async_memory(local_config, sample_messages, sample_preferences, user_id):\n",
+ " \"\"\"\n",
+ " Demonstrate asynchronous Memory class operations with concurrent execution.\n",
+ " \"\"\"\n",
+ "\n",
+ " agentops.start_trace(\"mem0_memory_async_example\", tags=[\"mem0_memory_async_example\"])\n",
+ " try:\n",
+ "\n",
+ " async_memory = await AsyncMemory.from_config(local_config)\n",
+ "\n",
+ " result = await async_memory.add(\n",
+ " sample_messages, user_id=user_id, metadata={\"category\": \"async_movie_preferences\", \"session\": \"async_demo\"}\n",
+ " )\n",
+ "\n",
+ " async def add_preference(preference, index):\n",
+ " \"\"\"Helper function to add a single preference asynchronously.\"\"\"\n",
+ " return await async_memory.add(\n",
+ " preference, user_id=user_id, metadata={\"type\": \"async_preference\", \"index\": index}\n",
+ " )\n",
+ "\n",
+ " tasks = [add_preference(pref, i) for i, pref in enumerate(sample_preferences)]\n",
+ " results = await asyncio.gather(*tasks)\n",
+ " for i, result in enumerate(results):\n",
+ " print(f\"Added async preference {i+1}: {result}\")\n",
+ "\n",
+ " search_queries = [\n",
+ " \"What movies does the user like?\",\n",
+ " \"What are the user's dietary restrictions?\",\n",
+ " \"What does the user do for work?\",\n",
+ " ]\n",
+ "\n",
+ " async def search_memory(query):\n",
+ " \"\"\"Helper function to perform async memory search.\"\"\"\n",
+ " return await async_memory.search(query, user_id=user_id), query\n",
+ "\n",
+ " search_tasks = [search_memory(query) for query in search_queries]\n",
+ " search_results = await asyncio.gather(*search_tasks)\n",
+ "\n",
+ " for result, query in search_results:\n",
+ " if result and \"results\" in result:\n",
+ " for j, res in enumerate(result[\"results\"]):\n",
+ " print(f\"Result {j+1}: {res.get('memory', 'N/A')}\")\n",
+ " else:\n",
+ " print(\"No results found\")\n",
+ "\n",
+ " all_memories = await async_memory.get_all(user_id=user_id)\n",
+ " if all_memories and \"results\" in all_memories:\n",
+ " print(f\"Total async memories: {len(all_memories['results'])}\")\n",
+ "\n",
+ " delete_all_result = await async_memory.delete_all(user_id=user_id)\n",
+ " print(f\"Delete all result: {delete_all_result}\")\n",
+ "\n",
+ " agentops.end_trace(end_state=\"success\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " agentops.end_trace(end_state=\"error\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9689055c",
+ "metadata": {},
+ "source": [
+ "## Execute Demonstrations\n",
+ "\n",
+ "Run both synchronous and asynchronous demonstrations to compare their execution patterns and performance. The async version demonstrates the benefits of concurrent execution for multiple memory operations.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "714436f5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Execute both sync and async demonstrations\n",
+ "demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id)\n",
+ "await demonstrate_async_memory(local_config, sample_messages, sample_preferences, user_id)"
+ ]
+ }
+ ],
+ "metadata": {
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "main_language": "python",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/mem0/mem0_memory_example.py b/examples/mem0/mem0_memory_example.py
new file mode 100644
index 000000000..58b80b8c6
--- /dev/null
+++ b/examples/mem0/mem0_memory_example.py
@@ -0,0 +1,223 @@
+"""
+# Memory Operations with Mem0
+
+This example demonstrates how to use Mem0's memory management capabilities with both synchronous and asynchronous operations to store, search, and manage conversational context and user preferences.
+
+## Overview
+
+This example showcases practical memory management operations where we:
+
+1. **Initialize Mem0 Memory instances** for both sync and async operations
+2. **Store conversation history** and user preferences with metadata
+3. **Search memories** using natural language queries
+4. **Compare performance** between synchronous and asynchronous memory operations
+
+By using async operations, you can perform multiple memory operations simultaneously instead of waiting for each one to complete sequentially. This is particularly beneficial when dealing with multiple memory additions or searches.
+"""
+import os
+import asyncio
+from dotenv import load_dotenv
+
+# Load environment variables first
+load_dotenv()
+
+# Enable debug logging for AgentOps
+os.environ["AGENTOPS_LOG_LEVEL"] = "DEBUG"
+
+# Set environment variables before importing
+os.environ["AGENTOPS_API_KEY"] = os.getenv("AGENTOPS_API_KEY")
+os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
+
+# Now import mem0 - it will be instrumented by agentops
+from mem0 import Memory, AsyncMemory # noqa E402
+
+# Import agentops BEFORE mem0 to ensure proper instrumentation
+import agentops # noqa E402
+
+
+def demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id):
+ """
+ Demonstrate synchronous Memory class operations.
+
+ This function performs sequential memory operations including:
+ - Adding conversation messages with metadata
+ - Storing individual user preferences
+ - Searching memories using natural language queries
+ - Retrieving all memories for a user
+ - Cleaning up memories after demonstration
+
+ Args:
+ local_config: Configuration dict for Memory initialization
+ sample_messages: List of conversation messages to store
+ sample_preferences: List of user preferences to store
+ user_id: Unique identifier for the user
+
+ Performance note: Sequential operations take longer as each operation
+ must complete before the next one begins.
+ """
+
+ agentops.start_trace("mem0_memory_example", tags=["mem0_memory_example"])
+ try:
+ # Initialize sync Memory with local configuration
+ memory = Memory.from_config(local_config)
+
+ # Add conversation messages with metadata for categorization
+ result = memory.add(
+ sample_messages, user_id=user_id, metadata={"category": "movie_preferences", "session": "demo"}
+ )
+
+ # Add individual preferences sequentially
+ for i, preference in enumerate(sample_preferences):
+ result = memory.add(preference, user_id=user_id, metadata={"type": "preference", "index": i})
+
+ # 2. SEARCH operations - demonstrate natural language search capabilities
+ search_queries = [
+ "What movies does the user like?",
+ "What are the user's food preferences?",
+ "When does the user exercise?",
+ ]
+
+ for query in search_queries:
+ results = memory.search(query, user_id=user_id)
+
+ if results and "results" in results:
+ for j, result in enumerate(results["results"][:2]): # Show top 2
+ print(f"Result {j+1}: {result.get('memory', 'N/A')}")
+ else:
+ print("No results found")
+
+ # 3. GET_ALL operations - retrieve all memories for the user
+ all_memories = memory.get_all(user_id=user_id)
+ if all_memories and "results" in all_memories:
+ print(f"Total memories: {len(all_memories['results'])}")
+
+ # Cleanup - remove all memories for the user
+ delete_all_result = memory.delete_all(user_id=user_id)
+ print(f"Delete all result: {delete_all_result}")
+
+ agentops.end_trace(end_state="success")
+ except Exception:
+ agentops.end_trace(end_state="error")
+
+
+async def demonstrate_async_memory(local_config, sample_messages, sample_preferences, user_id):
+ """
+ Demonstrate asynchronous Memory class operations with concurrent execution.
+
+ This function performs concurrent memory operations including:
+ - Adding conversation messages asynchronously
+ - Storing multiple preferences concurrently using asyncio.gather()
+ - Performing parallel search operations
+ - Retrieving all memories asynchronously
+ - Cleaning up memories after demonstration
+
+ Args:
+ local_config: Configuration dict for AsyncMemory initialization
+ sample_messages: List of conversation messages to store
+ sample_preferences: List of user preferences to store
+ user_id: Unique identifier for the user
+
+ Performance benefit: Concurrent operations significantly reduce total execution time
+ by running multiple memory operations in parallel.
+ """
+
+ agentops.start_trace("mem0_memory_async_example")
+ try:
+ # Initialize async Memory with configuration
+ async_memory = await AsyncMemory.from_config(local_config)
+
+ # 1. ADD operation - store conversation with async context
+ # Add conversation messages
+ result = await async_memory.add(
+ sample_messages, user_id=user_id, metadata={"category": "async_movie_preferences", "session": "async_demo"}
+ )
+
+ # Add preferences concurrently using asyncio.gather()
+ async def add_preference(preference, index):
+ """Helper function to add a single preference asynchronously."""
+ return await async_memory.add(
+ preference, user_id=user_id, metadata={"type": "async_preference", "index": index}
+ )
+
+ # Create tasks for concurrent execution
+ tasks = [add_preference(pref, i) for i, pref in enumerate(sample_preferences)]
+ results = await asyncio.gather(*tasks)
+ for i, result in enumerate(results):
+ print(f"Added async preference {i+1}: {result}")
+
+ # 2. SEARCH operations - perform multiple searches concurrently
+ search_queries = [
+ "What movies does the user like?",
+ "What are the user's dietary restrictions?",
+ "What does the user do for work?",
+ ]
+
+ async def search_memory(query):
+ """Helper function to perform async memory search."""
+ return await async_memory.search(query, user_id=user_id), query
+
+ # Execute all searches concurrently
+ search_tasks = [search_memory(query) for query in search_queries]
+ search_results = await asyncio.gather(*search_tasks)
+
+ for result, query in search_results:
+ if result and "results" in result:
+ for j, res in enumerate(result["results"][:2]):
+ print(f"Result {j+1}: {res.get('memory', 'N/A')}")
+ else:
+ print("No results found")
+
+ # 3. GET_ALL operations - retrieve all memories asynchronously
+ all_memories = await async_memory.get_all(user_id=user_id)
+ if all_memories and "results" in all_memories:
+ print(f"Total async memories: {len(all_memories['results'])}")
+
+ # Cleanup - remove all memories asynchronously
+ delete_all_result = await async_memory.delete_all(user_id=user_id)
+ print(f"Delete all result: {delete_all_result}")
+
+ agentops.end_trace(end_state="success")
+
+ except Exception:
+ agentops.end_trace(end_state="error")
+
+
+# Configuration for local memory (Memory)
+# This configuration specifies the LLM provider and model settings
+local_config = {
+ "llm": {
+ "provider": "openai",
+ "config": {
+ "model": "gpt-4o-mini",
+ "temperature": 0.1,
+ "max_tokens": 2000,
+ "api_key": os.getenv("OPENAI_API_KEY"),
+ },
+ }
+}
+# Sample user data
+user_id = "alice_demo"
+agent_id = "assistant_demo"
+run_id = "session_001"
+
+# Sample conversation data demonstrating movie preference discovery
+sample_messages = [
+ {"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
+ {"role": "assistant", "content": "How about a thriller? They can be quite engaging."},
+ {"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
+ {
+ "role": "assistant",
+ "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.",
+ },
+]
+
+# Sample user preferences covering various aspects of daily life
+sample_preferences = [
+ "I prefer dark roast coffee over light roast",
+ "I exercise every morning at 6 AM",
+]
+
+
+# Execute both sync and async demonstrations
+demonstrate_sync_memory(local_config, sample_messages, sample_preferences, user_id)
+asyncio.run(demonstrate_async_memory(local_config, sample_messages, sample_preferences, user_id))
diff --git a/examples/mem0/mem0_memoryclient_example.ipynb b/examples/mem0/mem0_memoryclient_example.ipynb
new file mode 100644
index 000000000..cbcb72496
--- /dev/null
+++ b/examples/mem0/mem0_memoryclient_example.ipynb
@@ -0,0 +1,554 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "ecd1d2ee",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# Cloud Memory Operations with Mem0 MemoryClient\n",
+ "\n",
+ "This example demonstrates how to use Mem0's cloud-based MemoryClient for managing conversational memory and user preferences with both synchronous and asynchronous operations.\n",
+ "\n",
+ "## Overview\n",
+ "\n",
+ "This example showcases cloud-based memory management operations where we:\n",
+ "\n",
+ "1. **Initialize MemoryClient instances** for both sync and async cloud operations\n",
+ "2. **Store conversation history** in the cloud with rich metadata\n",
+ "3. **Perform concurrent operations** using async/await patterns\n",
+ "4. **Search and filter memories** using natural language and structured queries\n",
+ "\n",
+ "By using the cloud-based MemoryClient with async operations, you can leverage Mem0's managed infrastructure while performing multiple memory operations simultaneously. This is ideal for production applications that need scalable memory management without managing local storage.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a63847c4",
+ "metadata": {},
+ "source": [
+ "## Setup and Imports\n",
+ "\n",
+ "First, we'll import the necessary libraries for working with Mem0's cloud-based memory management system. We'll use both synchronous and asynchronous clients to demonstrate different usage patterns.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f98af04f",
+ "metadata": {},
+ "source": [
+ "# Install the required dependencies:\n",
+ "%pip install agentops\n",
+ "%pip install mem0ai\n",
+ "%pip install python-dotenv"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 95,
+ "id": "69b834f6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: agentops in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (0.4.14)\n",
+ "Requirement already satisfied: httpx<0.29.0,>=0.24.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (0.28.1)\n",
+ "Requirement already satisfied: opentelemetry-api>1.29.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (1.34.1)\n",
+ "Requirement already satisfied: opentelemetry-exporter-otlp-proto-http>1.29.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (1.34.1)\n",
+ "Requirement already satisfied: opentelemetry-instrumentation>=0.50b0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (0.55b1)\n",
+ "Requirement already satisfied: opentelemetry-sdk>1.29.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (1.34.1)\n",
+ "Requirement already satisfied: opentelemetry-semantic-conventions>=0.50b0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (0.55b1)\n",
+ "Requirement already satisfied: ordered-set<5.0.0,>=4.0.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (4.1.0)\n",
+ "Requirement already satisfied: packaging<25.0,>=21.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (24.2)\n",
+ "Requirement already satisfied: psutil<7.0.1,>=5.9.8 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (7.0.0)\n",
+ "Requirement already satisfied: pyyaml<7.0,>=5.3 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (6.0.2)\n",
+ "Requirement already satisfied: requests<3.0.0,>=2.0.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (2.32.4)\n",
+ "Requirement already satisfied: termcolor<2.5.0,>=2.3.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (2.4.0)\n",
+ "Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from agentops) (1.17.2)\n",
+ "Requirement already satisfied: anyio in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx<0.29.0,>=0.24.0->agentops) (4.9.0)\n",
+ "Requirement already satisfied: certifi in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx<0.29.0,>=0.24.0->agentops) (2025.4.26)\n",
+ "Requirement already satisfied: httpcore==1.* in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx<0.29.0,>=0.24.0->agentops) (1.0.9)\n",
+ "Requirement already satisfied: idna in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx<0.29.0,>=0.24.0->agentops) (3.10)\n",
+ "Requirement already satisfied: h11>=0.16 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpcore==1.*->httpx<0.29.0,>=0.24.0->agentops) (0.16.0)\n",
+ "Requirement already satisfied: charset_normalizer<4,>=2 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.0.0->agentops) (3.4.2)\n",
+ "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.0.0->agentops) (2.4.0)\n",
+ "Requirement already satisfied: importlib-metadata<8.8.0,>=6.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from opentelemetry-api>1.29.0->agentops) (8.7.0)\n",
+ "Requirement already satisfied: typing-extensions>=4.5.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from opentelemetry-api>1.29.0->agentops) (4.14.0)\n",
+ "Requirement already satisfied: zipp>=3.20 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from importlib-metadata<8.8.0,>=6.0->opentelemetry-api>1.29.0->agentops) (3.23.0)\n",
+ "Requirement already satisfied: googleapis-common-protos~=1.52 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from opentelemetry-exporter-otlp-proto-http>1.29.0->agentops) (1.70.0)\n",
+ "Requirement already satisfied: opentelemetry-exporter-otlp-proto-common==1.34.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from opentelemetry-exporter-otlp-proto-http>1.29.0->agentops) (1.34.1)\n",
+ "Requirement already satisfied: opentelemetry-proto==1.34.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from opentelemetry-exporter-otlp-proto-http>1.29.0->agentops) (1.34.1)\n",
+ "Requirement already satisfied: protobuf<6.0,>=5.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from opentelemetry-proto==1.34.1->opentelemetry-exporter-otlp-proto-http>1.29.0->agentops) (5.29.5)\n",
+ "Requirement already satisfied: sniffio>=1.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from anyio->httpx<0.29.0,>=0.24.0->agentops) (1.3.1)\n",
+ "Note: you may need to restart the kernel to use updated packages.\n",
+ "Requirement already satisfied: mem0ai in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (0.1.104)\n",
+ "Requirement already satisfied: openai>=1.33.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from mem0ai) (1.75.0)\n",
+ "Requirement already satisfied: posthog>=3.5.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from mem0ai) (3.25.0)\n",
+ "Requirement already satisfied: pydantic>=2.7.3 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from mem0ai) (2.11.4)\n",
+ "Requirement already satisfied: pytz>=2024.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from mem0ai) (2024.2)\n",
+ "Requirement already satisfied: qdrant-client>=1.9.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from mem0ai) (1.14.2)\n",
+ "Requirement already satisfied: sqlalchemy>=2.0.31 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from mem0ai) (2.0.41)\n",
+ "Requirement already satisfied: anyio<5,>=3.5.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (4.9.0)\n",
+ "Requirement already satisfied: distro<2,>=1.7.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (1.9.0)\n",
+ "Requirement already satisfied: httpx<1,>=0.23.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (0.28.1)\n",
+ "Requirement already satisfied: jiter<1,>=0.4.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (0.8.2)\n",
+ "Requirement already satisfied: sniffio in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (1.3.1)\n",
+ "Requirement already satisfied: tqdm>4 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (4.67.1)\n",
+ "Requirement already satisfied: typing-extensions<5,>=4.11 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from openai>=1.33.0->mem0ai) (4.14.0)\n",
+ "Requirement already satisfied: idna>=2.8 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from anyio<5,>=3.5.0->openai>=1.33.0->mem0ai) (3.10)\n",
+ "Requirement already satisfied: certifi in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai>=1.33.0->mem0ai) (2025.4.26)\n",
+ "Requirement already satisfied: httpcore==1.* in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai>=1.33.0->mem0ai) (1.0.9)\n",
+ "Requirement already satisfied: h11>=0.16 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai>=1.33.0->mem0ai) (0.16.0)\n",
+ "Requirement already satisfied: annotated-types>=0.6.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from pydantic>=2.7.3->mem0ai) (0.7.0)\n",
+ "Requirement already satisfied: pydantic-core==2.33.2 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from pydantic>=2.7.3->mem0ai) (2.33.2)\n",
+ "Requirement already satisfied: typing-inspection>=0.4.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from pydantic>=2.7.3->mem0ai) (0.4.0)\n",
+ "Requirement already satisfied: requests<3.0,>=2.7 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from posthog>=3.5.0->mem0ai) (2.32.4)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from posthog>=3.5.0->mem0ai) (1.17.0)\n",
+ "Requirement already satisfied: monotonic>=1.5 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from posthog>=3.5.0->mem0ai) (1.6)\n",
+ "Requirement already satisfied: backoff>=1.10.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from posthog>=3.5.0->mem0ai) (2.2.1)\n",
+ "Requirement already satisfied: python-dateutil>2.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from posthog>=3.5.0->mem0ai) (2.9.0.post0)\n",
+ "Requirement already satisfied: charset_normalizer<4,>=2 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from requests<3.0,>=2.7->posthog>=3.5.0->mem0ai) (3.4.2)\n",
+ "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from requests<3.0,>=2.7->posthog>=3.5.0->mem0ai) (2.4.0)\n",
+ "Requirement already satisfied: grpcio>=1.41.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from qdrant-client>=1.9.1->mem0ai) (1.71.0)\n",
+ "Requirement already satisfied: numpy>=1.21 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from qdrant-client>=1.9.1->mem0ai) (2.2.5)\n",
+ "Requirement already satisfied: portalocker<3.0.0,>=2.7.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from qdrant-client>=1.9.1->mem0ai) (2.10.1)\n",
+ "Requirement already satisfied: protobuf>=3.20.0 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from qdrant-client>=1.9.1->mem0ai) (5.29.5)\n",
+ "Requirement already satisfied: h2<5,>=3 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from httpx[http2]>=0.20.0->qdrant-client>=1.9.1->mem0ai) (4.2.0)\n",
+ "Requirement already satisfied: hyperframe<7,>=6.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from h2<5,>=3->httpx[http2]>=0.20.0->qdrant-client>=1.9.1->mem0ai) (6.1.0)\n",
+ "Requirement already satisfied: hpack<5,>=4.1 in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (from h2<5,>=3->httpx[http2]>=0.20.0->qdrant-client>=1.9.1->mem0ai) (4.1.0)\n",
+ "Note: you may need to restart the kernel to use updated packages.\n",
+ "Requirement already satisfied: python-dotenv in /Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages (1.1.0)\n",
+ "Note: you may need to restart the kernel to use updated packages.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Install the required dependencies:\n",
+ "%pip install agentops\n",
+ "%pip install mem0ai\n",
+ "%pip install python-dotenv"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 96,
+ "id": "e552e158",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "from mem0 import MemoryClient, AsyncMemoryClient\n",
+ "import agentops\n",
+ "import os\n",
+ "import asyncio\n",
+ "from dotenv import load_dotenv"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a70fadde",
+ "metadata": {},
+ "source": [
+ "## Environment Configuration\n",
+ "\n",
+ "Load environment variables including API keys for AgentOps, Mem0, and OpenAI. These credentials are required for authenticating with the respective services.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 97,
+ "id": "969f7c42",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Load environment variables\n",
+ "load_dotenv()\n",
+ "os.environ[\"AGENTOPS_API_KEY\"] = os.getenv(\"AGENTOPS_API_KEY\")\n",
+ "mem0_api_key = os.getenv(\"MEM0_API_KEY\")\n",
+ "os.environ[\"OPENAI_API_KEY\"] = os.getenv(\"OPENAI_API_KEY\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3f630d40",
+ "metadata": {},
+ "source": [
+ "## Sample Data Setup\n",
+ "\n",
+ "Define user identifiers and sample data that will be used throughout the demonstration. This includes user IDs for tracking memory ownership and agent/session identifiers for context.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 98,
+ "id": "0abf3fe7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Sample user data for demonstration\n",
+ "user_id = \"alice_demo\"\n",
+ "agent_id = \"assistant_demo\"\n",
+ "run_id = \"session_001\"\n",
+ "\n",
+ "# Sample conversation data demonstrating preference discovery through dialogue\n",
+ "sample_messages = [\n",
+ " {\"role\": \"user\", \"content\": \"I'm planning to watch a movie tonight. Any recommendations?\"},\n",
+ " {\"role\": \"assistant\", \"content\": \"How about a thriller? They can be quite engaging.\"},\n",
+ " {\"role\": \"user\", \"content\": \"I'm not a big fan of thriller movies but I love sci-fi movies.\"},\n",
+ " {\n",
+ " \"role\": \"assistant\",\n",
+ " \"content\": \"Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.\",\n",
+ " },\n",
+ "]\n",
+ "\n",
+ "# Sample user preferences representing various personal attributes\n",
+ "sample_preferences = [\n",
+ " \"I prefer dark roast coffee over light roast\",\n",
+ " \"I exercise every morning at 6 AM\",\n",
+ " \"I'm vegetarian and avoid all meat products\",\n",
+ " \"I love reading science fiction novels\",\n",
+ " \"I work in software engineering\",\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "046d2588",
+ "metadata": {},
+ "source": [
+ "## Synchronous Memory Operations\n",
+ "\n",
+ "The following function demonstrates how to use the synchronous MemoryClient for sequential cloud memory operations. This approach is straightforward but operations execute one after another.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 99,
+ "id": "63d2f851",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def demonstrate_sync_memory_client(sample_messages, sample_preferences, user_id):\n",
+ " \"\"\"\n",
+ " Demonstrate synchronous MemoryClient operations with cloud storage.\n",
+ " \n",
+ " This function performs sequential cloud memory operations including:\n",
+ " - Initializing cloud-based memory client with API authentication\n",
+ " - Adding conversation messages to cloud storage\n",
+ " - Storing user preferences with metadata\n",
+ " - Searching memories using natural language\n",
+ " - Retrieving memories with filters\n",
+ " - Cleaning up cloud memories\n",
+ " \n",
+ " \"\"\"\n",
+ " agentops.start_trace(\"mem0_memoryclient_sync_example\",tags=[\"mem0_memoryclient_example\"])\n",
+ " try:\n",
+ " # Initialize sync MemoryClient with API key for cloud access\n",
+ " client = MemoryClient(api_key=mem0_api_key)\n",
+ "\n",
+ "\n",
+ " # Add conversation to cloud storage with metadata\n",
+ " result = client.add(\n",
+ " sample_messages, user_id=user_id, metadata={\"category\": \"cloud_movie_preferences\", \"session\": \"cloud_demo\"},version=\"v2\"\n",
+ " )\n",
+ " print(f\"Add result: {result}\")\n",
+ "\n",
+ " # Add preferences sequentially to cloud\n",
+ " for i, preference in enumerate(sample_preferences[:3]): # Limit for demo\n",
+ " # Convert string preference to message format\n",
+ " preference_message = [{\"role\": \"user\", \"content\": preference}]\n",
+ " result = client.add(preference_message, user_id=user_id, metadata={\"type\": \"cloud_preference\", \"index\": i})\n",
+ " \n",
+ "\n",
+ " # 2. SEARCH operations - leverage cloud search capabilities\n",
+ " search_result = client.search(\"What are the user's movie preferences?\", user_id=user_id)\n",
+ " print(f\"Search result: {search_result}\")\n",
+ "\n",
+ " # 3. GET_ALL - retrieve all memories for the user\n",
+ " all_memories = client.get_all(user_id=user_id, limit=10)\n",
+ " print(f\"Cloud memories retrieved: {all_memories}\")\n",
+ "\n",
+ " # Cleanup - remove all user memories from cloud\n",
+ " delete_all_result = client.delete_all(user_id=user_id)\n",
+ " print(f\"Delete all result: {delete_all_result}\")\n",
+ " agentops.end_trace(end_state=\"success\")\n",
+ " except Exception as e:\n",
+ " agentops.end_trace(end_state=\"error\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6e629329",
+ "metadata": {},
+ "source": [
+ "## Asynchronous Memory Operations\n",
+ "\n",
+ "This function showcases the power of asynchronous operations with Mem0's AsyncMemoryClient. By using async/await patterns, we can execute multiple memory operations concurrently, significantly improving performance.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 100,
+ "id": "2462a05f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "async def demonstrate_async_memory_client(sample_messages, sample_preferences, user_id):\n",
+ " \"\"\"\n",
+ " Demonstrate asynchronous MemoryClient operations with concurrent cloud access.\n",
+ " \n",
+ " This function performs concurrent cloud memory operations including:\n",
+ " - Initializing async cloud-based memory client\n",
+ " - Adding multiple memories concurrently using asyncio.gather()\n",
+ " - Performing parallel search operations across cloud storage\n",
+ " - Retrieving filtered memories asynchronously\n",
+ " - Cleaning up cloud memories efficiently\n",
+ " \n",
+ " \"\"\"\n",
+ " agentops.start_trace(\"mem0_memoryclient_async_example\",tags=[\"mem0_memoryclient_example\"])\n",
+ " try:\n",
+ " # Initialize async MemoryClient for concurrent cloud operations\n",
+ " async_client = AsyncMemoryClient(api_key=mem0_api_key)\n",
+ " \n",
+ " # Add conversation and preferences concurrently to cloud\n",
+ " add_conversation_task = async_client.add(\n",
+ " sample_messages, user_id=user_id, metadata={\"category\": \"async_cloud_movies\", \"session\": \"async_cloud_demo\"}\n",
+ " )\n",
+ "\n",
+ " # Create tasks for adding preferences in parallel\n",
+ " add_preference_tasks = [\n",
+ " async_client.add([{\"role\": \"user\", \"content\": pref}], user_id=user_id, metadata={\"type\": \"async_cloud_preference\", \"index\": i})\n",
+ " for i, pref in enumerate(sample_preferences[:3])\n",
+ " ]\n",
+ "\n",
+ " # Execute all add operations concurrently\n",
+ " results = await asyncio.gather(add_conversation_task, *add_preference_tasks)\n",
+ " for i, result in enumerate(results):\n",
+ " print(f\"{i+1}. {result}\")\n",
+ "\n",
+ " # 2. Concurrent SEARCH operations - multiple cloud searches in parallel\n",
+ " search_tasks = [\n",
+ " async_client.search(\"movie preferences\", user_id=user_id),\n",
+ " async_client.search(\"food preferences\", user_id=user_id),\n",
+ " async_client.search(\"work information\", user_id=user_id),\n",
+ " ]\n",
+ "\n",
+ " # Execute all searches concurrently\n",
+ " search_results = await asyncio.gather(*search_tasks)\n",
+ " for i, result in enumerate(search_results):\n",
+ " print(f\"Search {i+1} result: {result}\")\n",
+ "\n",
+ " # 3. GET_ALL operation - retrieve all memories from cloud\n",
+ " all_memories = await async_client.get_all(user_id=user_id, limit=10)\n",
+ " print(f\"Async cloud memories: {all_memories}\")\n",
+ "\n",
+ " # Final cleanup - remove all memories asynchronously\n",
+ " delete_all_result = await async_client.delete_all(user_id=user_id)\n",
+ " print(f\"Delete all result: {delete_all_result}\")\n",
+ "\n",
+ " agentops.end_trace(end_state=\"success\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " agentops.end_trace(end_state=\"error\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c5e1af75",
+ "metadata": {},
+ "source": [
+ "## Execute Demonstrations\n",
+ "\n",
+ "Run both synchronous and asynchronous demonstrations to compare their behavior and performance. The async version typically completes faster due to concurrent operations, especially when dealing with multiple API calls.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 101,
+ "id": "3a6524b2",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "🖇 AgentOps: \u001b[34m\u001b[34mSession Replay for mem0_memoryclient_sync_example trace: https://app.agentops.ai/sessions?trace_id=75c9c672ca2f2205d1bdb9b4f615ba2e\u001b[0m\u001b[0m\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "args: ([{'role': 'user', 'content': \"I'm planning to watch a movie tonight. Any recommendations?\"}, {'role': 'assistant', 'content': 'How about a thriller? They can be quite engaging.'}, {'role': 'user', 'content': \"I'm not a big fan of thriller movies but I love sci-fi movies.\"}, {'role': 'assistant', 'content': \"Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.\"}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'category': 'cloud_movie_preferences', 'session': 'cloud_demo'}, 'version': 'v2'}\n",
+ "return_value: None\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/fenil/Documents/agentops/venv/lib/python3.11/site-packages/mem0/client/main.py:34: DeprecationWarning: output_format='v1.0' is deprecated therefore setting it to 'v1.1' by default.Check out the docs for more information: https://docs.mem0.ai/platform/quickstart#4-1-create-memories\n",
+ " return func(*args, **kwargs)\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: {'results': [{'id': '15b4579e-a074-4ef6-a318-03078a18858b', 'event': 'ADD', 'memory': 'Prefers sci-fi movies over thriller movies'}]}\n",
+ "Add result: {'results': [{'id': '15b4579e-a074-4ef6-a318-03078a18858b', 'event': 'ADD', 'memory': 'Prefers sci-fi movies over thriller movies'}]}\n",
+ "args: ([{'role': 'user', 'content': 'I prefer dark roast coffee over light roast'}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'type': 'cloud_preference', 'index': 0}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: {'results': [{'id': 'd012318e-6dd3-4456-9ddb-1e33b5973201', 'event': 'ADD', 'memory': 'Prefers dark roast coffee over light roast'}]}\n",
+ "args: ([{'role': 'user', 'content': 'I exercise every morning at 6 AM'}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'type': 'cloud_preference', 'index': 1}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: {'results': [{'id': '1e0829f9-ada4-4fde-8371-9a0b24410692', 'event': 'ADD', 'memory': 'Exercises every morning at 6 AM'}]}\n",
+ "args: ([{'role': 'user', 'content': \"I'm vegetarian and avoid all meat products\"}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'type': 'cloud_preference', 'index': 2}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: {'results': [{'id': 'ea933b7a-ad62-4340-9e4b-422ba89ccb13', 'event': 'ADD', 'memory': 'Is vegetarian and avoids all meat products'}]}\n",
+ "get_search_attributes args: (\"What are the user's movie preferences?\",)\n",
+ "get_search_attributes kwargs: {'user_id': 'alice_demo'}\n",
+ "get_search_attributes return_value: None\n",
+ "get_search_attributes args: None\n",
+ "get_search_attributes kwargs: None\n",
+ "get_search_attributes return_value: [{'id': '15b4579e-a074-4ef6-a318-03078a18858b', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'cloud_demo', 'category': 'cloud_movie_preferences'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:02.776810-07:00', 'updated_at': '2025-06-13T07:01:02.795856-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.5759006142616313}, {'id': 'd012318e-6dd3-4456-9ddb-1e33b5973201', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:09.645061-07:00', 'updated_at': '2025-06-13T07:01:09.666647-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.4206016754386812}, {'id': 'ea933b7a-ad62-4340-9e4b-422ba89ccb13', 'memory': 'Is vegetarian and avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 2}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:24.877544-07:00', 'updated_at': '2025-06-13T07:01:24.896089-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3932421036670084}, {'id': '1e0829f9-ada4-4fde-8371-9a0b24410692', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:16.659112-07:00', 'updated_at': '2025-06-13T07:01:16.740428-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3198329210281394}]\n",
+ "Search result: [{'id': '15b4579e-a074-4ef6-a318-03078a18858b', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'cloud_demo', 'category': 'cloud_movie_preferences'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:02.776810-07:00', 'updated_at': '2025-06-13T07:01:02.795856-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.5759006142616313}, {'id': 'd012318e-6dd3-4456-9ddb-1e33b5973201', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:09.645061-07:00', 'updated_at': '2025-06-13T07:01:09.666647-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.4206016754386812}, {'id': 'ea933b7a-ad62-4340-9e4b-422ba89ccb13', 'memory': 'Is vegetarian and avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 2}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:24.877544-07:00', 'updated_at': '2025-06-13T07:01:24.896089-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3932421036670084}, {'id': '1e0829f9-ada4-4fde-8371-9a0b24410692', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:16.659112-07:00', 'updated_at': '2025-06-13T07:01:16.740428-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3198329210281394}]\n",
+ "Cloud memories retrieved: [{'id': 'ea933b7a-ad62-4340-9e4b-422ba89ccb13', 'memory': 'Is vegetarian and avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 2}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:24.877544-07:00', 'updated_at': '2025-06-13T07:01:24.896089-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': '1e0829f9-ada4-4fde-8371-9a0b24410692', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:16.659112-07:00', 'updated_at': '2025-06-13T07:01:16.740428-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': 'd012318e-6dd3-4456-9ddb-1e33b5973201', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:09.645061-07:00', 'updated_at': '2025-06-13T07:01:09.666647-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': '15b4579e-a074-4ef6-a318-03078a18858b', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'cloud_demo', 'category': 'cloud_movie_preferences'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:02.776810-07:00', 'updated_at': '2025-06-13T07:01:02.795856-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}]\n",
+ "Delete all result: {'message': 'Memories deleted successfully!'}\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "🖇 AgentOps: \u001b[34m\u001b[34mSession Replay for mem0_memoryclient_sync_example.session trace: https://app.agentops.ai/sessions?trace_id=75c9c672ca2f2205d1bdb9b4f615ba2e\u001b[0m\u001b[0m\n",
+ "🖇 AgentOps: \u001b[34m\u001b[34mSession Replay for mem0_memoryclient_async_example trace: https://app.agentops.ai/sessions?trace_id=077cb3ff917063d6c4c668dd3de1a5ed\u001b[0m\u001b[0m\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "args: ([{'role': 'user', 'content': \"I'm planning to watch a movie tonight. Any recommendations?\"}, {'role': 'assistant', 'content': 'How about a thriller? They can be quite engaging.'}, {'role': 'user', 'content': \"I'm not a big fan of thriller movies but I love sci-fi movies.\"}, {'role': 'assistant', 'content': \"Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.\"}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'category': 'async_cloud_movies', 'session': 'async_cloud_demo'}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: \n",
+ "args: ([{'role': 'user', 'content': 'I prefer dark roast coffee over light roast'}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'type': 'async_cloud_preference', 'index': 0}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: \n",
+ "args: ([{'role': 'user', 'content': 'I exercise every morning at 6 AM'}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'type': 'async_cloud_preference', 'index': 1}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: \n",
+ "args: ([{'role': 'user', 'content': \"I'm vegetarian and avoid all meat products\"}],)\n",
+ "kwargs: {'user_id': 'alice_demo', 'metadata': {'type': 'async_cloud_preference', 'index': 2}}\n",
+ "return_value: None\n",
+ "args: None\n",
+ "kwargs: None\n",
+ "return_value: \n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/events.py:84: DeprecationWarning: output_format='v1.0' is deprecated therefore setting it to 'v1.1' by default.Check out the docs for more information: https://docs.mem0.ai/platform/quickstart#4-1-create-memories\n",
+ " self._context.run(self._callback, *self._args)\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1. {'results': [{'id': '2ef3c2da-4ad1-4b5d-b735-9852d78c17e7', 'event': 'ADD', 'memory': 'Prefers sci-fi movies over thriller movies'}]}\n",
+ "2. {'results': [{'id': '05b6099e-2269-4ba1-9c8c-476a160e180d', 'event': 'ADD', 'memory': 'Prefers dark roast coffee over light roast'}]}\n",
+ "3. {'results': [{'id': '38b92272-b8f2-491a-8674-2b37e60fe93a', 'event': 'ADD', 'memory': 'Exercises every morning at 6 AM'}]}\n",
+ "4. {'results': [{'id': 'e053d1bd-a584-4a82-ba61-eb2b00f299c8', 'event': 'ADD', 'memory': 'Is vegetarian'}, {'id': '2dcee7ea-0684-4ddc-bacd-8a66609649f9', 'event': 'ADD', 'memory': 'Avoids all meat products'}]}\n",
+ "get_search_attributes args: ('movie preferences',)\n",
+ "get_search_attributes kwargs: {'user_id': 'alice_demo'}\n",
+ "get_search_attributes return_value: None\n",
+ "get_search_attributes args: None\n",
+ "get_search_attributes kwargs: None\n",
+ "get_search_attributes return_value: \n",
+ "get_search_attributes args: ('food preferences',)\n",
+ "get_search_attributes kwargs: {'user_id': 'alice_demo'}\n",
+ "get_search_attributes return_value: None\n",
+ "get_search_attributes args: None\n",
+ "get_search_attributes kwargs: None\n",
+ "get_search_attributes return_value: \n",
+ "get_search_attributes args: ('work information',)\n",
+ "get_search_attributes kwargs: {'user_id': 'alice_demo'}\n",
+ "get_search_attributes return_value: None\n",
+ "get_search_attributes args: None\n",
+ "get_search_attributes kwargs: None\n",
+ "get_search_attributes return_value: \n",
+ "Search 1 result: [{'id': '2ef3c2da-4ad1-4b5d-b735-9852d78c17e7', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'async_cloud_demo', 'category': 'async_cloud_movies'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:33.908188-07:00', 'updated_at': '2025-06-13T07:01:33.928443-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.5863419103332085}, {'id': '05b6099e-2269-4ba1-9c8c-476a160e180d', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:33.765858-07:00', 'updated_at': '2025-06-13T07:01:33.785855-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.4206878417525355}, {'id': '2dcee7ea-0684-4ddc-bacd-8a66609649f9', 'memory': 'Avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': None, 'created_at': '2025-06-13T07:01:36.120569-07:00', 'updated_at': '2025-06-13T07:01:36.138808-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3659444262368807}, {'id': '38b92272-b8f2-491a-8674-2b37e60fe93a', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:33.281702-07:00', 'updated_at': '2025-06-13T07:01:33.300498-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3010108740041443}]\n",
+ "Search 2 result: [{'id': '05b6099e-2269-4ba1-9c8c-476a160e180d', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:33.765858-07:00', 'updated_at': '2025-06-13T07:01:33.785855-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.5045933422688647}, {'id': '2dcee7ea-0684-4ddc-bacd-8a66609649f9', 'memory': 'Avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': None, 'created_at': '2025-06-13T07:01:36.120569-07:00', 'updated_at': '2025-06-13T07:01:36.138808-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.48612332344055176}, {'id': '2ef3c2da-4ad1-4b5d-b735-9852d78c17e7', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'async_cloud_demo', 'category': 'async_cloud_movies'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:33.908188-07:00', 'updated_at': '2025-06-13T07:01:33.928443-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.43332618077553475}, {'id': 'e053d1bd-a584-4a82-ba61-eb2b00f299c8', 'memory': 'Is vegetarian', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:34.655396-07:00', 'updated_at': '2025-06-13T07:01:34.728349-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3886008858680725}, {'id': '38b92272-b8f2-491a-8674-2b37e60fe93a', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:33.281702-07:00', 'updated_at': '2025-06-13T07:01:33.300498-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3528817804385299}]\n",
+ "Search 3 result: [{'id': '2dcee7ea-0684-4ddc-bacd-8a66609649f9', 'memory': 'Avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': None, 'created_at': '2025-06-13T07:01:36.120569-07:00', 'updated_at': '2025-06-13T07:01:36.138808-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3600524282486701}, {'id': '38b92272-b8f2-491a-8674-2b37e60fe93a', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:33.281702-07:00', 'updated_at': '2025-06-13T07:01:33.300498-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3572185167334696}, {'id': '05b6099e-2269-4ba1-9c8c-476a160e180d', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:33.765858-07:00', 'updated_at': '2025-06-13T07:01:33.785855-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.32800289988517994}, {'id': 'e053d1bd-a584-4a82-ba61-eb2b00f299c8', 'memory': 'Is vegetarian', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:34.655396-07:00', 'updated_at': '2025-06-13T07:01:34.728349-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.31821893562191406}, {'id': '2ef3c2da-4ad1-4b5d-b735-9852d78c17e7', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'async_cloud_demo', 'category': 'async_cloud_movies'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:33.908188-07:00', 'updated_at': '2025-06-13T07:01:33.928443-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None, 'score': 0.3099285733614652}]\n",
+ "Async cloud memories: [{'id': '2dcee7ea-0684-4ddc-bacd-8a66609649f9', 'memory': 'Avoids all meat products', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': ['food'], 'created_at': '2025-06-13T07:01:36.120569-07:00', 'updated_at': '2025-06-13T07:01:36.138808-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': 'e053d1bd-a584-4a82-ba61-eb2b00f299c8', 'memory': 'Is vegetarian', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 2}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:34.655396-07:00', 'updated_at': '2025-06-13T07:01:34.728349-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': '2ef3c2da-4ad1-4b5d-b735-9852d78c17e7', 'memory': 'Prefers sci-fi movies over thriller movies', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'session': 'async_cloud_demo', 'category': 'async_cloud_movies'}, 'categories': ['user_preferences', 'entertainment'], 'created_at': '2025-06-13T07:01:33.908188-07:00', 'updated_at': '2025-06-13T07:01:33.928443-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': '05b6099e-2269-4ba1-9c8c-476a160e180d', 'memory': 'Prefers dark roast coffee over light roast', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 0}, 'categories': ['user_preferences', 'food'], 'created_at': '2025-06-13T07:01:33.765858-07:00', 'updated_at': '2025-06-13T07:01:33.785855-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}, {'id': '38b92272-b8f2-491a-8674-2b37e60fe93a', 'memory': 'Exercises every morning at 6 AM', 'user_id': 'alice_demo', 'actor_id': None, 'metadata': {'type': 'async_cloud_preference', 'index': 1}, 'categories': ['health'], 'created_at': '2025-06-13T07:01:33.281702-07:00', 'updated_at': '2025-06-13T07:01:33.300498-07:00', 'expiration_date': None, 'structured_attributes': None, 'internal_metadata': None, 'deleted_at': None}]\n",
+ "Delete all result: {'message': 'Memories deleted successfully!'}\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "🖇 AgentOps: \u001b[34m\u001b[34mSession Replay for mem0_memoryclient_async_example.session trace: https://app.agentops.ai/sessions?trace_id=077cb3ff917063d6c4c668dd3de1a5ed\u001b[0m\u001b[0m\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Execute both sync and async demonstrations\n",
+ "demonstrate_sync_memory_client(sample_messages, sample_preferences, user_id)\n",
+ "await demonstrate_async_memory_client(sample_messages, sample_preferences, user_id)"
+ ]
+ }
+ ],
+ "metadata": {
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "main_language": "python",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/mem0/mem0_memoryclient_example.py b/examples/mem0/mem0_memoryclient_example.py
new file mode 100644
index 000000000..2928fa99e
--- /dev/null
+++ b/examples/mem0/mem0_memoryclient_example.py
@@ -0,0 +1,184 @@
+"""
+# Cloud Memory Operations with Mem0 MemoryClient
+
+This example demonstrates how to use Mem0's cloud-based MemoryClient for managing conversational memory and user preferences with both synchronous and asynchronous operations.
+
+## Overview
+
+This example showcases cloud-based memory management operations where we:
+
+1. **Initialize MemoryClient instances** for both sync and async cloud operations
+2. **Store conversation history** in the cloud with rich metadata
+3. **Perform concurrent operations** using async/await patterns
+4. **Search and filter memories** using natural language and structured queries
+
+By using the cloud-based MemoryClient with async operations, you can leverage Mem0's managed infrastructure while performing multiple memory operations simultaneously. This is ideal for production applications that need scalable memory management without managing local storage.
+"""
+import os
+import asyncio
+from dotenv import load_dotenv
+
+# Load environment variables first
+load_dotenv()
+
+# Set environment variables before importing
+os.environ["AGENTOPS_API_KEY"] = os.getenv("AGENTOPS_API_KEY")
+os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
+mem0_api_key = os.getenv("MEM0_API_KEY")
+
+# Import agentops BEFORE mem0 to ensure proper instrumentation
+import agentops # noqa E402
+
+# Now import mem0 - it will be instrumented by agentops
+from mem0 import MemoryClient, AsyncMemoryClient # noqa E402
+
+
+def demonstrate_sync_memory_client(sample_messages, sample_preferences, user_id):
+ """
+ Demonstrate synchronous MemoryClient operations with cloud storage.
+
+ This function performs sequential cloud memory operations including:
+ - Initializing cloud-based memory client with API authentication
+ - Adding conversation messages to cloud storage
+ - Storing user preferences with metadata
+ - Searching memories using natural language
+ - Retrieving memories with filters
+ - Cleaning up cloud memories
+
+ Args:
+ sample_messages: List of conversation messages to store
+ sample_preferences: List of user preferences to store
+ user_id: Unique identifier for the user
+
+ Cloud benefit: All memory operations are handled by Mem0's infrastructure,
+ providing scalability and persistence without local storage management.
+ """
+ agentops.start_trace("mem0_memoryclient_example", tags=["mem0_memoryclient_example"])
+ try:
+ # Initialize sync MemoryClient with API key for cloud access
+ client = MemoryClient(api_key=mem0_api_key)
+
+ # Add conversation to cloud storage with metadata
+ result = client.add(
+ sample_messages, user_id=user_id, metadata={"category": "cloud_movie_preferences", "session": "cloud_demo"}
+ )
+ print(f"Add result: {result}")
+
+ # Add preferences sequentially to cloud
+ for i, preference in enumerate(sample_preferences[:3]): # Limit for demo
+ result = client.add(preference, user_id=user_id, metadata={"type": "cloud_preference", "index": i})
+
+ # 2. SEARCH operations - leverage cloud search capabilities
+ search_result = client.search("What are the user's movie preferences?", user_id=user_id)
+ print(f"Search result: {search_result}")
+
+ # 3. GET_ALL with filters - demonstrate structured query capabilities
+ filters = {"AND": [{"user_id": user_id}]}
+ all_memories = client.get_all(filters=filters, limit=10)
+ print(f"Cloud memories retrieved: {all_memories}")
+
+ # Cleanup - remove all user memories from cloud
+ delete_all_result = client.delete_all(user_id=user_id)
+ print(f"Delete all result: {delete_all_result}")
+ agentops.end_trace(end_state="success")
+ except Exception:
+ agentops.end_trace(end_state="error")
+
+
+async def demonstrate_async_memory_client(sample_messages, sample_preferences, user_id):
+ """
+ Demonstrate asynchronous MemoryClient operations with concurrent cloud access.
+
+ This function performs concurrent cloud memory operations including:
+ - Initializing async cloud-based memory client
+ - Adding multiple memories concurrently using asyncio.gather()
+ - Performing parallel search operations across cloud storage
+ - Retrieving filtered memories asynchronously
+ - Cleaning up cloud memories efficiently
+
+ Args:
+ sample_messages: List of conversation messages to store
+ sample_preferences: List of user preferences to store
+ user_id: Unique identifier for the user
+
+ Performance benefit: Async operations allow multiple cloud API calls to execute
+ concurrently, significantly reducing total execution time compared to sequential calls.
+ This is especially beneficial when dealing with network I/O to cloud services.
+ """
+ agentops.start_trace("mem0_memoryclient_example", tags=["mem0_memoryclient_example"])
+ try:
+ # Initialize async MemoryClient for concurrent cloud operations
+ async_client = AsyncMemoryClient(api_key=mem0_api_key)
+
+ # Add conversation and preferences concurrently to cloud
+ add_conversation_task = async_client.add(
+ sample_messages, user_id=user_id, metadata={"category": "async_cloud_movies", "session": "async_cloud_demo"}
+ )
+
+ # Create tasks for adding preferences in parallel
+ add_preference_tasks = [
+ async_client.add(pref, user_id=user_id, metadata={"type": "async_cloud_preference", "index": i})
+ for i, pref in enumerate(sample_preferences[:3])
+ ]
+
+ # Execute all add operations concurrently
+ results = await asyncio.gather(add_conversation_task, *add_preference_tasks)
+ for i, result in enumerate(results):
+ print(f"{i+1}. {result}")
+
+ # 2. Concurrent SEARCH operations - multiple cloud searches in parallel
+ search_tasks = [
+ async_client.search("movie preferences", user_id=user_id),
+ async_client.search("food preferences", user_id=user_id),
+ async_client.search("work information", user_id=user_id),
+ ]
+
+ # Execute all searches concurrently
+ search_results = await asyncio.gather(*search_tasks)
+ for i, result in enumerate(search_results):
+ print(f"Search {i+1} result: {result}")
+
+ # 3. GET_ALL operation - retrieve filtered memories from cloud
+ filters = {"AND": [{"user_id": user_id}]}
+ all_memories = await async_client.get_all(filters=filters, limit=10)
+ print(f"Async cloud memories: {all_memories}")
+
+ # Final cleanup - remove all memories asynchronously
+ delete_all_result = await async_client.delete_all(user_id=user_id)
+ print(f"Delete all result: {delete_all_result}")
+
+ agentops.end_trace(end_state="success")
+
+ except Exception:
+ agentops.end_trace(end_state="error")
+
+
+# Sample user data for demonstration
+user_id = "alice_demo"
+agent_id = "assistant_demo"
+run_id = "session_001"
+
+# Sample conversation data demonstrating preference discovery through dialogue
+sample_messages = [
+ {"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
+ {"role": "assistant", "content": "How about a thriller? They can be quite engaging."},
+ {"role": "user", "content": "I'm not a big fan of thriller movies but I love sci-fi movies."},
+ {
+ "role": "assistant",
+ "content": "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future.",
+ },
+]
+
+# Sample user preferences representing various personal attributes
+sample_preferences = [
+ "I prefer dark roast coffee over light roast",
+ "I exercise every morning at 6 AM",
+ "I'm vegetarian and avoid all meat products",
+ "I love reading science fiction novels",
+ "I work in software engineering",
+]
+
+# Execute both sync and async demonstrations
+# Note: The async version typically completes faster due to concurrent operations
+demonstrate_sync_memory_client(sample_messages, sample_preferences, user_id)
+asyncio.run(demonstrate_async_memory_client(sample_messages, sample_preferences, user_id))