From 1aed611539529f9084ab0d7406017ec4a7508290 Mon Sep 17 00:00:00 2001
From: Prateek Chhikara <46902268+prateekchhikara@users.noreply.github.com>
Date: Wed, 19 Mar 2025 09:51:15 -0700
Subject: [PATCH] Added graph memory (#2403)
---
docs/docs.json | 1 +
docs/features/graph-memory.mdx | 295 ++++++++++++++++++++++++++++
docs/features/platform-overview.mdx | 3 +
docs/images/graph-platform.png | Bin 0 -> 73900 bytes
4 files changed, 299 insertions(+)
create mode 100644 docs/features/graph-memory.mdx
create mode 100644 docs/images/graph-platform.png
diff --git a/docs/docs.json b/docs/docs.json
index 75b73aae..19067e81 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -56,6 +56,7 @@
"features/async-client",
"features/memory-export",
"features/webhooks",
+ "features/graph-memory",
"features/feedback-mechanism"
]
}
diff --git a/docs/features/graph-memory.mdx b/docs/features/graph-memory.mdx
new file mode 100644
index 00000000..e4f37c3c
--- /dev/null
+++ b/docs/features/graph-memory.mdx
@@ -0,0 +1,295 @@
+---
+title: Graph Memory
+icon: "circle-nodes"
+iconType: "solid"
+description: "Enable graph-based memory retrieval for more contextually relevant results"
+---
+
+## Overview
+
+Graph Memory enhances memory pipeline by creating relationships between entities in your data. It builds a network of interconnected information for more contextually relevant search results.
+
+This feature allows your AI applications to understand connections between entities, providing richer context for responses. It's ideal for applications needing relationship tracking and nuanced information retrieval across related memories.
+
+## How Graph Memory Works
+
+The Graph Memory feature analyzes how each entity connects and relates to each other. When enabled:
+
+1. Mem0 automatically builds a graph representation of entities
+2. Retrieval considers graph relationships between entities
+3. Results include entities that may be contextually important even if they're not direct semantic matches
+
+## Using Graph Memory
+
+To use Graph Memory, you need to enable it in your API calls by setting the `enable_graph=True` parameter. You'll also need to specify `output_format="v1.1"` to receive the enriched response format.
+
+### Adding Memories with Graph Memory
+
+When adding new memories, enable Graph Memory to automatically build relationships with existing memories:
+
+
+
+```python Python
+from mem0 import MemoryClient
+
+client = MemoryClient(
+ api_key="your-api-key",
+ org_id="your-org-id",
+ project_id="your-project-id"
+)
+
+messages = [
+ {"role": "user", "content": "My name is Joseph"},
+ {"role": "assistant", "content": "Hello Joseph, it's nice to meet you!"},
+ {"role": "user", "content": "I'm from Seattle and I work as a software engineer"}
+]
+
+# Enable graph memory when adding
+client.add(
+ messages,
+ user_id="joseph",
+ version="v1",
+ enable_graph=True,
+ output_format="v1.1"
+)
+```
+
+```javascript JavaScript
+import { MemoryClient } from "mem0";
+
+const client = new MemoryClient({
+ apiKey: "your-api-key",
+ orgId: "your-org-id",
+ projectId: "your-project-id"
+});
+
+const messages = [
+ { role: "user", content: "My name is Joseph" },
+ { role: "assistant", content: "Hello Joseph, it's nice to meet you!" },
+ { role: "user", content: "I'm from Seattle and I work as a software engineer" }
+];
+
+// Enable graph memory when adding
+await client.add({
+ messages,
+ userId: "joseph",
+ version: "v1",
+ enableGraph: true,
+ outputFormat: "v1.1"
+});
+```
+
+```json Output
+{
+ "results": [
+ {
+ "memory": "Name is Joseph",
+ "event": "ADD",
+ "id": "4a5a417a-fa10-43b5-8c53-a77c45e80438"
+ },
+ {
+ "memory": "Is from Seattle",
+ "event": "ADD",
+ "id": "8d268d0f-5452-4714-b27d-ae46f676a49d"
+ },
+ {
+ "memory": "Is a software engineer",
+ "event": "ADD",
+ "id": "5f0a184e-ddea-4fe6-9b92-692d6a901df8"
+ }
+ ]
+}
+```
+
+
+The graph memory would look like this:
+
+
+
+
+
+
Graph Memory creates a network of relationships between entities, enabling more contextual retrieval
+
+
+
+Response for the graph memory's `add` operation will not be available directly in the response.
+As adding graph memories is an asynchronous operation due to heavy processing,
+you can use the `get_all()` endpoint to retrieve the memory with the graph metadata.
+
+
+
+### Searching with Graph Memory
+
+When searching memories, Graph Memory helps retrieve entities that are contextually important even if they're not direct semantic matches.
+
+
+
+```python Python
+# Search with graph memory enabled
+results = client.search(
+ "what is my name?",
+ user_id="joseph",
+ enable_graph=True,
+ output_format="v1.1"
+)
+
+print(results)
+```
+
+```javascript JavaScript
+// Search with graph memory enabled
+const results = await client.search({
+ query: "what is my name?",
+ userId: "joseph",
+ enableGraph: true,
+ outputFormat: "v1.1"
+});
+
+console.log(results);
+```
+
+```json Output
+{
+ "results": [
+ {
+ "id": "4a5a417a-fa10-43b5-8c53-a77c45e80438",
+ "memory": "Name is Joseph",
+ "user_id": "joseph",
+ "metadata": null,
+ "categories": ["personal_details"],
+ "immutable": false,
+ "created_at": "2025-03-19T09:09:00.146390-07:00",
+ "updated_at": "2025-03-19T09:09:00.146404-07:00",
+ "score": 0.3621795393335552
+ },
+ {
+ "id": "8d268d0f-5452-4714-b27d-ae46f676a49d",
+ "memory": "Is from Seattle",
+ "user_id": "joseph",
+ "metadata": null,
+ "categories": ["personal_details"],
+ "immutable": false,
+ "created_at": "2025-03-19T09:09:00.170680-07:00",
+ "updated_at": "2025-03-19T09:09:00.170692-07:00",
+ "score": 0.31212713194651254
+ }
+ ],
+ "relations": [
+ {
+ "source": "joseph",
+ "source_type": "person",
+ "relationship": "name",
+ "target": "joseph",
+ "target_type": "person",
+ "score": 0.39
+ }
+ ]
+}
+```
+
+
+
+### Retrieving All Memories with Graph Memory
+
+When retrieving all memories, Graph Memory provides additional relationship context:
+
+
+
+```python Python
+# Get all memories with graph context
+memories = client.get_all(
+ user_id="joseph",
+ enable_graph=True,
+ output_format="v1.1"
+)
+
+print(memories)
+```
+
+```javascript JavaScript
+// Get all memories with graph context
+const memories = await client.getAll({
+ userId: "joseph",
+ enableGraph: true,
+ outputFormat: "v1.1"
+});
+
+console.log(memories);
+```
+
+```json Output
+{
+ "results": [
+ {
+ "id": "5f0a184e-ddea-4fe6-9b92-692d6a901df8",
+ "memory": "Is a software engineer",
+ "user_id": "joseph",
+ "metadata": null,
+ "categories": ["professional_details"],
+ "immutable": false,
+ "created_at": "2025-03-19T09:09:00.194116-07:00",
+ "updated_at": "2025-03-19T09:09:00.194128-07:00",
+ },
+ {
+ "id": "8d268d0f-5452-4714-b27d-ae46f676a49d",
+ "memory": "Is from Seattle",
+ "user_id": "joseph",
+ "metadata": null,
+ "categories": ["personal_details"],
+ "immutable": false,
+ "created_at": "2025-03-19T09:09:00.170680-07:00",
+ "updated_at": "2025-03-19T09:09:00.170692-07:00",
+ },
+ {
+ "id": "4a5a417a-fa10-43b5-8c53-a77c45e80438",
+ "memory": "Name is Joseph",
+ "user_id": "joseph",
+ "metadata": null,
+ "categories": ["personal_details"],
+ "immutable": false,
+ "created_at": "2025-03-19T09:09:00.146390-07:00",
+ "updated_at": "2025-03-19T09:09:00.146404-07:00",
+ }
+ ],
+ "relations": [
+ {
+ "source": "joseph",
+ "source_type": "person",
+ "relationship": "name",
+ "target": "joseph",
+ "target_type": "person"
+ },
+ {
+ "source": "joseph",
+ "source_type": "person",
+ "relationship": "city",
+ "target": "seattle",
+ "target_type": "city"
+ },
+ {
+ "source": "joseph",
+ "source_type": "person",
+ "relationship": "job",
+ "target": "software engineer",
+ "target_type": "job"
+ }
+ ]
+}
+```
+
+
+
+## Best Practices
+
+- Enable Graph Memory for applications where understanding context and relationships between memories is important
+- Graph Memory works best with a rich history of related conversations
+- Consider Graph Memory for long-running assistants that need to track evolving information
+
+## Performance Considerations
+
+Graph Memory requires additional processing and may increase response times slightly for very large memory stores. However, for most use cases, the improved retrieval quality outweighs the minimal performance impact.
+
+If you have any questions, please feel free to reach out to us using one of the following methods:
+
+
+
diff --git a/docs/features/platform-overview.mdx b/docs/features/platform-overview.mdx
index e5280d26..8400adbd 100644
--- a/docs/features/platform-overview.mdx
+++ b/docs/features/platform-overview.mdx
@@ -36,6 +36,9 @@ Learn about the key features and capabilities that make Mem0 a powerful platform
Export memories in structured formats using customizable Pydantic schemas.
+
+ Add memories in the form of nodes and edges in a graph database and search for related memories.
+
## Getting Help
diff --git a/docs/images/graph-platform.png b/docs/images/graph-platform.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d386a055141bc3521c51d9298f457d3a2c45846
GIT binary patch
literal 73900
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