Last updated

LLM Tool Schema

This documentation provides the schema definitions for integrating Papr memory operations as tool calls in LLM agents. The schemas are defined using Zod and match our official API specification.

Memory Operations

Adding Memories

The add_memory tool allows LLMs to store new memories in the Papr system.

Schema Definition

const addMemorySchema = z.object({
    content: z.string().describe("The content of the memory item you want to add to memory"),
    type: z.enum(['text', 'code_snippet', 'document'])
        .default('text')
        .describe("Content type of the memory item"),
    metadata: z.object({
        topics: z.array(z.string()).optional().describe("List of topics related to the content, context & conversation history"),
        hierarchical_structures: z.string().optional().describe("Hierarchical structures to enable users to navigate from broad topics to more specific ones"),
        emoji_tags: z.array(z.string()).optional().describe("Emoji tags related to the memory item and conversation history"),
        emotion_tags: z.array(z.string()).optional().describe("Emotion tags related to the memory item and conversation history"),
        createdAt: z.string().optional().describe("ISO datetime when the memory was created"),
        sourceUrl: z.string().optional().describe("Link to the original source of the memory"),
        conversationId: z.string().optional().describe("ID of the conversation this memory belongs to"),
        external_user_id: z.string().optional().describe("External user ID for this memory"),
        external_user_read_access: z.array(z.string()).optional().describe("List of external user IDs with read access"),
        external_user_write_access: z.array(z.string()).optional().describe("List of external user IDs with write access"),
    }).passthrough().describe("Metadata for the memory item"), // Allow additional fields
    context: z.array(
        z.object({
            role: z.enum(['user', 'assistant']).describe("Person who created content for a context list"),
            content: z.string().describe("Content of the previous message in conversation history"),
        })
    ).optional().describe("Context for the memory item"),
    relationships_json: z.array(
        z.object({
            relation_type: z.string().describe("Defines the neo4j relationship type"),
            related_item_id: z.string().optional().describe("ID of the related item"),
            related_item_type: z.string().optional().describe("Memory item type inside context to relate"),
            metadata: z.record(z.any()).optional().describe("Metadata for the relation"),
        })
    ).optional().describe("Relationships defining connections between content and context"),
}).required({
    content: true,
    type: true,
});

Tool Implementation

const add_memory = tool({
    description: "Add a new memory item to the Papr system with size validation and background processing.",
    parameters: addMemorySchema,
    execute: async (args) => {
        // Implementation details...
    }
});

Retrieving Memories

The search tool enables LLMs to search and retrieve memories from the Papr system.

Schema Definition

const searchSchema = z.object({
    query: z.string()
        .describe("Detailed search query describing what you're looking for. For best results, write 2-3 sentences that include specific details, context, and time frame."),
    rank_results: z.boolean()
        .default(false)
        .describe("Whether to enable additional ranking of search results. Default is false because results are already ranked when using an LLM for search."),
    enable_agentic_graph: z.boolean()
        .default(true)
        .describe("HIGHLY RECOMMENDED: Enable agentic graph search for intelligent, context-aware results that can understand ambiguous references."),
    user_id: z.string()
        .optional()
        .describe("Optional internal user ID to filter search results by a specific user."),
    external_user_id: z.string()
        .optional()
        .describe("Optional external user ID to filter search results by a specific external user."),
    metadata: z.record(z.any())
        .optional()
        .describe("Optional metadata filter. Any metadata field can be used for filtering."),
    max_memories: z.number()
        .min(10)
        .max(50)
        .default(20)
        .optional()
        .describe("HIGHLY RECOMMENDED: Maximum number of memories to return. Use at least 15-20 for comprehensive results. Lower values (5-10) may miss relevant information. Default is 20 for optimal coverage."),
    max_nodes: z.number()
        .min(10)
        .max(50)
        .default(15)
        .optional()
        .describe("HIGHLY RECOMMENDED: Maximum number of neo nodes to return. Use at least 10-15 for comprehensive graph results. Lower values may miss important entity relationships. Default is 15 for optimal coverage.")
});

Tool Implementation

