Memory Management Guide
This guide covers best practices for managing memories in your application using the Papr Memory API. We'll explore strategies for organizing, updating, and maintaining your memory store effectively.
Memory Structure
A memory in Papr consists of:
interface Memory {
content: string; // The main content
type: "text" | "code_snippet" | "document"; // Content type
metadata?: {
topics?: string[]; // Topics as string array
hierarchical_structures?: string; // Navigation path (e.g., "Business/Planning/Product")
createdAt?: string; // ISO timestamp
location?: string; // Optional location context
emoji_tags?: string[]; // Emoji representations
emotion_tags?: string[]; // Emotional context
conversationId?: string; // For linking related memories
sourceUrl?: string; // Original source if applicable
// Access control fields
external_user_id?: string;
external_user_read_access?: string[];
external_user_write_access?: string[];
};
context?: Array<{
role: "user" | "assistant";
content: string;
}>;
relationships_json?: Array<{
relation_type: string;
related_item_id?: string;
metadata?: Record<string, any>;
}>;
}
Organizing Memories
Using Metadata Effectively
- Topics
const memory = {
content: "Discussion about new feature requirements",
type: "text",
metadata: {
topics: ["product development", "feature planning", "user feedback"],
hierarchical_structures: "Product/Features/Requirements"
}
};
- Emotional Context
const memory = {
content: "Team celebration of successful launch",
type: "text",
metadata: {
emoji_tags: ["🎉", "🚀", "🎯"],
emotion_tags: ["excited", "accomplished", "proud"]
}
};
- Conversation Threading
const memory = {
content: "Follow-up on feature discussion",
type: "text",
metadata: {
conversationId: "feat-123-discussion",
sourceUrl: "https://meeting-notes.company.com/123"
}
};
- Access Control
const memory = {
content: "Confidential project update",
type: "text",
metadata: {
external_user_id: "user_123",
external_user_read_access: ["user_123", "user_456", "user_789"],
external_user_write_access: ["user_123"]
}
};
Memory Lifecycle Management
1. Creating Memories
Always validate and format your content before creation:
function prepareMemory(content: string, type: string) {
return {
content: content.trim(),
type,
metadata: {
createdAt: new Date().toISOString(),
topics: extractTopics(content), // Your topic extraction logic
hierarchical_structures: determineHierarchy(content) // Your hierarchy logic
}
};
}
const memory = prepareMemory("Meeting notes...", "text");
const response = await fetch('https://memory.papr.ai/v1/memory', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify(memory)
});
2. Updating Memories
Keep track of memory changes:
const updateMemory = async (memoryId: string, updates: Partial<Memory>) => {
const response = await fetch(`https://memory.papr.ai/v1/memory/${memoryId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
...updates,
metadata: {
...updates.metadata,
updatedAt: new Date().toISOString()
}
})
});
return response.json();
};
3. Batch Processing
Group related memories for efficient processing:
You can set up webhooks to receive notifications when batch processing completes. This is especially useful for large batches that may take time to process asynchronously.
const batchAddMemories = async (memories: Memory[]) => {
// Group memories by type
const groupedMemories = memories.reduce((acc, memory) => {
acc[memory.type] = acc[memory.type] || [];
acc[memory.type].push(memory);
return acc;
}, {});
// Process each group
for (const [type, typeMemories] of Object.entries(groupedMemories)) {
const response = await fetch('https://memory.papr.ai/v1/memory/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
memories: typeMemories,
batch_size: 10,
webhook_url: "https://your-app.com/webhooks/batch-complete" // Webhook for batch completion notification
})
});
const result = await response.json();
handleBatchResults(result); // Your result handling logic
}
};
// Example webhook handler implementation
const handleBatchWebhook = async (req, res) => {
const batchData = req.body;
console.log(`Batch ${batchData.batch_id} completed: ${batchData.total_successful} successful, ${batchData.total_failed} failed`);
// Update your application state based on completion
await updateBatchStatus(batchData.batch_id, batchData.status);
res.status(200).send({ received: true });
};
Search and Retrieval Strategies
1. Semantic Search
Write detailed queries for better results:
const searchMemories = async (topic: string, context: string) => {
const response = await fetch('https://memory.papr.ai/v1/memory/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
query: `Find detailed discussions about ${topic} that include ${context}. Focus on items with specific examples or decisions made.`,
enable_agentic_graph: true // Enable intelligent context-aware search
})
});
return response.json();
};
2. Result Processing
Handle search results effectively:
interface SearchResult {
memories: Memory[];
nodes: Node[]; // Graph nodes if available
}
function processSearchResults(results: SearchResult) {
// Group by hierarchical structure
const groupedResults = results.memories.reduce((acc, memory) => {
const hierarchy = memory.metadata?.hierarchical_structures || 'Uncategorized';
acc[hierarchy] = acc[hierarchy] || [];
acc[hierarchy].push(memory);
return acc;
}, {});
// Sort within each group by date
for (const hierarchy in groupedResults) {
groupedResults[hierarchy].sort((a, b) => {
return new Date(b.metadata.createdAt) - new Date(a.metadata.createdAt);
});
}
return groupedResults;
}
3. Providing Feedback
Improve future search results by providing feedback:
const submitFeedback = async (searchId: string, isHelpful: boolean, details?: string) => {
const response = await fetch('https://memory.papr.ai/v1/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name'
},
body: JSON.stringify({
search_id: searchId,
feedbackData: {
feedbackType: isHelpful ? 'thumbs_up' : 'thumbs_down',
feedbackSource: 'inline',
feedbackText: details
}
})
});
return response.json();
};
// After receiving search results
const searchResponse = await searchMemories('product roadmap', 'Q2 planning');
const searchId = searchResponse.search_id;
// Later, after user interaction
submitFeedback(searchId, true, "This was very helpful for our planning meeting");
Error Handling and Monitoring
Implement robust error handling:
async function safeMemoryOperation<T>(operation: () => Promise<T>): Promise<T> {
const maxRetries = 3;
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (error.status === 429) { // Rate limit
const retryAfter = parseInt(error.headers.get('Retry-After') || '5');
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (error.status >= 500) { // Server error
await new Promise(resolve => setTimeout(resolve, attempt * 1000));
continue;
}
throw error; // Client error or other - don't retry
}
}
throw lastError;
}
// Usage
try {
const memory = await safeMemoryOperation(() =>
client.memory.add({
content: "Important content",
type: "text"
})
);
console.log(`Memory added: ${memory.data[0].memoryId}`);
} catch (error) {
console.error("Failed to add memory", error);
// Handle specific error codes
if (error.code === 403) {
console.log("Subscription limit reached. Please upgrade.");
} else if (error.code === 413) {
console.log("Content too large. Please reduce size.");
}
}
Memory Management Best Practices
Content Organization
- Use consistent hierarchical structures
- Apply meaningful topic tags
- Add emotional context when relevant
Batch Processing
- Group related memories in batches
- Use appropriate batch sizes (10-20 items)
- Handle partial success scenarios
Access Control
- Use external_user_id for easy identification
- Set appropriate read/write access lists
- Review access periodically
Performance Optimization
- Use Accept-Encoding: gzip header
- Implement client-side caching
- Enable agentic graph for better search results
Next Steps
- Learn about Batch Processing
- Explore Search Tuning
- See the complete API Reference