npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@langgraph-js/memory

v1.4.1

Published

A memory management system based on PostgreSQL + pgvector for LangGraph workflows

Downloads

15

Readme

LangGraph-Memory

A memory management system based on PostgreSQL + pgvector, designed for memory storage and retrieval in LangGraph workflows.

Features

  • Vector Similarity Search: Support for HNSW and IVFFlat indexes for efficient vector similarity search
  • LLM-Powered Memory Management: Intelligent memory merging and deduplication using large language models
  • Multi-dimensional Filtering: Advanced querying capabilities with multiple filter options
  • Complete CRUD Operations: Full create, read, update, and delete operations for memory items
  • LangChain Integration: Seamless integration with LangChain's embedding and LLM capabilities, including LangChainEmbedder wrapper
  • Extensible Embedder Interface: Support for custom embedding providers beyond LangChain
  • Memory Expiration: Support for time-based memory expiration
  • Immutable Memories: Option to mark memories as read-only
  • Metadata Support: Flexible metadata storage for enhanced memory organization
  • Multi-tenant Support: Organization-based isolation for enterprise use cases

Prerequisites

  • Node.js 18+
  • PostgreSQL 15+ with pgvector extension
  • OpenAI API Key (required for testing and examples)

Installation and Setup

1. Install Dependencies

pnpm install

2. Start PostgreSQL with pgvector

docker-compose up -d

3. Create Test Database

createdb langgraph_memory_test

4. Set Environment Variables

export OPENAI_API_KEY="your-openai-api-key-here"

Testing

Run the complete test suite (requires internet connection and OpenAI API):

pnpm test

Run a specific test file:

pnpm test memory-database.test.ts

Usage Examples

Basic Setup

import { MemoryDataBase } from './src/MemoryDatabase';
import { PostgresVectorStore } from './src/vector-store/pg';
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import { Pool } from 'pg';

// Initialize database connection
const pool = new Pool({
    host: 'localhost',
    port: 5432,
    user: 'postgres',
    password: 'postgres',
    database: 'langgraph_memory',
});

// Initialize vector store
const vectorStore = new PostgresVectorStore({
    pool,
    tableName: 'memory_vectors',
    dimension: 1536, // text-embedding-3-small dimension
});

// Create embedder (direct implementation)
const embedder = {
    embed: async (text: string) => {
        const openaiEmbedder = new OpenAIEmbeddings({
            modelName: 'text-embedding-3-small',
        });
        return await openaiEmbedder.embedQuery(text);
    },
    embedBatch: async (texts: string[]) => {
        const openaiEmbedder = new OpenAIEmbeddings({
            modelName: 'text-embedding-3-small',
        });
        const embeddings = await openaiEmbedder.embedDocuments(texts);
        return embeddings.map((embedding, index) => ({
            embedding,
            original: texts[index],
        }));
    },
};

// Initialize memory database
const memoryDB = new MemoryDataBase('your-org-id', new ChatOpenAI({ modelName: 'gpt-4o-mini' }), embedder, vectorStore);

// Setup database schema
await vectorStore.initialize();

Adding Memories

import { HumanMessage, AIMessage } from '@langchain/core/messages';

// Add conversation memories
// Note: At least one of userId, agentId, or runId is required
const messages = [
    new HumanMessage('What is TypeScript?'),
    new AIMessage('TypeScript is a programming language developed by Microsoft...'),
];

const result = await memoryDB.add(messages, {
    userId: 'user123',
    agentId: 'agent456',
    metadata: {
        topic: 'programming',
        language: 'typescript',
    },
});

console.log('Added memories:', result.results.length);

Searching Memories

// Search with text query
// Note: At least one of userId, agentId, or runId is required
const searchResult = await memoryDB.search('programming languages', {
    userId: 'user123',
    limit: 5,
    filters: {
        categories: 'technical', // Filter by category
        createdAtAfter: '2024-01-01T00:00:00Z', // Filter by creation time
    },
});

console.log('Search results:', searchResult.results);

Advanced Filtering

The memory system supports comprehensive filtering capabilities:

// Get memories with multiple filters
const filteredMemories = await memoryDB.getAll({
    userId: 'user123',
    categories: ['hobby', 'work'], // Must contain both categories (AND operation)
    createdAtBefore: new Date().toISOString(),
    createdAtAfter: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), // Last 7 days
    limit: 20,
});