const search = tool({
    description: "Search through memories with authentication required.",
    parameters: searchSchema,
    execute: async (args) => {
        // Implementation details...
    }
});

Submitting Feedback

The submit_feedback tool enables LLMs to provide feedback on search results to improve future searches.

Schema Definition

const feedbackDataSchema = z.object({
    feedbackType: z.enum([
        'thumbs_up', 
        'thumbs_down', 
        'rating', 
        'correction', 
        'report', 
        'copy_action', 
        'save_action', 
        'create_document', 
        'memory_relevance',
        'answer_quality'
    ]).describe("Type of feedback being provided"),
    feedbackSource: z.enum([
        'inline', 
        'post_query', 
        'session_end', 
        'memory_citation',
        'answer_panel'
    ]).describe("Where the feedback was provided from"),
    feedbackValue: z.string().optional().describe("Optional value associated with the feedback"),
    feedbackScore: z.number().optional().describe("Optional numerical score for rating feedback types"),
    feedbackText: z.string().optional().describe("Optional text describing the feedback in detail"),
    citedMemoryIds: z.array(z.string()).optional().describe("IDs of memories cited in the response"),
    citedNodeIds: z.array(z.string()).optional().describe("IDs of nodes cited in the response"),
});

const feedbackSchema = z.object({
    search_id: z.string().describe("The search_id from SearchResponse that this feedback relates to"),
    feedbackData: feedbackDataSchema.describe("The feedback data containing all feedback information"),
    external_user_id: z.string().optional().describe("External user ID for developer API keys acting on behalf of end users")
});

Tool Implementation

const submit_feedback = tool({
    description: "Submit feedback on search results to help improve model performance.",
    parameters: feedbackSchema,
    execute: async (args) => {
        // Implementation details...
    }
});

Key Features

  • Schema Validation: Uses Zod for robust type checking and validation that matches our API spec
  • Timeout Management: Handles timeouts appropriately with retries
  • Error Handling: Comprehensive error handling with proper status codes
  • Data Processing: Includes memory data processing and validation
  • Graph Support: Support for Neo4j graph relationships
  • Response Limiting: Automatically limits results based on API constraints
  • Security: Proper authentication handling through API keys or tokens

Best Practices

  1. Metadata Population: Use LLMs to intelligently fill metadata fields based on content
  2. Query Formation: Leverage LLMs to create detailed, contextual queries
  3. Enable Agentic Graph: Always set enable_agentic_graph: true for best search results
  4. Set Appropriate Limits: Use recommended values for max_memories (15-20) and max_nodes (10-15)
  5. Provide Feedback: Use the feedback API to improve future search results
  6. Error Handling: Always implement proper error handling and fallbacks
  7. Authorization: Ensure proper API key management
  8. Timeout Handling: Consider implementing retry logic for timeout scenarios

Example Usage

Here's a complete example of using these tools in an LLM application:

// Adding a memory
const memoryResult = await add_memory.execute({
    content: "Meeting notes from the product planning session",
    type: "text",
    metadata: {
        topics: ["product", "planning"],
        hierarchical_structures: "Business/Planning/Product",
        emoji_tags: ["📊", "💡", "📝"],
        emotion_tags: ["focused", "productive"],
        createdAt: "2024-03-21T10:00:00Z",
        location: "Conference Room A",
        sourceUrl: "https://meeting-notes.example.com/123",
        conversationId: "conv-123",
        external_user_id: "external_user_123",
        external_user_read_access: ["external_user_123", "external_user_789"]
    },
    context: [
        { role: "user", content: "Let's discuss the Q2 product roadmap" },
        { role: "assistant", content: "I'll help you plan the roadmap. What are your key objectives?" }
    ],
    relationships_json: [{
        relation_type: "follows",
        related_item_id: "previous_memory_item_id",
        metadata: {
            relevance: "high"
        }
    }]
});

// Searching memories
const searchResults = await search.execute({
    query: "Find recurring customer complaints about API performance from the last month. Focus on issues where customers specifically mentioned timeout errors or slow response times in their conversations.",
    enable_agentic_graph: true,
    max_memories: 20,
    max_nodes: 15,
    external_user_id: "external_user_123"
});

