aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDhravya Shah <[email protected]>2025-10-01 18:11:37 -0700
committerDhravya Shah <[email protected]>2025-10-01 18:11:37 -0700
commit5c575785737b06beafa09943ecff13be6027d2b7 (patch)
tree887d95ae179814a93fbba6df0f81115f3fc53f61
parentfeat: Claude memory integration (diff)
downloadsupermemory-5c575785737b06beafa09943ecff13be6027d2b7.tar.xz
supermemory-5c575785737b06beafa09943ecff13be6027d2b7.zip
feat: Add memory vs rag and migration section to docs
-rw-r--r--apps/docs/docs.json15
-rw-r--r--apps/docs/memory-vs-rag.mdx239
-rw-r--r--apps/docs/migration/from-mem0.mdx303
-rw-r--r--apps/docs/migration/mem0-migration-script.py337
-rw-r--r--packages/tools/src/claude-memory.ts143
5 files changed, 986 insertions, 51 deletions
diff --git a/apps/docs/docs.json b/apps/docs/docs.json
index e658df17..217e74fb 100644
--- a/apps/docs/docs.json
+++ b/apps/docs/docs.json
@@ -71,7 +71,11 @@
"pages": [
{
"group": "Getting Started",
- "pages": ["intro", "routervsapi", "quickstart"]
+ "pages": ["intro", "routervsapi", "quickstart", "memory-vs-rag"]
+ },
+ {
+ "group": "Migration Guides",
+ "pages": ["migration/from-mem0"]
},
{
"group": "Memory API",
@@ -83,7 +87,7 @@
"pages": [
"add-memories/overview",
"add-memories/parameters",
- "memory-api/ingesting",
+ "api/ingesting",
{
"group": "Examples",
"pages": [
@@ -112,7 +116,7 @@
]
},
"search/filtering",
- "memory-api/track-progress",
+ "api/track-progress",
{
"group": "List Memories",
"icon": "list",
@@ -185,11 +189,6 @@
"pages": ["memory-api/sdks/openai-plugins", "ai-sdk/npm"]
},
{
- "group": "Anthropic SDK",
- "icon": "sparkles",
- "pages": ["memory-api/sdks/anthropic-claude-memory"]
- },
- {
"group": "AI SDK",
"icon": "triangle",
"pages": [
diff --git a/apps/docs/memory-vs-rag.mdx b/apps/docs/memory-vs-rag.mdx
new file mode 100644
index 00000000..699b906d
--- /dev/null
+++ b/apps/docs/memory-vs-rag.mdx
@@ -0,0 +1,239 @@
+---
+title: "Memory vs RAG: Understanding the Difference"
+description: "Learn why agent memory and RAG are fundamentally different, and when to use each approach"
+sidebarTitle: "Memory vs RAG"
+---
+
+Most developers confuse RAG (Retrieval-Augmented Generation) with agent memory. They're not the same thing, and using RAG for memory is why your agents keep forgetting important context. Let's understand the fundamental difference.
+
+## The Core Problem
+
+When building AI agents, developers often treat memory as just another retrieval problem. They store conversations in a vector database, embed queries, and hope semantic search will surface the right context.
+
+**This approach fails because memory isn't about finding similar textβ€”it's about understanding relationships, temporal context, and user state over time.**
+
+## Documents vs Memories in Supermemory
+
+Supermemory makes a clear distinction between these two concepts:
+
+### Documents: Raw Knowledge
+Documents are the raw content you send to Supermemoryβ€”PDFs, web pages, text files. They represent static knowledge that doesn't change based on who's accessing it.
+
+**Characteristics:**
+- **Stateless**: A document about Python programming is the same for everyone
+- **Unversioned**: Content doesn't track changes over time
+- **Universal**: Not linked to specific users or entities
+- **Searchable**: Perfect for semantic similarity search
+
+**Use Cases:**
+- Company knowledge bases
+- Technical documentation
+- Research papers
+- General reference material
+
+### Memories: Contextual Understanding
+Memories are the insights, preferences, and relationships extracted from documents and conversations. They're tied to specific users or entities and evolve over time.
+
+**Characteristics:**
+- **Stateful**: "User prefers dark mode" is specific to that user
+- **Temporal**: Tracks when facts became true or invalid
+- **Personal**: Linked to users, sessions, or entities
+- **Relational**: Understands connections between facts
+
+**Use Cases:**
+- User preferences and history
+- Conversation context
+- Personal facts and relationships
+- Behavioral patterns
+
+## Why RAG Fails as Memory
+
+Let's look at a real scenario that illustrates the problem:
+
+<Tabs>
+ <Tab title="The Scenario">
+ ```
+ Day 1: "I love Adidas sneakers"
+ Day 30: "My Adidas broke after a month, terrible quality"
+ Day 31: "I'm switching to Puma"
+ Day 45: "What sneakers should I buy?"
+ ```
+ </Tab>
+
+ <Tab title="RAG Approach (Wrong)">
+ ```python
+ # RAG sees these as isolated embeddings
+ query = "What sneakers should I buy?"
+
+ # Semantic search finds closest match
+ result = vector_search(query)
+ # Returns: "I love Adidas sneakers" (highest similarity)
+
+ # Agent recommends Adidas 🀦
+ ```
+
+ **Problem**: RAG finds the most semantically similar text but misses the temporal progression and causal relationships.
+ </Tab>
+
+ <Tab title="Memory Approach (Right)">
+ ```python
+ # Supermemory understands temporal context
+ query = "What sneakers should I buy?"
+
+ # Memory retrieval considers:
+ # 1. Temporal validity (Adidas preference is outdated)
+ # 2. Causal relationships (broke β†’ disappointment β†’ switch)
+ # 3. Current state (now prefers Puma)
+
+ # Agent correctly recommends Puma βœ…
+ ```
+
+ **Solution**: Memory systems track when facts become invalid and understand causal chains.
+ </Tab>
+</Tabs>
+
+## The Technical Difference
+
+### RAG: Semantic Similarity
+```
+Query β†’ Embedding β†’ Vector Search β†’ Top-K Results β†’ LLM
+```
+
+RAG excels at finding information that's semantically similar to your query. It's statelessβ€”each query is independent.
+
+### Memory: Contextual Graph
+```
+Query β†’ Entity Recognition β†’ Graph Traversal β†’ Temporal Filtering β†’ Context Assembly β†’ LLM
+```
+
+Memory systems build a knowledge graph that understands:
+- **Entities**: Users, products, concepts
+- **Relationships**: Preferences, ownership, causality
+- **Temporal Context**: When facts were true
+- **Invalidation**: When facts became outdated
+
+## When to Use Each
+
+<CardGroup cols={2}>
+ <Card title="Use RAG For" icon="search">
+ - Static documentation
+ - Knowledge bases
+ - Research queries
+ - General Q&A
+ - Content that doesn't change per user
+ </Card>
+
+ <Card title="Use Memory For" icon="brain">
+ - User preferences
+ - Conversation history
+ - Personal facts
+ - Behavioral patterns
+ - Anything that evolves over time
+ </Card>
+</CardGroup>
+
+## Real-World Examples
+
+### E-commerce Assistant
+
+<Tabs>
+ <Tab title="RAG Component">
+ Stores product catalogs, specifications, reviews
+
+ ```python
+ # Good for RAG
+ "What are the specs of iPhone 15?"
+ "Compare Nike and Adidas running shoes"
+ "Show me waterproof jackets"
+ ```
+ </Tab>
+
+ <Tab title="Memory Component">
+ Tracks user preferences, purchase history, interactions
+
+ ```python
+ # Needs Memory
+ "What size do I usually wear?"
+ "Did I like my last purchase?"
+ "What's my budget preference?"
+ ```
+ </Tab>
+</Tabs>
+
+### Customer Support Bot
+
+<Tabs>
+ <Tab title="RAG Component">
+ FAQ documents, troubleshooting guides, policies
+
+ ```python
+ # Good for RAG
+ "How do I reset my password?"
+ "What's your return policy?"
+ "Troubleshooting WiFi issues"
+ ```
+ </Tab>
+
+ <Tab title="Memory Component">
+ Previous issues, user account details, conversation context
+
+ ```python
+ # Needs Memory
+ "Is my issue from last week resolved?"
+ "What plan am I on?"
+ "You were helping me with..."
+ ```
+ </Tab>
+</Tabs>
+
+## How Supermemory Handles Both
+
+Supermemory provides a unified platform that correctly handles both patterns:
+
+### 1. Document Storage (RAG)
+```python
+# Add a document for RAG-style retrieval
+client.memories.add(
+ content="iPhone 15 has a 48MP camera and A17 Pro chip",
+ # No user association - universal knowledge
+)
+```
+
+### 2. Memory Creation
+```python
+# Add a user-specific memory
+client.memories.add(
+ content="User prefers Android over iOS",
+ container_tags=["user_123"], # User-specific
+ metadata={
+ "type": "preference",
+ "confidence": "high"
+ }
+)
+```
+
+### 3. Hybrid Retrieval
+```python
+# Search combines both approaches
+results = client.memories.search(
+ query="What phone should I recommend?",
+ container_tags=["user_123"], # Gets user memories
+ # Also searches general knowledge
+)
+
+# Results include:
+# - User's Android preference (memory)
+# - Latest Android phone specs (documents)
+```
+
+## The Bottom Line
+
+<Note>
+**Key Insight**: RAG answers "What do I know?" while Memory answers "What do I remember about you?"
+</Note>
+
+Stop treating memory like a retrieval problem. Your agents need both:
+- **RAG** for accessing knowledge
+- **Memory** for understanding users
+
+Supermemory provides both capabilities in a unified platform, ensuring your agents have the right context at the right time.
diff --git a/apps/docs/migration/from-mem0.mdx b/apps/docs/migration/from-mem0.mdx
new file mode 100644
index 00000000..e24a5548
--- /dev/null
+++ b/apps/docs/migration/from-mem0.mdx
@@ -0,0 +1,303 @@
+---
+title: "Migrating from Mem0.ai to Supermemory"
+description: "Complete guide to migrate your data and applications from Mem0.ai to Supermemory"
+sidebarTitle: "From Mem0"
+---
+
+Migrating from Mem0.ai to Supermemory is straightforward. This guide walks you through exporting your memories from Mem0 and importing them into Supermemory.
+
+## Why Migrate to Supermemory?
+
+Supermemory offers enhanced capabilities over Mem0.ai:
+- **Memory Router** for zero-code LLM integration
+- **Knowledge graph** architecture for better context relationships
+- **Multiple content types** (URLs, PDFs, images, videos)
+- **Generous free tier** (100k tokens) with affordable pricing
+- **Multiple integration options** (API, Router, MCP, SDKs)
+
+## Quick Migration (All-in-One)
+
+Complete migration in one script:
+
+```python
+from mem0 import MemoryClient
+from supermemory import Supermemory
+import json, time
+
+# Export from Mem0
+mem0 = MemoryClient(api_key="your_mem0_api_key")
+export = mem0.create_memory_export(
+ schema={"type": "object", "properties": {"memories": {"type": "array", "items": {"type": "object"}}}},
+ filters={}
+)
+time.sleep(5)
+data = mem0.get_memory_export(memory_export_id=export["id"])
+
+# Import to Supermemory
+supermemory = Supermemory(api_key="your_supermemory_api_key")
+for memory in data["memories"]:
+ if memory.get("content"):
+ supermemory.memories.add(
+ content=memory["content"],
+ container_tags=["imported_from_mem0"]
+ )
+ print(f"βœ… {memory['content'][:50]}...")
+
+print("Migration complete!")
+```
+
+## Step-by-Step Migration
+
+<Steps>
+ <Step title="Export from Mem0.ai">
+ Mem0 provides two ways to export your memories:
+
+ ### Option 1: Export via Dashboard (Recommended)
+ 1. Log into your [Mem0 dashboard](https://app.mem0.ai)
+ 2. Navigate to the export section
+ 3. Download your memories as JSON
+
+ ### Option 2: Export via API
+
+ Simple script to export all your memories from Mem0:
+
+ ```python
+ from mem0 import MemoryClient
+ import json
+ import time
+
+ # Connect to Mem0
+ client = MemoryClient(api_key="your_mem0_api_key")
+
+ # Create export job
+ schema = {
+ "type": "object",
+ "properties": {
+ "memories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {"type": "string"},
+ "content": {"type": "string"},
+ "metadata": {"type": "object"},
+ "created_at": {"type": "string"}
+ }
+ }
+ }
+ }
+ }
+
+ response = client.create_memory_export(schema=schema, filters={})
+ export_id = response["id"]
+
+ # Wait and retrieve
+ print("Exporting memories...")
+ time.sleep(5)
+ export_data = client.get_memory_export(memory_export_id=export_id)
+
+ # Save to file
+ with open("mem0_export.json", "w") as f:
+ json.dump(export_data, f, indent=2)
+
+ print(f"Exported {len(export_data['memories'])} memories")
+ ```
+ </Step>
+
+ <Step title="Set Up Supermemory">
+ Create your Supermemory account and get your API key:
+
+ 1. Sign up at [console.supermemory.ai](https://console.supermemory.ai)
+ 2. Create a new project
+ 3. Generate an API key from the dashboard
+
+ ```bash
+ # Set your environment variable
+ export SUPERMEMORY_API_KEY="your_supermemory_api_key"
+ ```
+ </Step>
+
+ <Step title="Import to Supermemory">
+ Simple script to import your Mem0 memories into Supermemory:
+
+ ```python
+ import json
+ from supermemory import Supermemory
+
+ # Load your Mem0 export
+ with open("mem0_export.json", "r") as f:
+ mem0_data = json.load(f)
+
+ # Connect to Supermemory
+ client = Supermemory(api_key="your_supermemory_api_key")
+
+ # Import memories
+ for memory in mem0_data["memories"]:
+ content = memory.get("content", "")
+
+ # Skip empty memories
+ if not content:
+ continue
+
+ # Import to Supermemory
+ try:
+ result = client.memories.add(
+ content=content,
+ container_tags=["imported_from_mem0"],
+ metadata={
+ "source": "mem0",
+ "created_at": memory.get("created_at"),
+ **(memory.get("metadata") or {})
+ }
+ )
+ print(f"Imported: {content[:50]}...")
+ except Exception as e:
+ print(f"Failed: {e}")
+
+ print("Migration complete!")
+ ```
+ </Step>
+</Steps>
+
+## API Migration Reference
+
+Here's how common Mem0.ai operations map to Supermemory:
+
+### Adding Memories
+
+<CodeGroup>
+
+```python Mem0.ai
+from mem0 import MemoryClient
+
+client = MemoryClient(api_key="...")
+client.add(
+ messages="User prefers dark mode",
+ user_id="alice"
+)
+```
+
+```python Supermemory
+from supermemory import Supermemory
+
+client = Supermemory(api_key="...")
+client.memories.add(
+ content="User prefers dark mode",
+ container_tags=["user_alice"]
+)
+```
+
+</CodeGroup>
+
+### Searching Memories
+
+<CodeGroup>
+
+```python Mem0.ai
+results = client.search(
+ query="user preferences",
+ user_id="alice"
+)
+```
+
+```python Supermemory
+results = client.memories.search(
+ query="user preferences",
+ container_tags=["user_alice"]
+)
+```
+
+</CodeGroup>
+
+### Getting All Memories
+
+<CodeGroup>
+
+```python Mem0.ai
+memories = client.get_all(
+ user_id="alice"
+)
+```
+
+```python Supermemory
+memories = client.memories.list(
+ container_tags=["user_alice"],
+ limit=100
+)
+```
+
+</CodeGroup>
+
+### Deleting Memories
+
+<CodeGroup>
+
+```python Mem0.ai
+client.delete(memory_id="mem_123")
+```
+
+```python Supermemory
+client.memories.delete("mem_123")
+```
+
+</CodeGroup>
+
+## Using Memory Router (Easiest Migration)
+
+For the simplest migration path, use Supermemory's Memory Router which requires minimal code changes:
+
+<CodeGroup>
+
+```python Before (Mem0 + OpenAI)
+from openai import OpenAI
+from mem0 import MemoryClient
+
+# Two separate clients needed
+openai = OpenAI(api_key="sk-...")
+memory = MemoryClient(api_key="mem0_key")
+
+# Manual memory management
+context = memory.search("user preferences", user_id="alice")
+messages = [
+ {"role": "system", "content": f"Context: {context}"},
+ {"role": "user", "content": "What are my preferences?"}
+]
+
+response = openai.chat.completions.create(
+ model="gpt-4",
+ messages=messages
+)
+```
+
+```python After (Supermemory Router)
+from openai import OpenAI
+
+# Single client with automatic memory management
+client = OpenAI(
+ api_key="sk-...",
+ base_url="https://api.supermemory.ai/v3/https://api.openai.com/v1",
+ default_headers={
+ "x-supermemory-api-key": "your_supermemory_key",
+ "x-supermemory-user-id": "alice"
+ }
+)
+
+# Memories handled automatically!
+response = client.chat.completions.create(
+ model="gpt-4",
+ messages=[{"role": "user", "content": "What are my preferences?"}]
+)
+```
+
+</CodeGroup>
+
+<Note>
+For enterprise migrations, [contact us](mailto:[email protected]) for assistance.
+</Note>
+
+## Next Steps
+
+1. [Explore](/how-it-works) how Supermemory works
+2. Read the [quickstart](/quickstart) and add and retrieve your first memories
+3. [Connect](/connectors/overview) to Google Drive, Notion, and OneDrive with automatic syncing
+
diff --git a/apps/docs/migration/mem0-migration-script.py b/apps/docs/migration/mem0-migration-script.py
new file mode 100644
index 00000000..7b05edf6
--- /dev/null
+++ b/apps/docs/migration/mem0-migration-script.py
@@ -0,0 +1,337 @@
+#!/usr/bin/env python3
+"""
+Mem0.ai to Supermemory Migration Script
+========================================
+Simple script to migrate memories from Mem0.ai to Supermemory.
+
+Prerequisites:
+1. Install required packages:
+ pip install mem0ai supermemory python-dotenv
+
+2. Set environment variables:
+ export MEM0_API_KEY="your_mem0_api_key"
+ export MEM0_ORG_ID="your_org_id" # Optional
+ export MEM0_PROJECT_ID="your_project_id" # Optional
+ export SUPERMEMORY_API_KEY="your_supermemory_api_key"
+
+Usage:
+ python mem0-migration-script.py
+"""
+
+import os
+import json
+import time
+from datetime import datetime
+from typing import Dict, Any, Optional
+from mem0 import MemoryClient
+from supermemory import Supermemory
+from dotenv import load_dotenv
+
+# Load environment variables
+load_dotenv()
+
+def export_from_mem0(
+ api_key: str,
+ org_id: Optional[str] = None,
+ project_id: Optional[str] = None,
+ filters: Optional[Dict] = None
+) -> Dict[str, Any]:
+ """
+ Export memories from Mem0.ai using their export API
+ """
+ print("πŸ”„ Starting Mem0.ai export...")
+
+ # Initialize Mem0 client
+ client = MemoryClient(
+ api_key=api_key,
+ org_id=org_id,
+ project_id=project_id
+ )
+
+ # Define export schema - this matches what Mem0 actually returns
+ export_schema = {
+ "type": "object",
+ "properties": {
+ "memories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {"type": "string"},
+ "content": {"type": "string"},
+ "user_id": {"type": "string"},
+ "agent_id": {"type": "string"},
+ "app_id": {"type": "string"},
+ "run_id": {"type": "string"},
+ "metadata": {"type": "object"},
+ "created_at": {"type": "string"},
+ "updated_at": {"type": "string"}
+ }
+ }
+ }
+ }
+ }
+
+ try:
+ # Step 1: Create export job
+ print("πŸ“€ Creating export job...")
+ export_response = client.create_memory_export(
+ schema=export_schema,
+ filters=filters if filters else {}
+ )
+
+ export_id = export_response.get("id")
+ print(f"βœ… Export job created with ID: {export_id}")
+
+ # Step 2: Wait for export to complete
+ print("⏳ Waiting for export to complete...")
+ time.sleep(5) # Usually takes a few seconds
+
+ # Step 3: Retrieve the exported data using the correct method
+ print("πŸ“₯ Retrieving exported data...")
+ export_data = client.get_memory_export(memory_export_id=export_id)
+
+ # Step 4: Save backup
+ backup_filename = f"mem0_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
+ with open(backup_filename, "w") as f:
+ json.dump(export_data, f, indent=2)
+ print(f"πŸ’Ύ Backup saved to: {backup_filename}")
+
+ memory_count = len(export_data.get("memories", []))
+ print(f"βœ… Successfully exported {memory_count} memories from Mem0.ai")
+
+ # Show sample of exported data
+ if memory_count > 0:
+ print("\nπŸ“‹ Sample exported memory:")
+ sample = export_data["memories"][0]
+ print(f" Content: {sample.get('content', 'N/A')[:50]}...")
+ print(f" ID: {sample.get('id', 'None')}")
+ print(f" User ID: {sample.get('user_id', 'None')}")
+
+ return export_data
+
+ except Exception as e:
+ print(f"❌ Error exporting from Mem0: {str(e)}")
+ raise
+
+def import_to_supermemory(mem0_data: Dict[str, Any], api_key: str) -> Dict[str, int]:
+ """
+ Import Mem0 memories into Supermemory
+ """
+ print("\nπŸš€ Starting import to Supermemory...")
+
+ # Initialize Supermemory client
+ client = Supermemory(api_key=api_key)
+
+ memories = mem0_data.get("memories", [])
+ if not memories:
+ print("⚠️ No memories found to import")
+ return {"imported": 0, "failed": 0, "skipped": 0}
+
+ # Statistics
+ stats = {
+ "imported": 0,
+ "failed": 0,
+ "skipped": 0
+ }
+
+ print(f"πŸ“¦ Processing {len(memories)} memories...")
+
+ for i, memory in enumerate(memories, 1):
+ try:
+ # Check if content exists
+ content = memory.get("content", "").strip()
+ if not content:
+ print(f"⚠️ [{i}/{len(memories)}] Skipping: No content")
+ stats["skipped"] += 1
+ continue
+
+ # Build container tags
+ container_tags = ["imported_from_mem0"]
+
+ # Add user tag if present (handle None values)
+ user_id = memory.get("user_id")
+ if user_id and user_id != "None":
+ container_tags.append(f"user_{user_id}")
+
+ # Add agent tag if present
+ agent_id = memory.get("agent_id")
+ if agent_id and agent_id != "None":
+ container_tags.append(f"agent_{agent_id}")
+
+ # Add app tag if present
+ app_id = memory.get("app_id")
+ if app_id and app_id != "None":
+ container_tags.append(f"app_{app_id}")
+
+ # Add session tag if present
+ session_id = memory.get("session_id")
+ if session_id and session_id != "None":
+ container_tags.append(f"session_{session_id}")
+
+ # Generate a unique ID if Mem0 didn't provide one
+ memory_id = memory.get("id")
+ if not memory_id or memory_id == "None":
+ # Use content hash for uniqueness
+ import hashlib
+ memory_id = hashlib.md5(content.encode()).hexdigest()[:8]
+
+ # Prepare metadata
+ metadata = {
+ "source": "mem0_migration",
+ "migration_date": datetime.now().isoformat()
+ }
+
+ # Add original ID if it existed
+ if memory.get("id") and memory["id"] != "None":
+ metadata["original_id"] = memory["id"]
+
+ # Add timestamps if available and not None
+ created_at = memory.get("created_at")
+ if created_at and created_at != "None":
+ metadata["original_created_at"] = created_at
+
+ updated_at = memory.get("updated_at")
+ if updated_at and updated_at != "None":
+ metadata["original_updated_at"] = updated_at
+
+ # Add hash information if available
+ hash_val = memory.get("hash")
+ if hash_val and hash_val != "None":
+ metadata["original_hash"] = hash_val
+
+ prev_hash = memory.get("prev_hash")
+ if prev_hash and prev_hash != "None":
+ metadata["original_prev_hash"] = prev_hash
+
+ # Merge with existing metadata if it's a valid dict
+ if memory.get("metadata") and isinstance(memory["metadata"], dict):
+ metadata.update(memory["metadata"])
+
+ # Import to Supermemory
+ result = client.memories.add(
+ content=content,
+ container_tags=container_tags,
+ custom_id=f"mem0_{memory_id}",
+ metadata=metadata
+ )
+
+ stats["imported"] += 1
+ print(f"βœ… [{i}/{len(memories)}] Imported: {content[:50]}...")
+
+ # Small delay to avoid rate limiting
+ if i % 10 == 0:
+ time.sleep(0.5)
+
+ except Exception as e:
+ stats["failed"] += 1
+ print(f"❌ [{i}/{len(memories)}] Failed: {str(e)}")
+
+ return stats
+
+def verify_migration(api_key: str, expected_count: int):
+ """
+ Verify that memories were imported correctly
+ """
+ print("\nπŸ” Verifying migration...")
+
+ client = Supermemory(api_key=api_key)
+
+ try:
+ # Check imported memories
+ result = client.memories.list(
+ container_tags=["imported_from_mem0"],
+ limit=100
+ )
+
+ total_imported = result['pagination']['totalItems']
+ print(f"βœ… Found {total_imported} imported memories in Supermemory")
+
+ # Show sample memories
+ if result['memories']:
+ print("\nπŸ“‹ Sample imported memories:")
+ for memory in result['memories'][:3]:
+ print(f" - {memory['id']}: {memory.get('summary', 'No summary')[:50]}...")
+
+ # Check success rate
+ success_rate = (total_imported / expected_count * 100) if expected_count > 0 else 0
+ print(f"\nπŸ“Š Migration success rate: {success_rate:.1f}%")
+
+ return total_imported
+
+ except Exception as e:
+ print(f"❌ Error during verification: {str(e)}")
+ return 0
+
+def main():
+ """Main migration function"""
+ print("=" * 60)
+ print("🎯 Mem0.ai to Supermemory Migration Tool")
+ print("=" * 60)
+
+ # Get credentials from environment
+ mem0_api_key = os.getenv("MEM0_API_KEY")
+ mem0_org_id = os.getenv("MEM0_ORG_ID")
+ mem0_project_id = os.getenv("MEM0_PROJECT_ID")
+ supermemory_api_key = os.getenv("SUPERMEMORY_API_KEY")
+
+ # Validate credentials
+ if not mem0_api_key:
+ print("❌ Error: MEM0_API_KEY environment variable not set")
+ return
+
+ if not supermemory_api_key:
+ print("❌ Error: SUPERMEMORY_API_KEY environment variable not set")
+ return
+
+ try:
+ # Step 1: Export from Mem0
+ print("\nπŸ“€ STEP 1: Export from Mem0.ai")
+ print("-" * 40)
+
+ # You can add filters here if needed
+ # Example: filters = {"AND": [{"user_id": "specific_user"}]}
+ filters = None
+
+ mem0_data = export_from_mem0(
+ api_key=mem0_api_key,
+ org_id=mem0_org_id,
+ project_id=mem0_project_id,
+ filters=filters
+ )
+
+ # Step 2: Import to Supermemory
+ print("\nπŸ“₯ STEP 2: Import to Supermemory")
+ print("-" * 40)
+
+ stats = import_to_supermemory(mem0_data, supermemory_api_key)
+
+ # Step 3: Verify migration
+ print("\nβœ”οΈ STEP 3: Verification")
+ print("-" * 40)
+
+ expected_count = len(mem0_data.get("memories", []))
+ verify_migration(supermemory_api_key, expected_count)
+
+ # Final summary
+ print("\n" + "=" * 60)
+ print("πŸ“Š MIGRATION SUMMARY")
+ print("=" * 60)
+ print(f"πŸ“€ Exported from Mem0: {expected_count}")
+ print(f"βœ… Successfully imported: {stats['imported']}")
+ print(f"⚠️ Skipped (no content): {stats['skipped']}")
+ print(f"❌ Failed: {stats['failed']}")
+
+ if stats['imported'] == expected_count - stats['skipped']:
+ print("\nπŸŽ‰ Migration completed successfully!")
+ elif stats['imported'] > 0:
+ print("\n⚠️ Migration completed with some issues. Check the logs above.")
+ else:
+ print("\n❌ Migration failed. Please check your credentials and try again.")
+
+ except Exception as e:
+ print(f"\n❌ Migration error: {str(e)}")
+ print("Please check your credentials and network connection.")
+
+if __name__ == "__main__":
+ main() \ No newline at end of file
diff --git a/packages/tools/src/claude-memory.ts b/packages/tools/src/claude-memory.ts
index 49ff39a7..54da77a1 100644
--- a/packages/tools/src/claude-memory.ts
+++ b/packages/tools/src/claude-memory.ts
@@ -1,6 +1,6 @@
import Supermemory from "supermemory"
-import type { SupermemoryToolsConfig } from "./types"
import { getContainerTags } from "./shared"
+import type { SupermemoryToolsConfig } from "./types"
// Claude Memory Tool Types
export interface ClaudeMemoryConfig extends SupermemoryToolsConfig {
@@ -92,24 +92,45 @@ export class ClaudeMemoryTool {
return await this.view(command.path, command.view_range)
case "create":
if (!command.file_text) {
- return { success: false, error: "file_text is required for create command" }
+ return {
+ success: false,
+ error: "file_text is required for create command",
+ }
}
return await this.create(command.path, command.file_text)
case "str_replace":
if (!command.old_str || !command.new_str) {
- return { success: false, error: "old_str and new_str are required for str_replace command" }
+ return {
+ success: false,
+ error: "old_str and new_str are required for str_replace command",
+ }
}
- return await this.strReplace(command.path, command.old_str, command.new_str)
+ return await this.strReplace(
+ command.path,
+ command.old_str,
+ command.new_str,
+ )
case "insert":
if (command.insert_line === undefined || !command.insert_text) {
- return { success: false, error: "insert_line and insert_text are required for insert command" }
+ return {
+ success: false,
+ error:
+ "insert_line and insert_text are required for insert command",
+ }
}
- return await this.insert(command.path, command.insert_line, command.insert_text)
+ return await this.insert(
+ command.path,
+ command.insert_line,
+ command.insert_text,
+ )
case "delete":
return await this.delete(command.path)
case "rename":
if (!command.new_path) {
- return { success: false, error: "new_path is required for rename command" }
+ return {
+ success: false,
+ error: "new_path is required for rename command",
+ }
}
return await this.rename(command.path, command.new_path)
default:
@@ -129,14 +150,17 @@ export class ClaudeMemoryTool {
/**
* Handle command and return properly formatted tool result
*/
- async handleCommandForToolResult(command: MemoryCommand, toolUseId: string): Promise<MemoryToolResult> {
+ async handleCommandForToolResult(
+ command: MemoryCommand,
+ toolUseId: string,
+ ): Promise<MemoryToolResult> {
const response = await this.handleCommand(command)
return {
type: "tool_result",
tool_use_id: toolUseId,
content: response.success
- ? (response.content || "Operation completed successfully")
+ ? response.content || "Operation completed successfully"
: `Error: ${response.error}`,
is_error: !response.success,
}
@@ -145,7 +169,10 @@ export class ClaudeMemoryTool {
/**
* View command: List directory contents or read file with optional line range
*/
- private async view(path: string, viewRange?: [number, number]): Promise<MemoryResponse> {
+ private async view(
+ path: string,
+ viewRange?: [number, number],
+ ): Promise<MemoryResponse> {
// If path ends with / or is exactly /memories, it's a directory listing request
if (path.endsWith("/") || path === "/memories") {
// Normalize path to end with /
@@ -202,10 +229,7 @@ export class ClaudeMemoryTool {
}
// Format directory listing
- const entries = [
- ...Array.from(dirs).sort(),
- ...files.sort()
- ]
+ const entries = [...Array.from(dirs).sort(), ...files.sort()]
if (entries.length === 0) {
return {
@@ -216,7 +240,7 @@ export class ClaudeMemoryTool {
return {
success: true,
- content: `Directory: ${dirPath}\n${entries.map(entry => `- ${entry}`).join('\n')}`,
+ content: `Directory: ${dirPath}\n${entries.map((entry) => `- ${entry}`).join("\n")}`,
}
} catch (error) {
return {
@@ -229,7 +253,10 @@ export class ClaudeMemoryTool {
/**
* Read file contents with optional line range
*/
- private async readFile(filePath: string, viewRange?: [number, number]): Promise<MemoryResponse> {
+ private async readFile(
+ filePath: string,
+ viewRange?: [number, number],
+ ): Promise<MemoryResponse> {
try {
const normalizedId = this.normalizePathToCustomId(filePath)
@@ -241,7 +268,9 @@ export class ClaudeMemoryTool {
})
// Try to find exact match by customId
- const exactMatch = response.results?.find(r => r.customId === normalizedId)
+ const exactMatch = response.results?.find(
+ (r) => r.documentId === normalizedId,
+ )
const document = exactMatch || response.results?.[0]
if (!document) {
@@ -251,29 +280,31 @@ export class ClaudeMemoryTool {
}
}
- let content = document.raw || document.content || ""
+ let content = document.content || ""
// Apply line range if specified
if (viewRange) {
- const lines = content.split('\n')
+ const lines = content.split("\n")
const [startLine, endLine] = viewRange
const selectedLines = lines.slice(startLine - 1, endLine)
// Format with line numbers
- const numberedLines = selectedLines.map((line, index) => {
- const lineNum = startLine + index
- return `${lineNum.toString().padStart(4)}\t${line}`
- })
-
- content = numberedLines.join('\n')
+ const numberedLines = selectedLines.map(
+ (line: string, index: number) => {
+ const lineNum = startLine + index
+ return `${lineNum.toString().padStart(4)}\t${line}`
+ },
+ )
+
+ content = numberedLines.join("\n")
} else {
// Format all lines with line numbers
- const lines = content.split('\n')
+ const lines = content.split("\n")
const numberedLines = lines.map((line, index) => {
const lineNum = index + 1
return `${lineNum.toString().padStart(4)}\t${line}`
})
- content = numberedLines.join('\n')
+ content = numberedLines.join("\n")
}
return {
@@ -291,7 +322,10 @@ export class ClaudeMemoryTool {
/**
* Create command: Create or overwrite a memory file
*/
- private async create(filePath: string, fileText: string): Promise<MemoryResponse> {
+ private async create(
+ filePath: string,
+ fileText: string,
+ ): Promise<MemoryResponse> {
try {
const normalizedId = this.normalizePathToCustomId(filePath)
@@ -302,7 +336,7 @@ export class ClaudeMemoryTool {
metadata: {
claude_memory_type: "file",
file_path: filePath,
- line_count: fileText.split('\n').length,
+ line_count: fileText.split("\n").length,
created_by: "claude_memory_tool",
last_modified: new Date().toISOString(),
},
@@ -323,7 +357,11 @@ export class ClaudeMemoryTool {
/**
* String replace command: Replace text in existing file
*/
- private async strReplace(filePath: string, oldStr: string, newStr: string): Promise<MemoryResponse> {
+ private async strReplace(
+ filePath: string,
+ oldStr: string,
+ newStr: string,
+ ): Promise<MemoryResponse> {
try {
// First, find and read the existing file
const readResult = await this.getFileDocument(filePath)
@@ -334,7 +372,8 @@ export class ClaudeMemoryTool {
}
}
- const originalContent = readResult.document.raw || readResult.document.content || ""
+ const originalContent =
+ readResult.document.raw || readResult.document.content || ""
// Check if old_str exists in the content
if (!originalContent.includes(oldStr)) {
@@ -355,7 +394,7 @@ export class ClaudeMemoryTool {
containerTags: this.containerTags,
metadata: {
...readResult.document.metadata,
- line_count: newContent.split('\n').length,
+ line_count: newContent.split("\n").length,
last_modified: new Date().toISOString(),
},
})
@@ -375,7 +414,11 @@ export class ClaudeMemoryTool {
/**
* Insert command: Insert text at specific line
*/
- private async insert(filePath: string, insertLine: number, insertText: string): Promise<MemoryResponse> {
+ private async insert(
+ filePath: string,
+ insertLine: number,
+ insertText: string,
+ ): Promise<MemoryResponse> {
try {
// First, find and read the existing file
const readResult = await this.getFileDocument(filePath)
@@ -386,8 +429,9 @@ export class ClaudeMemoryTool {
}
}
- const originalContent = readResult.document.raw || readResult.document.content || ""
- const lines = originalContent.split('\n')
+ const originalContent =
+ readResult.document.raw || readResult.document.content || ""
+ const lines = originalContent.split("\n")
// Validate line number
if (insertLine < 1 || insertLine > lines.length + 1) {
@@ -399,7 +443,7 @@ export class ClaudeMemoryTool {
// Insert the text (insertLine is 1-based)
lines.splice(insertLine - 1, 0, insertText)
- const newContent = lines.join('\n')
+ const newContent = lines.join("\n")
// Update the document
const normalizedId = this.normalizePathToCustomId(filePath)
@@ -409,7 +453,7 @@ export class ClaudeMemoryTool {
containerTags: this.containerTags,
metadata: {
...readResult.document.metadata,
- line_count: newContent.split('\n').length,
+ line_count: newContent.split("\n").length,
last_modified: new Date().toISOString(),
},
})
@@ -459,7 +503,10 @@ export class ClaudeMemoryTool {
/**
* Rename command: Move/rename memory file
*/
- private async rename(oldPath: string, newPath: string): Promise<MemoryResponse> {
+ private async rename(
+ oldPath: string,
+ newPath: string,
+ ): Promise<MemoryResponse> {
try {
// Validate new path
if (!this.isValidPath(newPath)) {
@@ -478,7 +525,8 @@ export class ClaudeMemoryTool {
}
}
- const originalContent = readResult.document.raw || readResult.document.content || ""
+ const originalContent =
+ readResult.document.raw || readResult.document.content || ""
const newNormalizedId = this.normalizePathToCustomId(newPath)
// Create new document with new path
@@ -526,7 +574,9 @@ export class ClaudeMemoryTool {
})
// Try to find exact match by customId first
- const exactMatch = response.results?.find(r => r.customId === normalizedId)
+ const exactMatch = response.results?.find(
+ (r) => r.documentId === normalizedId,
+ )
const document = exactMatch || response.results?.[0]
if (!document) {
@@ -552,13 +602,20 @@ export class ClaudeMemoryTool {
* Validate that path starts with /memories for security
*/
private isValidPath(path: string): boolean {
- return (path.startsWith("/memories/") || path === "/memories") && !path.includes("../") && !path.includes("..\\")
+ return (
+ (path.startsWith("/memories/") || path === "/memories") &&
+ !path.includes("../") &&
+ !path.includes("..\\")
+ )
}
}
/**
* Create a Claude memory tool instance
*/
-export function createClaudeMemoryTool(apiKey: string, config?: ClaudeMemoryConfig) {
+export function createClaudeMemoryTool(
+ apiKey: string,
+ config?: ClaudeMemoryConfig,
+) {
return new ClaudeMemoryTool(apiKey, config)
-} \ No newline at end of file
+}