// Search with category filter
const hobbyMemories = await memoryDB.search('interests', {
    userId: 'user123',
    filters: {
        categories: 'hobby', // Single category filter
    },
    limit: 10,
});

// Filter by time ranges
const recentMemories = await memoryDB.getAll({
    userId: 'user123',
    updatedAtBefore: new Date().toISOString(),
    updatedAtAfter: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // Last 24 hours
});

// Filter by expiration date
const expiringMemories = await memoryDB.getAll({
    userId: 'user123',
    expirationDateBefore: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // Expires within 7 days
});

Memory Management

// Get a specific memory
const memory = await memoryDB.get('memory-id-123');

// Update memory content
await memoryDB.update('memory-id-123', 'Updated memory content');

// Delete a specific memory
await memoryDB.delete('memory-id-123');

// Delete all memories for a user
await memoryDB.deleteAll({
    userId: 'user123',
});

// Delete memories by category
await memoryDB.deleteAll({
    userId: 'user123',
    categories: 'temporary',
});

// Delete memories by time range
await memoryDB.deleteAll({
    userId: 'user123',
    createdAtBefore: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(), // Older than 30 days
});

// Delete expired memories
await memoryDB.deleteAll({
    expirationDateBefore: new Date().toISOString(),
});

// Get all memories with pagination
const allMemories = await memoryDB.getAll({
    userId: 'user123',
    limit: 10,
});

API Reference

PostgresVectorStore Class

constructor(config: {
    pool: Pool;
    tableName?: string;
    dimension?: number;
})

Parameters:

  • pool: PostgreSQL connection pool
  • tableName: Table name for vector storage (default: 'memories')
  • dimension: Vector dimension (default: 1536)

Methods

  • initialize(): Promise<void> - Create tables and indexes
  • insert(id, orgId, memory, embedding, metadata?): Promise<void> - Insert vector data
  • search(queryEmbedding, config): Promise<VectorSearchResult[]> - Search similar vectors
  • delete(id): Promise<void> - Delete vector by ID
  • reset(): Promise<void> - Clear all data
  • close(): Promise<void> - Close connections

Embedder Interface

interface Embedder {
    embed(text: string): Promise<number[]>;
    embedBatch(texts: string[]): Promise<
        {
            embedding: number[];
            original: string;
        }[]
    >;
}

The embedder interface provides text-to-vector conversion methods. You can implement this interface with any embedding provider (OpenAI, HuggingFace, etc.).

Direct Implementation

const embedder: Embedder = {
    embed: async (text: string) => {
        const openaiEmbedder = new OpenAIEmbeddings({
            modelName: 'text-embedding-3-small',
        });
        return await openaiEmbedder.embedQuery(text);
    },
    embedBatch: async (texts: string[]) => {
        const openaiEmbedder = new OpenAIEmbeddings({
            modelName: 'text-embedding-3-small',
        });
        const embeddings = await openaiEmbedder.embedDocuments(texts);
        return embeddings.map((embedding, index) => ({
            embedding,
            original: texts[index],
        }));
    },
};

Custom Embedder Implementation

If you need to use a different embedding provider, you can implement the Embedder interface directly:

class CustomEmbedder implements Embedder {
    async embed(text: string): Promise<number[]> {
        // Your custom embedding logic
        return [
            /* embedding vector */
        ];
    }

    async embedBatch(texts: string[]): Promise<{ embedding: number[]; original: string }[]> {
        // Your custom batch embedding logic
        return texts.map((text) => ({
            embedding: [
                /* embedding vector */
            ],
            original: text,
        }));
    }
}

const embedder = new CustomEmbedder();

MemoryDataBase Class

Constructor

constructor(
    org_id: string,
    llm: BaseChatModel,
    embedder: Embedder,
    vectorStore: PostgresVectorStore,
    customPrompt?: string
)

Parameters:

  • org_id: Organization identifier for multi-tenant support
  • llm: Language model for memory processing and deduplication
  • embedder: Embedder implementation with embed and embedBatch methods
  • vectorStore: PostgreSQL vector store instance
  • customPrompt: Optional custom prompt for memory extraction

