diff --git a/docs/open-source/node-quickstart.mdx b/docs/open-source/node-quickstart.mdx
index 1fc1abd7..39f7cbf7 100644
--- a/docs/open-source/node-quickstart.mdx
+++ b/docs/open-source/node-quickstart.mdx
@@ -301,6 +301,56 @@ await memory.deleteAll({ userId: "alice" });
await memory.reset(); // Reset all memories
```
+### History Store
+
+Mem0 TypeScript SDK support history stores to run on a serverless environment:
+
+We recommend using `Supabase` as a history store for serverless environments or disable history store to run on a serverless environment.
+
+
+```typescript Supabase
+import { Memory } from 'mem0ai/oss';
+
+const memory = new Memory({
+ historyStore: {
+ provider: 'supabase',
+ config: {
+ supabaseUrl: process.env.SUPABASE_URL || '',
+ supabaseKey: process.env.SUPABASE_KEY || '',
+ tableName: 'memory_history',
+ },
+ },
+});
+```
+
+```typescript Disable History
+import { Memory } from 'mem0ai/oss';
+
+const memory = new Memory({
+ disableHistory: true,
+});
+```
+
+
+Mem0 uses SQLite as a default history store.
+
+#### Create Memory History Table in Supabase
+
+You may need to create a memory history table in Supabase to store the history of memories. Use the following SQL command in `SQL Editor` on the Supabase project dashboard to create a memory history table:
+
+```sql
+create table memory_history (
+ id text primary key,
+ memory_id text not null,
+ previous_value text,
+ new_value text,
+ action text not null,
+ created_at timestamp with time zone default timezone('utc', now()),
+ updated_at timestamp with time zone,
+ is_deleted integer default 0
+);
+```
+
## Configuration Parameters
Mem0 offers extensive configuration options to customize its behavior according to your needs. These configurations span across different components like vector stores, language models, embedders, and graph stores.
@@ -352,6 +402,14 @@ Mem0 offers extensive configuration options to customize its behavior according
| `customPrompt` | Custom prompt for memory processing | None |
+
+| Parameter | Description | Default |
+|------------------|--------------------------------------|----------------------------|
+| `provider` | History store provider | "sqlite" |
+| `config` | History store configuration | None (Defaults to SQLite) |
+| `disableHistory` | Disable history store | false |
+
+
```typescript
const config = {
@@ -377,7 +435,15 @@ const config = {
model: 'gpt-4-turbo-preview',
},
},
- historyDbPath: 'memory.db',
+ historyStore: {
+ provider: 'supabase',
+ config: {
+ supabaseUrl: process.env.SUPABASE_URL || '',
+ supabaseKey: process.env.SUPABASE_KEY || '',
+ tableName: 'memories',
+ },
+ },
+ disableHistory: false, // This is false by default
customPrompt: "I'm a virtual assistant. I'm here to help you with your queries.",
}
```
diff --git a/mem0-ts/package.json b/mem0-ts/package.json
index f3f89eb7..5f432bf8 100644
--- a/mem0-ts/package.json
+++ b/mem0-ts/package.json
@@ -1,6 +1,6 @@
{
"name": "mem0ai",
- "version": "2.1.9",
+ "version": "2.1.10",
"description": "The Memory Layer For Your AI Apps",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
diff --git a/mem0-ts/src/oss/src/config/defaults.ts b/mem0-ts/src/oss/src/config/defaults.ts
index e2606923..a855e5b3 100644
--- a/mem0-ts/src/oss/src/config/defaults.ts
+++ b/mem0-ts/src/oss/src/config/defaults.ts
@@ -1,6 +1,7 @@
import { MemoryConfig } from "../types";
export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
+ disableHistory: false,
version: "v1.1",
embedder: {
provider: "openai",
@@ -38,5 +39,10 @@ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
},
},
},
- historyDbPath: "memory.db",
+ historyStore: {
+ provider: "sqlite",
+ config: {
+ historyDbPath: "memory.db",
+ },
+ },
};
diff --git a/mem0-ts/src/oss/src/config/manager.ts b/mem0-ts/src/oss/src/config/manager.ts
index 6dc713aa..fa8daed7 100644
--- a/mem0-ts/src/oss/src/config/manager.ts
+++ b/mem0-ts/src/oss/src/config/manager.ts
@@ -51,6 +51,12 @@ export class ConfigManager {
...DEFAULT_MEMORY_CONFIG.graphStore,
...userConfig.graphStore,
},
+ historyStore: {
+ ...DEFAULT_MEMORY_CONFIG.historyStore,
+ ...userConfig.historyStore,
+ },
+ disableHistory:
+ userConfig.disableHistory || DEFAULT_MEMORY_CONFIG.disableHistory,
enableGraph: userConfig.enableGraph || DEFAULT_MEMORY_CONFIG.enableGraph,
};
diff --git a/mem0-ts/src/oss/src/memory/index.ts b/mem0-ts/src/oss/src/memory/index.ts
index a164798d..eaddaddc 100644
--- a/mem0-ts/src/oss/src/memory/index.ts
+++ b/mem0-ts/src/oss/src/memory/index.ts
@@ -12,6 +12,7 @@ import {
EmbedderFactory,
LLMFactory,
VectorStoreFactory,
+ HistoryManagerFactory,
} from "../utils/factory";
import {
getFactRetrievalMessages,
@@ -19,7 +20,7 @@ import {
parseMessages,
removeCodeBlocks,
} from "../prompts";
-import { SQLiteManager } from "../storage";
+import { DummyHistoryManager } from "../storage/DummyHistoryManager";
import { Embedder } from "../embeddings/base";
import { LLM } from "../llms/base";
import { VectorStore } from "../vector_stores/base";
@@ -32,14 +33,14 @@ import {
GetAllMemoryOptions,
} from "./memory.types";
import { parse_vision_messages } from "../utils/memory";
-
+import { HistoryManager } from "../storage/base";
export class Memory {
private config: MemoryConfig;
private customPrompt: string | undefined;
private embedder: Embedder;
private vectorStore: VectorStore;
private llm: LLM;
- private db: SQLiteManager;
+ private db: HistoryManager;
private collectionName: string;
private apiVersion: string;
private graphMemory?: MemoryGraph;
@@ -62,7 +63,25 @@ export class Memory {
this.config.llm.provider,
this.config.llm.config,
);
- this.db = new SQLiteManager(this.config.historyDbPath || ":memory:");
+ if (this.config.disableHistory) {
+ this.db = new DummyHistoryManager();
+ } else {
+ const defaultConfig = {
+ provider: "sqlite",
+ config: {
+ historyDbPath: this.config.historyDbPath || ":memory:",
+ },
+ };
+
+ this.db =
+ this.config.historyStore && !this.config.disableHistory
+ ? HistoryManagerFactory.create(
+ this.config.historyStore.provider,
+ this.config.historyStore,
+ )
+ : HistoryManagerFactory.create("sqlite", defaultConfig);
+ }
+
this.collectionName = this.config.vectorStore.config.collectionName;
this.apiVersion = this.config.version || "v1.0";
this.enableGraph = this.config.enableGraph || false;
diff --git a/mem0-ts/src/oss/src/storage/DummyHistoryManager.ts b/mem0-ts/src/oss/src/storage/DummyHistoryManager.ts
new file mode 100644
index 00000000..56bf7add
--- /dev/null
+++ b/mem0-ts/src/oss/src/storage/DummyHistoryManager.ts
@@ -0,0 +1,27 @@
+export class DummyHistoryManager {
+ constructor() {}
+
+ async addHistory(
+ memoryId: string,
+ previousValue: string | null,
+ newValue: string | null,
+ action: string,
+ createdAt?: string,
+ updatedAt?: string,
+ isDeleted: number = 0,
+ ): Promise {
+ return;
+ }
+
+ async getHistory(memoryId: string): Promise {
+ return [];
+ }
+
+ async reset(): Promise {
+ return;
+ }
+
+ close(): void {
+ return;
+ }
+}
diff --git a/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts b/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts
new file mode 100644
index 00000000..dfc321b6
--- /dev/null
+++ b/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts
@@ -0,0 +1,58 @@
+import { v4 as uuidv4 } from "uuid";
+import { HistoryManager } from "./base";
+interface HistoryEntry {
+ id: string;
+ memory_id: string;
+ previous_value: string | null;
+ new_value: string | null;
+ action: string;
+ created_at: string;
+ updated_at: string | null;
+ is_deleted: number;
+}
+
+export class MemoryHistoryManager implements HistoryManager {
+ private memoryStore: Map = new Map();
+
+ async addHistory(
+ memoryId: string,
+ previousValue: string | null,
+ newValue: string | null,
+ action: string,
+ createdAt?: string,
+ updatedAt?: string,
+ isDeleted: number = 0,
+ ): Promise {
+ const historyEntry: HistoryEntry = {
+ id: uuidv4(),
+ memory_id: memoryId,
+ previous_value: previousValue,
+ new_value: newValue,
+ action: action,
+ created_at: createdAt || new Date().toISOString(),
+ updated_at: updatedAt || null,
+ is_deleted: isDeleted,
+ };
+
+ this.memoryStore.set(historyEntry.id, historyEntry);
+ }
+
+ async getHistory(memoryId: string): Promise {
+ return Array.from(this.memoryStore.values())
+ .filter((entry) => entry.memory_id === memoryId)
+ .sort(
+ (a, b) =>
+ new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
+ )
+ .slice(0, 100);
+ }
+
+ async reset(): Promise {
+ this.memoryStore.clear();
+ }
+
+ close(): void {
+ // No need to close anything for in-memory storage
+ return;
+ }
+}
diff --git a/mem0-ts/src/oss/src/storage/SQLiteManager.ts b/mem0-ts/src/oss/src/storage/SQLiteManager.ts
index ef268f93..32143b37 100644
--- a/mem0-ts/src/oss/src/storage/SQLiteManager.ts
+++ b/mem0-ts/src/oss/src/storage/SQLiteManager.ts
@@ -1,7 +1,7 @@
import sqlite3 from "sqlite3";
-import { promisify } from "util";
+import { HistoryManager } from "./base";
-export class SQLiteManager {
+export class SQLiteManager implements HistoryManager {
private db: sqlite3.Database;
constructor(dbPath: string) {
diff --git a/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts b/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts
new file mode 100644
index 00000000..d8cf0e4c
--- /dev/null
+++ b/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts
@@ -0,0 +1,121 @@
+import { createClient, SupabaseClient } from "@supabase/supabase-js";
+import { v4 as uuidv4 } from "uuid";
+import { HistoryManager } from "./base";
+
+interface HistoryEntry {
+ id: string;
+ memory_id: string;
+ previous_value: string | null;
+ new_value: string | null;
+ action: string;
+ created_at: string;
+ updated_at: string | null;
+ is_deleted: number;
+}
+
+interface SupabaseHistoryConfig {
+ supabaseUrl: string;
+ supabaseKey: string;
+ tableName?: string;
+}
+
+export class SupabaseHistoryManager implements HistoryManager {
+ private supabase: SupabaseClient;
+ private readonly tableName: string;
+
+ constructor(config: SupabaseHistoryConfig) {
+ this.tableName = config.tableName || "memory_history";
+ this.supabase = createClient(config.supabaseUrl, config.supabaseKey);
+ this.initializeSupabase().catch(console.error);
+ }
+
+ private async initializeSupabase(): Promise {
+ // Check if table exists
+ const { error } = await this.supabase
+ .from(this.tableName)
+ .select("id")
+ .limit(1);
+
+ if (error) {
+ console.error(
+ "Error: Table does not exist. Please run this SQL in your Supabase SQL Editor:",
+ );
+ console.error(`
+create table ${this.tableName} (
+ id text primary key,
+ memory_id text not null,
+ previous_value text,
+ new_value text,
+ action text not null,
+ created_at timestamp with time zone default timezone('utc', now()),
+ updated_at timestamp with time zone,
+ is_deleted integer default 0
+);
+ `);
+ throw error;
+ }
+ }
+
+ async addHistory(
+ memoryId: string,
+ previousValue: string | null,
+ newValue: string | null,
+ action: string,
+ createdAt?: string,
+ updatedAt?: string,
+ isDeleted: number = 0,
+ ): Promise {
+ const historyEntry: HistoryEntry = {
+ id: uuidv4(),
+ memory_id: memoryId,
+ previous_value: previousValue,
+ new_value: newValue,
+ action: action,
+ created_at: createdAt || new Date().toISOString(),
+ updated_at: updatedAt || null,
+ is_deleted: isDeleted,
+ };
+
+ const { error } = await this.supabase
+ .from(this.tableName)
+ .insert(historyEntry);
+
+ if (error) {
+ console.error("Error adding history to Supabase:", error);
+ throw error;
+ }
+ }
+
+ async getHistory(memoryId: string): Promise {
+ const { data, error } = await this.supabase
+ .from(this.tableName)
+ .select("*")
+ .eq("memory_id", memoryId)
+ .order("created_at", { ascending: false })
+ .limit(100);
+
+ if (error) {
+ console.error("Error getting history from Supabase:", error);
+ throw error;
+ }
+
+ return data || [];
+ }
+
+ async reset(): Promise {
+ const { error } = await this.supabase
+ .from(this.tableName)
+ .delete()
+ .neq("id", "");
+
+ if (error) {
+ console.error("Error resetting Supabase history:", error);
+ throw error;
+ }
+ }
+
+ close(): void {
+ // No need to close anything as connections are handled by the client
+ return;
+ }
+}
diff --git a/mem0-ts/src/oss/src/storage/base.ts b/mem0-ts/src/oss/src/storage/base.ts
new file mode 100644
index 00000000..786b7a2c
--- /dev/null
+++ b/mem0-ts/src/oss/src/storage/base.ts
@@ -0,0 +1,14 @@
+export interface HistoryManager {
+ addHistory(
+ memoryId: string,
+ previousValue: string | null,
+ newValue: string | null,
+ action: string,
+ createdAt?: string,
+ updatedAt?: string,
+ isDeleted?: number,
+ ): Promise;
+ getHistory(memoryId: string): Promise;
+ reset(): Promise;
+ close(): void;
+}
diff --git a/mem0-ts/src/oss/src/storage/index.ts b/mem0-ts/src/oss/src/storage/index.ts
index c6de412d..59324401 100644
--- a/mem0-ts/src/oss/src/storage/index.ts
+++ b/mem0-ts/src/oss/src/storage/index.ts
@@ -1 +1,5 @@
export * from "./SQLiteManager";
+export * from "./DummyHistoryManager";
+export * from "./SupabaseHistoryManager";
+export * from "./MemoryHistoryManager";
+export * from "./base";
diff --git a/mem0-ts/src/oss/src/types/index.ts b/mem0-ts/src/oss/src/types/index.ts
index 62d2d473..9bd356bf 100644
--- a/mem0-ts/src/oss/src/types/index.ts
+++ b/mem0-ts/src/oss/src/types/index.ts
@@ -24,6 +24,16 @@ export interface VectorStoreConfig {
[key: string]: any;
}
+export interface HistoryStoreConfig {
+ provider: string;
+ config: {
+ historyDbPath?: string;
+ supabaseUrl?: string;
+ supabaseKey?: string;
+ tableName?: string;
+ };
+}
+
export interface LLMConfig {
provider?: string;
config?: Record;
@@ -58,6 +68,8 @@ export interface MemoryConfig {
provider: string;
config: LLMConfig;
};
+ historyStore?: HistoryStoreConfig;
+ disableHistory?: boolean;
historyDbPath?: string;
customPrompt?: string;
graphStore?: GraphStoreConfig;
@@ -137,4 +149,11 @@ export const MemoryConfigSchema = z.object({
customPrompt: z.string().optional(),
})
.optional(),
+ historyStore: z
+ .object({
+ provider: z.string(),
+ config: z.record(z.string(), z.any()),
+ })
+ .optional(),
+ disableHistory: z.boolean().optional(),
});
diff --git a/mem0-ts/src/oss/src/utils/factory.ts b/mem0-ts/src/oss/src/utils/factory.ts
index 5fb7caf8..62cfc78a 100644
--- a/mem0-ts/src/oss/src/utils/factory.ts
+++ b/mem0-ts/src/oss/src/utils/factory.ts
@@ -5,7 +5,12 @@ import { OpenAIStructuredLLM } from "../llms/openai_structured";
import { AnthropicLLM } from "../llms/anthropic";
import { GroqLLM } from "../llms/groq";
import { MemoryVectorStore } from "../vector_stores/memory";
-import { EmbeddingConfig, LLMConfig, VectorStoreConfig } from "../types";
+import {
+ EmbeddingConfig,
+ HistoryStoreConfig,
+ LLMConfig,
+ VectorStoreConfig,
+} from "../types";
import { Embedder } from "../embeddings/base";
import { LLM } from "../llms/base";
import { VectorStore } from "../vector_stores/base";
@@ -13,6 +18,10 @@ import { Qdrant } from "../vector_stores/qdrant";
import { RedisDB } from "../vector_stores/redis";
import { OllamaLLM } from "../llms/ollama";
import { SupabaseDB } from "../vector_stores/supabase";
+import { SQLiteManager } from "../storage/SQLiteManager";
+import { MemoryHistoryManager } from "../storage/MemoryHistoryManager";
+import { SupabaseHistoryManager } from "../storage/SupabaseHistoryManager";
+import { HistoryManager } from "../storage/base";
export class EmbedderFactory {
static create(provider: string, config: EmbeddingConfig): Embedder {
@@ -62,3 +71,22 @@ export class VectorStoreFactory {
}
}
}
+
+export class HistoryManagerFactory {
+ static create(provider: string, config: HistoryStoreConfig): HistoryManager {
+ switch (provider.toLowerCase()) {
+ case "sqlite":
+ return new SQLiteManager(config.config.historyDbPath || ":memory:");
+ case "supabase":
+ return new SupabaseHistoryManager({
+ supabaseUrl: config.config.supabaseUrl || "",
+ supabaseKey: config.config.supabaseKey || "",
+ tableName: config.config.tableName || "memory_history",
+ });
+ case "memory":
+ return new MemoryHistoryManager();
+ default:
+ throw new Error(`Unsupported history store provider: ${provider}`);
+ }
+ }
+}
diff --git a/mem0-ts/src/oss/src/vector_stores/supabase.ts b/mem0-ts/src/oss/src/vector_stores/supabase.ts
index fc05d302..f61beb8b 100644
--- a/mem0-ts/src/oss/src/vector_stores/supabase.ts
+++ b/mem0-ts/src/oss/src/vector_stores/supabase.ts
@@ -97,6 +97,11 @@ export class SupabaseDB implements VectorStore {
try {
// Verify table exists and vector operations work by attempting a test insert
const testVector = Array(1536).fill(0);
+ try {
+ await this.client.from(this.tableName).delete().eq("id", "test_vector");
+ } catch (error) {
+ console.warn("No test vector to delete, safe to ignore.");
+ }
const { error: testError } = await this.client
.from(this.tableName)
.insert({
@@ -113,6 +118,50 @@ export class SupabaseDB implements VectorStore {
1. The vector extension is enabled
2. The table "${this.tableName}" exists with correct schema
3. The match_vectors function is created
+
+RUN THE FOLLOWING SQL IN YOUR SUPABASE SQL EDITOR:
+
+-- Enable the vector extension
+create extension if not exists vector;
+
+-- Create the memories table
+create table if not exists memories (
+ id text primary key,
+ embedding vector(1536),
+ metadata jsonb,
+ created_at timestamp with time zone default timezone('utc', now()),
+ updated_at timestamp with time zone default timezone('utc', now())
+);
+
+-- Create the vector similarity search function
+create or replace function match_vectors(
+ query_embedding vector(1536),
+ match_count int,
+ filter jsonb default '{}'::jsonb
+)
+returns table (
+ id text,
+ similarity float,
+ metadata jsonb
+)
+language plpgsql
+as $$
+begin
+ return query
+ select
+ id,
+ similarity,
+ metadata
+ from memories
+ where case
+ when filter::text = '{}'::text then true
+ else metadata @> filter
+ end
+ order by embedding <=> query_embedding
+ limit match_count;
+end;
+$$;
+
See the SQL migration instructions in the code comments.`,
);
}