diff --git a/docs/concepts/crews.mdx b/docs/concepts/crews.mdx index 43451ca4b3..ec0f190de7 100644 --- a/docs/concepts/crews.mdx +++ b/docs/concepts/crews.mdx @@ -22,7 +22,8 @@ A crew in crewAI represents a collaborative group of agents working together to | **Max RPM** _(optional)_ | `max_rpm` | Maximum requests per minute the crew adheres to during execution. Defaults to `None`. | | **Language** _(optional)_ | `language` | Language used for the crew, defaults to English. | | **Language File** _(optional)_ | `language_file` | Path to the language file to be used for the crew. | -| **Memory** _(optional)_ | `memory` | Utilized for storing execution memories (short-term, long-term, entity memory). Defaults to `False`. | +| **Memory** _(optional)_ | `memory` | Utilized for storing execution memories (short-term, long-term, entity memory). | +| **Memory Config** _(optional)_ | `memory_config` | Configuration for the memory provider to be used by the crew. | | **Cache** _(optional)_ | `cache` | Specifies whether to use a cache for storing the results of tools' execution. Defaults to `True`. | | **Embedder** _(optional)_ | `embedder` | Configuration for the embedder to be used by the crew. Mostly used by memory for now. Default is `{"provider": "openai"}`. | | **Full Output** _(optional)_ | `full_output` | Whether the crew should return the full output with all tasks outputs or just the final output. Defaults to `False`. | diff --git a/docs/concepts/memory.mdx b/docs/concepts/memory.mdx index bda9f3401a..a7677cec11 100644 --- a/docs/concepts/memory.mdx +++ b/docs/concepts/memory.mdx @@ -18,6 +18,7 @@ reason, and learn from past interactions. | **Long-Term Memory** | Preserves valuable insights and learnings from past executions, allowing agents to build and refine their knowledge over time. | | **Entity Memory** | Captures and organizes information about entities (people, places, concepts) encountered during tasks, facilitating deeper understanding and relationship mapping. Uses `RAG` for storing entity information. | | **Contextual Memory**| Maintains the context of interactions by combining `ShortTermMemory`, `LongTermMemory`, and `EntityMemory`, aiding in the coherence and relevance of agent responses over a sequence of tasks or a conversation. | +| **User Memory** | Stores user-specific information and preferences, enhancing personalization and user experience. | ## How Memory Systems Empower Agents @@ -92,6 +93,47 @@ my_crew = Crew( ) ``` +## Integrating Mem0 for Enhanced User Memory + +[Mem0](https://mem0.ai/) is a self-improving memory layer for LLM applications, enabling personalized AI experiences. + +To include user-specific memory you can get your API key [here](https://app.mem0.ai/dashboard/api-keys) and refer the [docs](https://docs.mem0.ai/platform/quickstart#4-1-create-memories) for adding user preferences. + + +```python Code +import os +from crewai import Crew, Process +from mem0 import MemoryClient + +# Set environment variables for Mem0 +os.environ["MEM0_API_KEY"] = "m0-xx" + +# Step 1: Record preferences based on past conversation or user input +client = MemoryClient() +messages = [ + {"role": "user", "content": "Hi there! I'm planning a vacation and could use some advice."}, + {"role": "assistant", "content": "Hello! I'd be happy to help with your vacation planning. What kind of destination do you prefer?"}, + {"role": "user", "content": "I am more of a beach person than a mountain person."}, + {"role": "assistant", "content": "That's interesting. Do you like hotels or Airbnb?"}, + {"role": "user", "content": "I like Airbnb more."}, +] +client.add(messages, user_id="john") + +# Step 2: Create a Crew with User Memory + +crew = Crew( + agents=[...], + tasks=[...], + verbose=True, + process=Process.sequential, + memory=True, + memory_config={ + "provider": "mem0", + "config": {"user_id": "john"}, + }, +) +``` + ## Additional Embedding Providers diff --git a/poetry.lock b/poetry.lock index 8ba39f6c31..094b846643 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1597,12 +1597,12 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" @@ -4286,8 +4286,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" diff --git a/pyproject.toml b/pyproject.toml index 8a5d2f1b96..66adfee3e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ Repository = "https://github.com/crewAIInc/crewAI" [project.optional-dependencies] tools = ["crewai-tools>=0.14.0"] agentops = ["agentops>=0.3.0"] +mem0 = ["mem0ai>=0.1.29"] [tool.uv] dev-dependencies = [ diff --git a/src/crewai/agent.py b/src/crewai/agent.py index fc43137a25..4e9a0685f5 100644 --- a/src/crewai/agent.py +++ b/src/crewai/agent.py @@ -262,9 +262,11 @@ def execute_task( if self.crew and self.crew.memory: contextual_memory = ContextualMemory( + self.crew.memory_config, self.crew._short_term_memory, self.crew._long_term_memory, self.crew._entity_memory, + self.crew._user_memory, ) memory = contextual_memory.build_context_for_task(task, context) if memory.strip() != "": diff --git a/src/crewai/crew.py b/src/crewai/crew.py index 7bcaa82adf..04820adf82 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -27,6 +27,7 @@ from crewai.memory.entity.entity_memory import EntityMemory from crewai.memory.long_term.long_term_memory import LongTermMemory from crewai.memory.short_term.short_term_memory import ShortTermMemory +from crewai.memory.user.user_memory import UserMemory from crewai.process import Process from crewai.task import Task from crewai.tasks.conditional_task import ConditionalTask @@ -71,6 +72,7 @@ class Crew(BaseModel): manager_llm: The language model that will run manager agent. manager_agent: Custom agent that will be used as manager. memory: Whether the crew should use memory to store memories of it's execution. + memory_config: Configuration for the memory to be used for the crew. cache: Whether the crew should use a cache to store the results of the tools execution. function_calling_llm: The language model that will run the tool calling for all the agents. process: The process flow that the crew will follow (e.g., sequential, hierarchical). @@ -94,6 +96,7 @@ class Crew(BaseModel): _short_term_memory: Optional[InstanceOf[ShortTermMemory]] = PrivateAttr() _long_term_memory: Optional[InstanceOf[LongTermMemory]] = PrivateAttr() _entity_memory: Optional[InstanceOf[EntityMemory]] = PrivateAttr() + _user_memory: Optional[InstanceOf[UserMemory]] = PrivateAttr() _train: Optional[bool] = PrivateAttr(default=False) _train_iteration: Optional[int] = PrivateAttr() _inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None) @@ -114,6 +117,10 @@ class Crew(BaseModel): default=False, description="Whether the crew should use memory to store memories of it's execution", ) + memory_config: Optional[Dict[str, Any]] = Field( + default=None, + description="Configuration for the memory to be used for the crew.", + ) short_term_memory: Optional[InstanceOf[ShortTermMemory]] = Field( default=None, description="An Instance of the ShortTermMemory to be used by the Crew", @@ -126,7 +133,11 @@ class Crew(BaseModel): default=None, description="An Instance of the EntityMemory to be used by the Crew", ) - embedder: Optional[Any] = Field( + user_memory: Optional[InstanceOf[UserMemory]] = Field( + default=None, + description="An instance of the UserMemory to be used by the Crew to store/fetch memories of a specific user.", + ) + embedder: Optional[dict] = Field( default=None, description="Configuration for the embedder to be used for the crew.", ) @@ -238,13 +249,22 @@ def create_crew_memory(self) -> "Crew": self._short_term_memory = ( self.short_term_memory if self.short_term_memory - else ShortTermMemory(crew=self, embedder_config=self.embedder) + else ShortTermMemory( + crew=self, + embedder_config=self.embedder, + ) ) self._entity_memory = ( self.entity_memory if self.entity_memory else EntityMemory(crew=self, embedder_config=self.embedder) ) + if hasattr(self, "memory_config") and self.memory_config is not None: + self._user_memory = ( + self.user_memory if self.user_memory else UserMemory(crew=self) + ) + else: + self._user_memory = None return self @model_validator(mode="after") diff --git a/src/crewai/memory/__init__.py b/src/crewai/memory/__init__.py index 8182bede76..3f7ca2ad6e 100644 --- a/src/crewai/memory/__init__.py +++ b/src/crewai/memory/__init__.py @@ -1,5 +1,6 @@ from .entity.entity_memory import EntityMemory from .long_term.long_term_memory import LongTermMemory from .short_term.short_term_memory import ShortTermMemory +from .user.user_memory import UserMemory -__all__ = ["EntityMemory", "LongTermMemory", "ShortTermMemory"] +__all__ = ["UserMemory", "EntityMemory", "LongTermMemory", "ShortTermMemory"] diff --git a/src/crewai/memory/contextual/contextual_memory.py b/src/crewai/memory/contextual/contextual_memory.py index 5d91cf47d6..9598fe6eeb 100644 --- a/src/crewai/memory/contextual/contextual_memory.py +++ b/src/crewai/memory/contextual/contextual_memory.py @@ -1,13 +1,25 @@ -from typing import Optional +from typing import Optional, Dict, Any -from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory +from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory, UserMemory class ContextualMemory: - def __init__(self, stm: ShortTermMemory, ltm: LongTermMemory, em: EntityMemory): + def __init__( + self, + memory_config: Optional[Dict[str, Any]], + stm: ShortTermMemory, + ltm: LongTermMemory, + em: EntityMemory, + um: UserMemory, + ): + if memory_config is not None: + self.memory_provider = memory_config.get("provider") + else: + self.memory_provider = None self.stm = stm self.ltm = ltm self.em = em + self.um = um def build_context_for_task(self, task, context) -> str: """ @@ -23,6 +35,8 @@ def build_context_for_task(self, task, context) -> str: context.append(self._fetch_ltm_context(task.description)) context.append(self._fetch_stm_context(query)) context.append(self._fetch_entity_context(query)) + if self.memory_provider == "mem0": + context.append(self._fetch_user_context(query)) return "\n".join(filter(None, context)) def _fetch_stm_context(self, query) -> str: @@ -32,7 +46,10 @@ def _fetch_stm_context(self, query) -> str: """ stm_results = self.stm.search(query) formatted_results = "\n".join( - [f"- {result['context']}" for result in stm_results] + [ + f"- {result['memory'] if self.memory_provider == 'mem0' else result['context']}" + for result in stm_results + ] ) return f"Recent Insights:\n{formatted_results}" if stm_results else "" @@ -62,6 +79,26 @@ def _fetch_entity_context(self, query) -> str: """ em_results = self.em.search(query) formatted_results = "\n".join( - [f"- {result['context']}" for result in em_results] # type: ignore # Invalid index type "str" for "str"; expected type "SupportsIndex | slice" + [ + f"- {result['memory'] if self.memory_provider == 'mem0' else result['context']}" + for result in em_results + ] # type: ignore # Invalid index type "str" for "str"; expected type "SupportsIndex | slice" ) return f"Entities:\n{formatted_results}" if em_results else "" + + def _fetch_user_context(self, query: str) -> str: + """ + Fetches and formats relevant user information from User Memory. + Args: + query (str): The search query to find relevant user memories. + Returns: + str: Formatted user memories as bullet points, or an empty string if none found. + """ + user_memories = self.um.search(query) + if not user_memories: + return "" + + formatted_memories = "\n".join( + f"- {result['memory']}" for result in user_memories + ) + return f"User memories/preferences:\n{formatted_memories}" diff --git a/src/crewai/memory/entity/entity_memory.py b/src/crewai/memory/entity/entity_memory.py index 4de0594c7c..134e19bfad 100644 --- a/src/crewai/memory/entity/entity_memory.py +++ b/src/crewai/memory/entity/entity_memory.py @@ -11,21 +11,43 @@ class EntityMemory(Memory): """ def __init__(self, crew=None, embedder_config=None, storage=None): - storage = ( - storage - if storage - else RAGStorage( - type="entities", - allow_reset=True, - embedder_config=embedder_config, - crew=crew, + if hasattr(crew, "memory_config") and crew.memory_config is not None: + self.memory_provider = crew.memory_config.get("provider") + else: + self.memory_provider = None + + if self.memory_provider == "mem0": + try: + from crewai.memory.storage.mem0_storage import Mem0Storage + except ImportError: + raise ImportError( + "Mem0 is not installed. Please install it with `pip install mem0ai`." + ) + storage = Mem0Storage(type="entities", crew=crew) + else: + storage = ( + storage + if storage + else RAGStorage( + type="entities", + allow_reset=False, + embedder_config=embedder_config, + crew=crew, + ) ) - ) super().__init__(storage) def save(self, item: EntityMemoryItem) -> None: # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory" """Saves an entity item into the SQLite storage.""" - data = f"{item.name}({item.type}): {item.description}" + if self.memory_provider == "mem0": + data = f""" + Remember details about the following entity: + Name: {item.name} + Type: {item.type} + Entity Description: {item.description} + """ + else: + data = f"{item.name}({item.type}): {item.description}" super().save(data, item.metadata) def reset(self) -> None: diff --git a/src/crewai/memory/memory.py b/src/crewai/memory/memory.py index d0bcd614fe..4869f8e6bd 100644 --- a/src/crewai/memory/memory.py +++ b/src/crewai/memory/memory.py @@ -23,5 +23,12 @@ def save( self.storage.save(value, metadata) - def search(self, query: str) -> List[Dict[str, Any]]: - return self.storage.search(query) + def search( + self, + query: str, + limit: int = 3, + score_threshold: float = 0.35, + ) -> List[Any]: + return self.storage.search( + query=query, limit=limit, score_threshold=score_threshold + ) diff --git a/src/crewai/memory/short_term/short_term_memory.py b/src/crewai/memory/short_term/short_term_memory.py index 919fb61150..67a568d63e 100644 --- a/src/crewai/memory/short_term/short_term_memory.py +++ b/src/crewai/memory/short_term/short_term_memory.py @@ -14,13 +14,27 @@ class ShortTermMemory(Memory): """ def __init__(self, crew=None, embedder_config=None, storage=None): - storage = ( - storage - if storage - else RAGStorage( - type="short_term", embedder_config=embedder_config, crew=crew + if hasattr(crew, "memory_config") and crew.memory_config is not None: + self.memory_provider = crew.memory_config.get("provider") + else: + self.memory_provider = None + + if self.memory_provider == "mem0": + try: + from crewai.memory.storage.mem0_storage import Mem0Storage + except ImportError: + raise ImportError( + "Mem0 is not installed. Please install it with `pip install mem0ai`." + ) + storage = Mem0Storage(type="short_term", crew=crew) + else: + storage = ( + storage + if storage + else RAGStorage( + type="short_term", embedder_config=embedder_config, crew=crew + ) ) - ) super().__init__(storage) def save( @@ -30,11 +44,20 @@ def save( agent: Optional[str] = None, ) -> None: item = ShortTermMemoryItem(data=value, metadata=metadata, agent=agent) + if self.memory_provider == "mem0": + item.data = f"Remember the following insights from Agent run: {item.data}" super().save(value=item.data, metadata=item.metadata, agent=item.agent) - def search(self, query: str, score_threshold: float = 0.35): - return self.storage.search(query=query, score_threshold=score_threshold) # type: ignore # BUG? The reference is to the parent class, but the parent class does not have this parameters + def search( + self, + query: str, + limit: int = 3, + score_threshold: float = 0.35, + ): + return self.storage.search( + query=query, limit=limit, score_threshold=score_threshold + ) # type: ignore # BUG? The reference is to the parent class, but the parent class does not have this parameters def reset(self) -> None: try: diff --git a/src/crewai/memory/storage/interface.py b/src/crewai/memory/storage/interface.py index 8fbe10b034..8bec9a14f2 100644 --- a/src/crewai/memory/storage/interface.py +++ b/src/crewai/memory/storage/interface.py @@ -7,8 +7,10 @@ class Storage: def save(self, value: Any, metadata: Dict[str, Any]) -> None: pass - def search(self, key: str) -> List[Dict[str, Any]]: # type: ignore - pass + def search( + self, query: str, limit: int, score_threshold: float + ) -> Dict[str, Any] | List[Any]: + return {} def reset(self) -> None: pass diff --git a/src/crewai/memory/storage/mem0_storage.py b/src/crewai/memory/storage/mem0_storage.py new file mode 100644 index 0000000000..34aab97161 --- /dev/null +++ b/src/crewai/memory/storage/mem0_storage.py @@ -0,0 +1,104 @@ +import os +from typing import Any, Dict, List + +from mem0 import MemoryClient +from crewai.memory.storage.interface import Storage + + +class Mem0Storage(Storage): + """ + Extends Storage to handle embedding and searching across entities using Mem0. + """ + + def __init__(self, type, crew=None): + super().__init__() + + if type not in ["user", "short_term", "long_term", "entities"]: + raise ValueError("Invalid type for Mem0Storage. Must be 'user' or 'agent'.") + + self.memory_type = type + self.crew = crew + self.memory_config = crew.memory_config + + # User ID is required for user memory type "user" since it's used as a unique identifier for the user. + user_id = self._get_user_id() + if type == "user" and not user_id: + raise ValueError("User ID is required for user memory type") + + # API key in memory config overrides the environment variable + mem0_api_key = self.memory_config.get("config", {}).get("api_key") or os.getenv( + "MEM0_API_KEY" + ) + self.memory = MemoryClient(api_key=mem0_api_key) + + def _sanitize_role(self, role: str) -> str: + """ + Sanitizes agent roles to ensure valid directory names. + """ + return role.replace("\n", "").replace(" ", "_").replace("/", "_") + + def save(self, value: Any, metadata: Dict[str, Any]) -> None: + user_id = self._get_user_id() + agent_name = self._get_agent_name() + if self.memory_type == "user": + self.memory.add(value, user_id=user_id, metadata={**metadata}) + elif self.memory_type == "short_term": + agent_name = self._get_agent_name() + self.memory.add( + value, agent_id=agent_name, metadata={"type": "short_term", **metadata} + ) + elif self.memory_type == "long_term": + agent_name = self._get_agent_name() + self.memory.add( + value, + agent_id=agent_name, + infer=False, + metadata={"type": "long_term", **metadata}, + ) + elif self.memory_type == "entities": + entity_name = None + self.memory.add( + value, user_id=entity_name, metadata={"type": "entity", **metadata} + ) + + def search( + self, + query: str, + limit: int = 3, + score_threshold: float = 0.35, + ) -> List[Any]: + params = {"query": query, "limit": limit} + if self.memory_type == "user": + user_id = self._get_user_id() + params["user_id"] = user_id + elif self.memory_type == "short_term": + agent_name = self._get_agent_name() + params["agent_id"] = agent_name + params["metadata"] = {"type": "short_term"} + elif self.memory_type == "long_term": + agent_name = self._get_agent_name() + params["agent_id"] = agent_name + params["metadata"] = {"type": "long_term"} + elif self.memory_type == "entities": + agent_name = self._get_agent_name() + params["agent_id"] = agent_name + params["metadata"] = {"type": "entity"} + + # Discard the filters for now since we create the filters + # automatically when the crew is created. + results = self.memory.search(**params) + return [r for r in results if r["score"] >= score_threshold] + + def _get_user_id(self): + if self.memory_type == "user": + if hasattr(self, "memory_config") and self.memory_config is not None: + return self.memory_config.get("config", {}).get("user_id") + else: + return None + return None + + def _get_agent_name(self): + agents = self.crew.agents if self.crew else [] + agents = [self._sanitize_role(agent.role) for agent in agents] + agents = "_".join(agents) + return agents diff --git a/src/crewai/memory/user/__init__.py b/src/crewai/memory/user/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/crewai/memory/user/user_memory.py b/src/crewai/memory/user/user_memory.py new file mode 100644 index 0000000000..25e36617c7 --- /dev/null +++ b/src/crewai/memory/user/user_memory.py @@ -0,0 +1,45 @@ +from typing import Any, Dict, Optional + +from crewai.memory.memory import Memory + + +class UserMemory(Memory): + """ + UserMemory class for handling user memory storage and retrieval. + Inherits from the Memory class and utilizes an instance of a class that + adheres to the Storage for data storage, specifically working with + MemoryItem instances. + """ + + def __init__(self, crew=None): + try: + from crewai.memory.storage.mem0_storage import Mem0Storage + except ImportError: + raise ImportError( + "Mem0 is not installed. Please install it with `pip install mem0ai`." + ) + storage = Mem0Storage(type="user", crew=crew) + super().__init__(storage) + + def save( + self, + value, + metadata: Optional[Dict[str, Any]] = None, + agent: Optional[str] = None, + ) -> None: + # TODO: Change this function since we want to take care of the case where we save memories for the usr + data = f"Remember the details about the user: {value}" + super().save(data, metadata) + + def search( + self, + query: str, + limit: int = 3, + score_threshold: float = 0.35, + ): + results = super().search( + query=query, + limit=limit, + score_threshold=score_threshold, + ) + return results diff --git a/src/crewai/memory/user/user_memory_item.py b/src/crewai/memory/user/user_memory_item.py new file mode 100644 index 0000000000..288c1544a4 --- /dev/null +++ b/src/crewai/memory/user/user_memory_item.py @@ -0,0 +1,8 @@ +from typing import Any, Dict, Optional + + +class UserMemoryItem: + def __init__(self, data: Any, user: str, metadata: Optional[Dict[str, Any]] = None): + self.data = data + self.user = user + self.metadata = metadata if metadata is not None else {} diff --git a/tests/memory/cassettes/test_save_and_search_with_provider.yaml b/tests/memory/cassettes/test_save_and_search_with_provider.yaml new file mode 100644 index 0000000000..c30f3f0655 --- /dev/null +++ b/tests/memory/cassettes/test_save_and_search_with_provider.yaml @@ -0,0 +1,270 @@ +interactions: +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + host: + - api.mem0.ai + user-agent: + - python-httpx/0.27.0 + method: GET + uri: https://api.mem0.ai/v1/memories/?user_id=test + response: + body: + string: '[]' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8b477138bad847b9-BOM + Connection: + - keep-alive + Content-Length: + - '2' + Content-Type: + - application/json + Date: + - Sat, 17 Aug 2024 06:00:11 GMT + NEL: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=uuyH2foMJVDpV%2FH52g1q%2FnvXKe3dBKVzvsK0mqmSNezkiszNR9OgrEJfVqmkX%2FlPFRP2sH4zrOuzGo6k%2FjzsjYJczqSWJUZHN2pPujiwnr1E9W%2BdLGKmG6%2FqPrGYAy2SBRWkkJVWsTO3OQ%3D%3D"}],"group":"cf-nel","max_age":604800}' + Server: + - cloudflare + allow: + - GET, POST, DELETE, OPTIONS + alt-svc: + - h3=":443"; ma=86400 + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + vary: + - Accept, origin, Cookie + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 200 + message: OK +- request: + body: '{"batch": [{"properties": {"python_version": "3.12.4 (v3.12.4:8e8a4baf65, + Jun 6 2024, 17:33:18) [Clang 13.0.0 (clang-1300.0.29.30)]", "os": "darwin", + "os_version": "Darwin Kernel Version 23.4.0: Wed Feb 21 21:44:54 PST 2024; root:xnu-10063.101.15~2/RELEASE_ARM64_T6030", + "os_release": "23.4.0", "processor": "arm", "machine": "arm64", "function": + "mem0.client.main.MemoryClient", "$lib": "posthog-python", "$lib_version": "3.5.0", + "$geoip_disable": true}, "timestamp": "2024-08-17T06:00:11.526640+00:00", "context": + {}, "distinct_id": "fd411bd3-99a2-42d6-acd7-9fca8ad09580", "event": "client.init"}], + "historical_migration": false, "sentAt": "2024-08-17T06:00:11.701621+00:00", + "api_key": "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '740' + Content-Type: + - application/json + User-Agent: + - posthog-python/3.5.0 + method: POST + uri: https://us.i.posthog.com/batch/ + response: + body: + string: '{"status":"Ok"}' + headers: + Connection: + - keep-alive + Content-Length: + - '15' + Content-Type: + - application/json + Date: + - Sat, 17 Aug 2024 06:00:12 GMT + access-control-allow-credentials: + - 'true' + server: + - envoy + vary: + - origin, access-control-request-method, access-control-request-headers + x-envoy-upstream-service-time: + - '69' + status: + code: 200 + message: OK +- request: + body: '{"messages": [{"role": "user", "content": "Remember the following insights + from Agent run: test value with provider"}], "metadata": {"task": "test_task_provider", + "agent": "test_agent_provider"}, "app_id": "Researcher"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '219' + content-type: + - application/json + host: + - api.mem0.ai + user-agent: + - python-httpx/0.27.0 + method: POST + uri: https://api.mem0.ai/v1/memories/ + response: + body: + string: '{"message":"ok"}' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8b477140282547b9-BOM + Connection: + - keep-alive + Content-Length: + - '16' + Content-Type: + - application/json + Date: + - Sat, 17 Aug 2024 06:00:13 GMT + NEL: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=FRjJKSk3YxVj03wA7S05H8ts35KnWfqS3wb6Rfy4kVZ4BgXfw7nJbm92wI6vEv5fWcAcHVnOlkJDggs11B01BMuB2k3a9RqlBi0dJNiMuk%2Bgm5xE%2BODMPWJctYNRwQMjNVbteUpS%2Fad8YA%3D%3D"}],"group":"cf-nel","max_age":604800}' + Server: + - cloudflare + allow: + - GET, POST, DELETE, OPTIONS + alt-svc: + - h3=":443"; ma=86400 + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + vary: + - Accept, origin, Cookie + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 200 + message: OK +- request: + body: '{"query": "test value with provider", "limit": 3, "app_id": "Researcher"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '73' + content-type: + - application/json + host: + - api.mem0.ai + user-agent: + - python-httpx/0.27.0 + method: POST + uri: https://api.mem0.ai/v1/memories/search/ + response: + body: + string: '[]' + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8b47714d083b47b9-BOM + Connection: + - keep-alive + Content-Length: + - '2' + Content-Type: + - application/json + Date: + - Sat, 17 Aug 2024 06:00:14 GMT + NEL: + - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}' + Report-To: + - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=2DRWL1cdKdMvnE8vx1fPUGeTITOgSGl3N5g84PS6w30GRqpfz79BtSx6REhpnOiFV8kM6KGqln0iCZ5yoHc2jBVVJXhPJhQ5t0uerD9JFnkphjISrJOU1MJjZWneT9PlNABddxvVNCmluA%3D%3D"}],"group":"cf-nel","max_age":604800}' + Server: + - cloudflare + allow: + - POST, OPTIONS + alt-svc: + - h3=":443"; ma=86400 + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + vary: + - Accept, origin, Cookie + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 200 + message: OK +- request: + body: '{"batch": [{"properties": {"python_version": "3.12.4 (v3.12.4:8e8a4baf65, + Jun 6 2024, 17:33:18) [Clang 13.0.0 (clang-1300.0.29.30)]", "os": "darwin", + "os_version": "Darwin Kernel Version 23.4.0: Wed Feb 21 21:44:54 PST 2024; root:xnu-10063.101.15~2/RELEASE_ARM64_T6030", + "os_release": "23.4.0", "processor": "arm", "machine": "arm64", "function": + "mem0.client.main.MemoryClient", "$lib": "posthog-python", "$lib_version": "3.5.0", + "$geoip_disable": true}, "timestamp": "2024-08-17T06:00:13.593952+00:00", "context": + {}, "distinct_id": "fd411bd3-99a2-42d6-acd7-9fca8ad09580", "event": "client.add"}], + "historical_migration": false, "sentAt": "2024-08-17T06:00:13.858277+00:00", + "api_key": "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '739' + Content-Type: + - application/json + User-Agent: + - posthog-python/3.5.0 + method: POST + uri: https://us.i.posthog.com/batch/ + response: + body: + string: '{"status":"Ok"}' + headers: + Connection: + - keep-alive + Content-Length: + - '15' + Content-Type: + - application/json + Date: + - Sat, 17 Aug 2024 06:00:13 GMT + access-control-allow-credentials: + - 'true' + server: + - envoy + vary: + - origin, access-control-request-method, access-control-request-headers + x-envoy-upstream-service-time: + - '33' + status: + code: 200 + message: OK +version: 1