{"id":31767703,"url":"https://github.com/alexpota/pg-agent-memory","last_synced_at":"2025-10-10T01:19:16.165Z","repository":{"id":306555269,"uuid":"1026031011","full_name":"alexpota/pg-agent-memory","owner":"alexpota","description":"Stateful AI agent memory layer for PostgreSQL with pgvector - TypeScript-first with intelligent context management","archived":false,"fork":false,"pushed_at":"2025-10-06T07:07:30.000Z","size":558,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-06T08:42:31.777Z","etag":null,"topics":["agent","ai","artificial-intelligence","chatbot","conversation-memory","embeddings","langchain","llm","machine-learning","memory","multi-model","pgvector","postgresql","rag","semantic-search","typescript","vector-database"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexpota.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-25T07:41:33.000Z","updated_at":"2025-10-06T07:04:01.000Z","dependencies_parsed_at":"2025-07-26T12:50:16.009Z","dependency_job_id":"7a4ca631-c7a0-41d0-9033-80e378f76421","html_url":"https://github.com/alexpota/pg-agent-memory","commit_stats":null,"previous_names":["alexpota/pg-agent-memory"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/alexpota/pg-agent-memory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexpota%2Fpg-agent-memory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexpota%2Fpg-agent-memory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexpota%2Fpg-agent-memory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexpota%2Fpg-agent-memory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexpota","download_url":"https://codeload.github.com/alexpota/pg-agent-memory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexpota%2Fpg-agent-memory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002335,"owners_count":26083356,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","ai","artificial-intelligence","chatbot","conversation-memory","embeddings","langchain","llm","machine-learning","memory","multi-model","pgvector","postgresql","rag","semantic-search","typescript","vector-database"],"created_at":"2025-10-10T01:19:15.559Z","updated_at":"2025-10-10T01:19:16.158Z","avatar_url":"https://github.com/alexpota.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pg-agent-memory\n\n\u003e Stateful AI agent memory layer for PostgreSQL with pgvector. TypeScript-first with intelligent context management and semantic search.\n\n[![npm version](https://img.shields.io/npm/v/pg-agent-memory.svg)](https://www.npmjs.com/package/pg-agent-memory)\n[![Downloads](https://img.shields.io/npm/dm/pg-agent-memory.svg)](https://www.npmjs.com/package/pg-agent-memory)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)\n[![Node.js CI](https://github.com/alexpota/pg-agent-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/alexpota/pg-agent-memory/actions/workflows/ci.yml)\n[![Test Coverage](https://img.shields.io/badge/coverage-85%25-green.svg)](#testing)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)\n[![Node.js](https://img.shields.io/node/v/pg-agent-memory.svg)](https://nodejs.org/)\n\n## Why pg-agent-memory?\n\n### The Problem: AI Agents Forget Everything\n\n**❌ Without pg-agent-memory**\n\n```js\n// Day 1: User shares preference\nawait openai.chat.completions.create({\n  messages: [{ role: 'user', content: 'I prefer Python' }],\n});\n// AI: \"Got it! I'll remember that.\"\n\n// Day 2: User asks for help\nawait openai.chat.completions.create({\n  messages: [{ role: 'user', content: 'Help me start a project' }],\n});\n// AI: \"What language would you like to use?\"\n// 😤 Forgot everything!\n```\n\n**✅ With pg-agent-memory**\n\n```js\n// Day 1: Store preference\nawait memory.remember({\n  conversation: userId,\n  content: 'User prefers Python',\n  role: 'system',\n});\n\n// Day 2: Retrieve context\nconst context = await memory.getHistory(userId);\nawait openai.chat.completions.create({\n  messages: [...context, { role: 'user', content: 'Help me start a project' }],\n});\n// AI: \"I'll create a Python project for you!\"\n// 🎯 Remembers everything!\n```\n\n## Features\n\n- **Persistent Memory** - Conversations continue across sessions\n- **Multi-Model Support** - OpenAI, Anthropic, DeepSeek, Google, Meta + custom providers\n- **Local Embeddings** - Zero API costs for vector embeddings with Sentence Transformers\n- **Memory Compression** - Automatic summarization with 4 compression strategies\n- **Semantic Search** - Find relevant memories using AI embeddings\n- **Universal Tokenizer** - Accurate token counting based on official provider documentation\n- **TypeScript First** - Full type safety with autocomplete\n- **PostgreSQL Native** - Uses your existing database\n- **Zero-Cost Embeddings** - Local Sentence Transformers (@xenova/transformers)\n- **High Performance** - ~9ms memory operations, ~5ms vector search\n\n### Coming Soon\n\n- Multi-agent memory sharing\n- Memory graph visualization\n- Pattern detection\n\n## Quick Start\n\n```bash\n# Install\nnpm install pg-agent-memory\n\n# Start PostgreSQL with Docker (includes pgvector)\ndocker compose up -d\n\n# Run example\nnpm start\n```\n\n### Installation\n\n```bash\nnpm install pg-agent-memory\n```\n\n### Database Setup\n\n**Option 1: Docker (Recommended)**\n\n```bash\n# Use included docker compose configuration\ndocker compose up -d\n```\n\n**Option 2: Existing PostgreSQL**\n\n```sql\n-- Enable pgvector extension\nCREATE EXTENSION IF NOT EXISTS vector;\n```\n\n### Basic Usage\n\n```typescript\nimport { AgentMemory } from 'pg-agent-memory';\n\n// Option 1: Static factory method (recommended)\nconst memory = await AgentMemory.create({\n  agent: 'my-assistant',\n  connectionString: 'postgresql://user:pass@localhost:5432/db',\n});\n\n// Option 2: Traditional constructor + initialize\nconst memory = new AgentMemory({\n  agent: 'my-assistant',\n  connectionString: 'postgresql://user:pass@localhost:5432/db',\n});\nawait memory.initialize();\n\n// Store conversation memory\nconst memoryId = await memory.remember({\n  conversation: 'user-123',\n  content: 'User prefers email notifications',\n  role: 'user',\n  importance: 0.8,\n  timestamp: new Date(),\n});\n\n// Find related memories using vector similarity\nconst related = await memory.findRelatedMemories(memoryId, 5);\n\n// Semantic search across memories\nconst relevant = await memory.searchMemories('notification preferences');\n\n// Get relevant context for a query\nconst context = await memory.getRelevantContext(\n  'user-123',\n  'user communication preferences',\n  1000 // max tokens\n);\n\nconsole.log(`Found ${context.messages.length} relevant memories`);\nconsole.log(`Relevance score: ${context.relevanceScore}`);\n\n// Health check for monitoring\nconst health = await memory.healthCheck();\nconsole.log(`Status: ${health.status}, Memories: ${health.details.memoryCount}`);\n\n// Cleanup\nawait memory.disconnect();\n```\n\n## Multi-Model Support\n\nConfigure multiple AI providers with accurate token counting and prompt caching:\n\n```typescript\nconst memory = new AgentMemory({\n  agent: 'multi-model-bot',\n  connectionString,\n  modelProviders: [\n    {\n      name: 'gpt-4o',\n      provider: 'openai',\n      model: 'gpt-4o',\n      tokenLimits: { context: 128000, output: 4000 },\n      // High context limit: Supports large conversations\n    },\n    {\n      name: 'claude-sonnet',\n      provider: 'anthropic',\n      model: 'claude-sonnet-4-20250514',\n      tokenLimits: { context: 200000, output: 4000 },\n      // Large context window: Perfect for long conversations\n    },\n    {\n      name: 'deepseek-coder',\n      provider: 'deepseek',\n      model: 'deepseek-coder',\n      tokenLimits: { context: 32000, output: 4000 },\n      // Cost-effective option for coding tasks\n    },\n    {\n      name: 'gemini-pro',\n      provider: 'google',\n      model: 'gemini-1.5-pro',\n      tokenLimits: { context: 1048576, output: 8192 },\n      // Massive context window: Handle very long conversations\n    },\n    {\n      name: 'llama-3',\n      provider: 'meta',\n      model: 'llama-3.1-70b',\n      tokenLimits: { context: 128000, output: 4000 },\n      // More efficient tokenizer: ~44% fewer tokens than OpenAI\n    },\n  ],\n  defaultProvider: 'gpt-4o',\n  tokenCountingStrategy: 'hybrid', // 'precise', 'fast', or 'hybrid'\n});\n\n// Token counting uses official provider documentation:\n// - OpenAI: ~4 chars/token or 0.75 words/token (official baseline)\n// - Anthropic: ~3.5 chars/token (14% more tokens)\n// - DeepSeek: ~3.3 chars/token (20% more tokens)\n// - Google: ~4 chars/token (same as OpenAI)\n// - Meta/Llama: ~0.75 tokens/word (44% fewer tokens - more efficient)\nawait memory.remember({\n  conversation: userId,\n  content: longText,\n  provider: 'gpt-4o', // Use OpenAI for this memory\n});\n```\n\n## Memory Compression\n\nAutomatic memory compression with multiple strategies:\n\n```typescript\n// Enable compression for large conversations\nconst compressionResult = await memory.compressMemories({\n  strategy: 'hybrid', // 'token_based', 'time_based', 'importance_based', 'hybrid'\n  maxAge: '7d',\n  targetCompressionRatio: 0.6,\n});\n\nconsole.log(`Compressed ${compressionResult.memoriesCompressed} memories`);\nconsole.log(`Token savings: ${compressionResult.tokensSaved}`);\n\n// Get context with automatic compression\nconst context = await memory.getRelevantContextWithCompression(\n  'coding preferences',\n  4000 // Automatically compresses if needed\n);\n```\n\n## Real-World Integration\n\n### With OpenAI\n\n```typescript\nimport OpenAI from 'openai';\nimport { AgentMemory } from 'pg-agent-memory';\n\nconst client = new OpenAI({\n  apiKey: process.env.OPENAI_API_KEY,\n});\n\nconst memory = new AgentMemory({ agent: 'support-bot', connectionString });\n\n// Retrieve conversation history\nconst history = await memory.getHistory(userId);\n\n// Include memory in AI request\nconst completion = await client.chat.completions.create({\n  model: 'gpt-4o',\n  messages: [\n    ...history.map(m =\u003e ({ role: m.role, content: m.content })),\n    { role: 'user', content: userMessage },\n  ],\n});\n\n// Store the interaction\nawait memory.remember({\n  conversation: userId,\n  content: userMessage,\n  role: 'user',\n  importance: 0.5,\n  timestamp: new Date(),\n});\n\nawait memory.remember({\n  conversation: userId,\n  content: completion.choices[0].message.content,\n  role: 'assistant',\n  importance: 0.7,\n  timestamp: new Date(),\n});\n```\n\n### With Anthropic Claude\n\n```typescript\nimport Anthropic from '@anthropic-ai/sdk';\nimport { AgentMemory } from 'pg-agent-memory';\n\nconst client = new Anthropic({\n  apiKey: process.env.ANTHROPIC_API_KEY,\n});\n\nconst memory = new AgentMemory({\n  agent: 'claude-assistant',\n  connectionString,\n  modelProviders: [\n    {\n      name: 'claude',\n      provider: 'anthropic',\n      model: 'claude-sonnet-4-20250514',\n      tokenLimits: { context: 200000, output: 4000 },\n    },\n  ],\n});\n\n// Get conversation with compression for large contexts\nconst context = await memory.getRelevantContextWithCompression(\n  'user preferences and history',\n  180000 // Near Claude's context limit\n);\n\nconst message = await client.messages.create({\n  model: 'claude-sonnet-4-20250514',\n  max_tokens: 1024,\n  messages: [\n    ...context.messages.map(m =\u003e ({ role: m.role, content: m.content })),\n    { role: 'user', content: userMessage },\n  ],\n});\n\n// Store the interaction\nawait memory.remember({\n  conversation: userId,\n  content: message.content[0].text,\n  role: 'assistant',\n  importance: 0.7,\n  timestamp: new Date(),\n});\n```\n\n### With Vercel AI SDK\n\n```typescript\nimport { streamText } from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { AgentMemory } from 'pg-agent-memory';\n\nconst memory = new AgentMemory({ agent: 'chat-assistant', connectionString });\n\nexport async function POST(req: Request) {\n  const { messages, userId } = await req.json();\n\n  // Get user's conversation history\n  const history = await memory.getHistory(userId);\n\n  const result = streamText({\n    model: openai('gpt-4o'),\n    system: 'You are a helpful assistant with memory.',\n    messages: [...history, ...messages],\n  });\n\n  // Store the conversation\n  for (const message of messages) {\n    await memory.remember({\n      conversation: userId,\n      role: message.role,\n      content: message.content,\n      importance: 0.5,\n      timestamp: new Date(),\n    });\n  }\n\n  return result.toDataStreamResponse();\n}\n```\n\n## API Reference\n\n### AgentMemory\n\n#### Static Methods\n\n##### `AgentMemory.create(config: MemoryConfig): Promise\u003cAgentMemory\u003e`\n\n**Recommended**: Creates and initializes an AgentMemory instance in one call.\n\n```typescript\nconst memory = await AgentMemory.create({\n  agent: 'my-bot',\n  connectionString: 'postgresql://...',\n});\n```\n\n#### Constructor\n\n```typescript\nnew AgentMemory(config: MemoryConfig)\n```\n\n**MemoryConfig:**\n\n- `agent: string` - Unique agent identifier\n- `connectionString: string` - PostgreSQL connection string\n- `tablePrefix?: string` - Table prefix (default: 'agent')\n- `maxTokens?: number` - Max tokens per memory (default: 4000)\n- `embeddingDimensions?: number` - Vector dimensions (default: 384)\n\n#### Core Methods\n\n##### `initialize(): Promise\u003cvoid\u003e`\n\nInitialize database schema and embedding model.\n\n##### `remember(message: Message): Promise\u003cstring\u003e`\n\nStore a conversation memory.\n\n##### `findRelatedMemories(memoryId: string, limit?: number): Promise\u003cMessage[]\u003e`\n\nFind memories related to a specific memory using vector similarity.\n\n```typescript\nconst related = await memory.findRelatedMemories(memoryId, 5);\n```\n\n##### `healthCheck(): Promise\u003cHealthStatus\u003e`\n\nCheck system health for monitoring and debugging.\n\n```typescript\nconst health = await memory.healthCheck();\n// Returns: { status: 'healthy' | 'unhealthy', details: {...} }\n```\n\n**Message:**\n\n```typescript\n{\n  conversation: string;    // Conversation ID\n  content: string;         // Memory content\n  role: 'user' | 'assistant' | 'system'; // Required, defaults to 'user'\n  importance: number;      // 0-1 relevance score, defaults to 0.5\n  timestamp: Date;         // Required, defaults to new Date()\n  id?: string;             // Optional memory ID\n  metadata?: Record\u003cstring, unknown\u003e;\n  embedding?: number[];    // Optional vector embedding\n  expires?: Date | string; // Expiration (e.g., '30d', '1h')\n}\n```\n\n##### `recall(filter: MemoryFilter): Promise\u003cContext\u003e`\n\nRetrieve memories with filtering.\n\n##### `getHistory(conversation: string, limit?: number): Promise\u003cMessage[]\u003e`\n\nGet chronological conversation history.\n\n##### `getRelevantContext(conversation: string, query: string, maxTokens: number): Promise\u003cContext\u003e`\n\nFind semantically relevant memories for a query.\n\n##### `searchMemories(query: string, filters?: MemoryFilter): Promise\u003cMessage[]\u003e`\n\nSemantic search across all agent memories.\n\n##### `deleteMemory(memoryId: string): Promise\u003cvoid\u003e`\n\nDelete specific memory.\n\n##### `deleteConversation(conversation: string): Promise\u003cvoid\u003e`\n\nDelete entire conversation.\n\n## Database Requirements\n\n- PostgreSQL 12+\n- pgvector extension\n\n## Performance\n\n- **Memory operations**: ~9ms average (range: 5-22ms, first operation slower due to model loading)\n- **Vector search**: ~5ms average for semantic similarity search using pgvector\n- **Token counting**: \u003c1ms sub-millisecond performance for all text sizes\n- **Embedding generation**: Local processing, no API calls required\n- **Model size**: ~80-90MB (all-MiniLM-L6-v2, cached after first download)\n- **Architecture**: Built for production scale with proper indexing and connection pooling\n\n## Architecture\n\n```\n┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐\n│   AgentMemory   │────│  EmbeddingService │────│ @xenova/trans.. │\n│                 │    │                  │    │   (Local Model) │\n└─────────────────┘    └──────────────────┘    └─────────────────┘\n         │\n         ▼\n┌─────────────────┐    ┌──────────────────┐\n│   PostgreSQL    │────│     pgvector     │\n│   (Memories)    │    │  (Vector Search) │\n└─────────────────┘    └──────────────────┘\n```\n\n**Components:**\n\n- **AgentMemory**: Main API for memory operations\n- **EmbeddingService**: Local text-to-vector conversion using @xenova/transformers\n- **PostgreSQL**: Persistent storage with ACID properties\n- **pgvector**: Efficient vector similarity search\n- **@xenova/transformers**: Local Sentence Transformers model (all-MiniLM-L6-v2)\n\n## Examples\n\n### Chatbot Integration\n\n```typescript\nimport { AgentMemory } from 'pg-agent-memory';\n\nclass ChatBot {\n  private memory: AgentMemory;\n\n  constructor() {\n    this.memory = new AgentMemory({\n      agent: 'chatbot',\n      connectionString: process.env.DATABASE_URL,\n    });\n  }\n\n  async processMessage(userId: string, message: string) {\n    // Store user message\n    await this.memory.remember({\n      conversation: userId,\n      content: message,\n      role: 'user',\n      importance: 0.5,\n      timestamp: new Date(),\n    });\n\n    // Get relevant context\n    const context = await this.memory.getRelevantContext(userId, message, 800);\n\n    // Generate response using context\n    const response = await this.generateResponse(message, context);\n\n    // Store bot response\n    await this.memory.remember({\n      conversation: userId,\n      content: response,\n      role: 'assistant',\n      importance: 0.7,\n      timestamp: new Date(),\n    });\n\n    return response;\n  }\n}\n```\n\n### Advanced Filtering\n\n```typescript\n// Search with filters\nconst memories = await memory.searchMemories('user preferences', {\n  importance: { min: 0.7 },\n  dateRange: {\n    start: new Date('2024-01-01'),\n    end: new Date('2024-12-31'),\n  },\n  metadata: { category: 'user_settings' },\n  limit: 10,\n});\n\n// Get memories by role\nconst userMessages = await memory.recall({\n  conversation: 'user-123',\n  role: 'user',\n  limit: 50,\n});\n```\n\n## Running Examples\n\n```bash\n# Set database URL\nexport DATABASE_URL=\"postgresql://user:pass@localhost:5432/dbname\"\n\n# Run basic example\nnpm run example:basic\n\n# Run chatbot example\nnpm run example:chatbot\n\n# Run all examples\nnpm run example:all\n```\n\n## Development\n\n### Quick Start with Docker\n\n```bash\n# Clone repository\ngit clone \u003crepository\u003e\ncd pg-agent-memory\nnpm install\n\n# Start PostgreSQL with pgvector\nnpm run dev:up\n\n# Copy environment variables\ncp .env.example .env\n\n# Run examples\nnpm run example:basic\n\n# Run tests\nnpm run test:docker\n```\n\n### Docker Commands\n\n```bash\n# Start development database\nnpm run dev:up\n\n# Stop database (data persists)\nnpm run dev:down\n\n# View database logs\nnpm run dev:logs\n\n# Clean everything (including data)\nnpm run dev:clean\n\n# Connect to PostgreSQL shell\nbash scripts/docker-dev.sh shell\n```\n\n### Manual Setup\n\nIf you prefer using your own PostgreSQL:\n\n```bash\n# Install pgvector extension\nCREATE EXTENSION IF NOT EXISTS vector;\n\n# Set connection string\nexport DATABASE_URL=\"postgresql://user:pass@localhost:5432/dbname\"\n\n# Run tests\nnpm test\nnpm run test:integration\n```\n\n### Testing\n\n```bash\n# Unit tests (no database needed)\nnpm test\n\n# Integration tests with Docker\nnpm run test:docker\n\n# Integration tests with custom database\nexport DATABASE_URL=\"postgresql://user:pass@localhost:5432/test_db\"\nnpm run test:integration\n\n# All tests\nnpm run test:all\n\n# Code quality checks\nnpm run lint           # ESLint + Prettier\nnpm run type-check     # TypeScript compilation\nnpm run validate       # Full validation (lint + type-check + tests)\n\n# Performance benchmarks\nnpm run benchmark      # Verify performance claims\n```\n\n\n## License\n\n[MIT](./LICENSE) © Alex Potapenko\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Run tests: `npm test`\n4. Submit a pull request\n\n## Support\n\n- [GitHub Issues](https://github.com/alexpota/pg-agent-memory/issues)\n- [Documentation](https://github.com/alexpota/pg-agent-memory#readme)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexpota%2Fpg-agent-memory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexpota%2Fpg-agent-memory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexpota%2Fpg-agent-memory/lists"}