// Submitting feedback on search results
const feedbackResult = await submit_feedback.execute({
    search_id: searchResults.search_id,
    feedbackData: {
        feedbackType: "thumbs_up",
        feedbackSource: "inline",
        feedbackText: "These search results were exactly what I needed",
        citedMemoryIds: ["mem_123", "mem_456"]
    },
    external_user_id: "external_user_123"
});

LangChain Integration

You can use the Papr Memory schema with LangChain to create agentic tools that manage memory. Here's how to integrate these schemas with LangChain:

Python Implementation

import os
import json
from typing import Dict, List, Optional, Any, Union
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

# Initialize API key from environment
PAPR_API_KEY = os.environ.get("PAPR_MEMORY_API_KEY")

@tool
async def papr_add_memory(
    content: str,
    type: str = "text",
    metadata: Optional[Dict[str, Any]] = None,
    context: Optional[List[Dict[str, str]]] = None
):
    """Add a new memory item to the Papr system with size validation and background processing."""
    import aiohttp
    
    headers = {
        "Content-Type": "application/json",
        "X-API-Key": PAPR_API_KEY,
        "X-Client-Type": "langchain_tool"
    }
    
    payload = {
        "content": content,
        "type": type
    }
    
    if metadata:
        payload["metadata"] = metadata
    
    if context:
        payload["context"] = context
    
    async with aiohttp.ClientSession() as session:
        async with session.post("https://memory.papr.ai/v1/memory", 
                                headers=headers, 
                                json=payload) as response:
            return await response.json()

@tool
async def papr_search_memory(
    query: str,
    enable_agentic_graph: bool = True,
    max_memories: int = 20,
    max_nodes: int = 15,
    metadata: Optional[Dict[str, Any]] = None,
    external_user_id: Optional[str] = None
):
    """
    Search through memories with authentication required.
    
    For best results:
    - Write 2-3 detailed sentences in your query
    - Keep enable_agentic_graph set to True
    - Use recommended values for max_memories (15-20) and max_nodes (10-15)
    """
    import aiohttp
    
    headers = {
        "Content-Type": "application/json",
        "X-API-Key": PAPR_API_KEY,
        "X-Client-Type": "langchain_tool",
        "Accept-Encoding": "gzip"
    }
    
    payload = {
        "query": query,
        "enable_agentic_graph": enable_agentic_graph,
        "max_memories": max_memories,
        "max_nodes": max_nodes
    }
    
    if metadata:
        payload["metadata"] = metadata
        
    if external_user_id:
        payload["external_user_id"] = external_user_id
    
    async with aiohttp.ClientSession() as session:
        async with session.post("https://memory.papr.ai/v1/memory/search", 
                                headers=headers, 
                                json=payload) as response:
            return await response.json()

@tool
async def papr_submit_feedback(
    search_id: str,
    feedback_type: str,
    feedback_source: str = "inline",
    feedback_text: Optional[str] = None,
    feedback_score: Optional[float] = None,
    cited_memory_ids: Optional[List[str]] = None,
    external_user_id: Optional[str] = None
):
    """Submit feedback on search results to help improve model performance."""
    import aiohttp
    
    headers = {
        "Content-Type": "application/json",
        "X-API-Key": PAPR_API_KEY,
        "X-Client-Type": "langchain_tool"
    }
    
    feedback_data = {
        "feedbackType": feedback_type,
        "feedbackSource": feedback_source
    }
    
    if feedback_text:
        feedback_data["feedbackText"] = feedback_text
    
    if feedback_score is not None:
        feedback_data["feedbackScore"] = feedback_score
        
    if cited_memory_ids:
        feedback_data["citedMemoryIds"] = cited_memory_ids
    
    payload = {
        "search_id": search_id,
        "feedbackData": feedback_data
    }
    
    if external_user_id:
        payload["external_user_id"] = external_user_id
    
    async with aiohttp.ClientSession() as session:
        async with session.post("https://memory.papr.ai/v1/feedback", 
                                headers=headers, 
                                json=payload) as response:
            return await response.json()