Methods

  • setup(): Promise<void> - Initialize database schema and indexes
  • add(messages, config): Promise<SearchResult> - Add new memories from conversation messages
  • get(memoryId: string): Promise<MemoryItem | null> - Retrieve a specific memory
  • search(query: string, config): Promise<SearchResult> - Search memories by text similarity
  • update(memoryId: string, data: string): Promise<{ message: string }> - Update memory content
  • delete(memoryId: string): Promise<{ message: string }> - Delete a specific memory
  • deleteAll(config: DeleteAllMemoryOptions): Promise<{ message: string }> - Delete all memories matching filters
  • reset(): Promise<void> - Reset the entire memory database
  • getAll(config: GetAllMemoryOptions): Promise<SearchResult> - Get all memories with optional filtering

MemoryItem Interface

interface MemoryItem {
    id: string;
    org_id: string;
    agent_id?: string;
    user_id?: string;
    app_id?: string;
    run_id?: string;
    immutable?: boolean;
    memory: string;
    categories?: string[];
    metadata?: Record<string, any>;
    score?: number;
    updated_at: string;
    created_at: string;
    expiration_date?: string;
}

Configuration Options

MemoryFilters Interface

interface MemoryFilters extends IdSet {
    categories?: string[] | string; // Single category or array of categories
    createdAtBefore?: string; // ISO date string
    createdAtAfter?: string; // ISO date string
    updatedAtBefore?: string; // ISO date string
    updatedAtAfter?: string; // ISO date string
    expirationDateBefore?: string; // ISO date string
    expirationDateAfter?: string; // ISO date string
    [key: string]: any; // Additional custom filters
}

Add Configuration

interface AddConfig extends IdSet {
    metadata?: Record<string, any>; // Additional metadata to store
    filters?: MemoryFilters; // Filters for the operation
    infer?: boolean; // Whether to infer categories (default: true)
}

Search Configuration

interface SearchConfig extends IdSet {
    limit?: number; // Maximum number of results (default: 100)
    filters?: MemoryFilters; // Additional filters to apply
}

GetAll Configuration

interface GetAllMemoryOptions extends MemoryFilters {
    limit?: number; // Maximum number of results (default: 100)
}

DeleteAll Configuration

interface DeleteAllMemoryOptions extends MemoryFilters {
    // Same as MemoryFilters - at least one filter is required
}

Advanced Features

Multi-tenant Organization Isolation

The memory system provides complete organization-based isolation, ensuring that different organizations cannot access each other's memories:

// Create separate memory databases for different organizations
const orgAMemoryDB = new MemoryDataBase('org-a', llm, embedder, vectorStore);
const orgBMemoryDB = new MemoryDataBase('org-b', llm, embedder, vectorStore);

// Add memories for different organizations
await orgAMemoryDB.add([new HumanMessage('Organization A data')], { userId: 'user1' });
await orgBMemoryDB.add([new HumanMessage('Organization B data')], { userId: 'user1' });

// Each organization can only access their own data
const orgAData = await orgAMemoryDB.getAll({ userId: 'user1' }); // Only sees org-a data
const orgBData = await orgBMemoryDB.getAll({ userId: 'user1' }); // Only sees org-b data

// Reset only affects the current organization
await orgAMemoryDB.reset(); // Only clears org-a data

Memory Merging and Deduplication

The system uses LLM to intelligently merge and deduplicate memories when adding new content. This prevents duplicate information while preserving important context.

Custom Prompts

You can customize the behavior of memory extraction and merging by providing custom prompts:

const memoryDB = new MemoryDataBase(
    'your-org-id',
    llm,
    embedder,
    vectorStore,
    'Your custom prompt for memory processing...',
);

Vector Store Configuration

The vector store supports various configuration options for performance optimization:

const vectorStore = new PostgresVectorStore(pool, {
    tableName: 'memory_vectors',
    dimension: 1536,
    indexType: 'hnsw', // 'hnsw' or 'ivfflat'
    hnswM: 16, // HNSW parameter
    hnswEfConstruction: 64, // HNSW parameter
    ivfflatLists: 100, // IVFFlat parameter
});

Performance Considerations

  • Indexing: Choose appropriate vector indexes based on your data size and query patterns
  • Batch Operations: Use batch embedding for better performance when adding multiple memories
  • Connection Pooling: Configure PostgreSQL connection pooling for production use
  • Memory Expiration: Regularly clean up expired memories to maintain optimal performance

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

Acknowledgments

This project draws inspiration from the mem0 project's source code.

License

Apache License 2.0