diff --git a/.changeset/yummy-actors-obey.md b/.changeset/yummy-actors-obey.md new file mode 100644 index 000000000..ebdf0c9ec --- /dev/null +++ b/.changeset/yummy-actors-obey.md @@ -0,0 +1,5 @@ +--- +"@voltagent/core": patch +--- + +Fix issue where vector embeddings were not deleted when clearing messages when using a vector adapter diff --git a/packages/core/src/memory/index.ts b/packages/core/src/memory/index.ts index e7a613967..2aad00610 100644 --- a/packages/core/src/memory/index.ts +++ b/packages/core/src/memory/index.ts @@ -164,12 +164,30 @@ export class Memory { /** * Clear messages for a user + * Also purges vector embeddings when a vector adapter is configured, + * preventing orphaned embeddings from surfacing in future semantic searches. */ async clearMessages( userId: string, conversationId?: string, context?: OperationContext, ): Promise { + // If vector adapter is configured and a conversationId is given, clean up vectors first + if (this.vector && conversationId) { + try { + const messages = await this.storage.getMessages(userId, conversationId); + if (messages.length > 0) { + const vectorIds = messages.map((msg) => `msg_${conversationId}_${msg.id}`); + await this.vector.deleteBatch(vectorIds); + } + } catch (error) { + console.warn( + `Failed to delete vectors when clearing messages for conversation ${conversationId}:`, + error, + ); + } + } + return this.storage.clearMessages(userId, conversationId, context); } diff --git a/packages/core/src/memory/semantic-search.spec.ts b/packages/core/src/memory/semantic-search.spec.ts index fbc0f4788..222db1fac 100644 --- a/packages/core/src/memory/semantic-search.spec.ts +++ b/packages/core/src/memory/semantic-search.spec.ts @@ -455,5 +455,48 @@ describe("Memory V2 - Semantic Search", () => { expect.arrayContaining([`msg_${conversationId}_msg1`, `msg_${conversationId}_msg2`]), ); }); + + it("should delete vectors when clearMessages is called with a conversationId", async () => { + const userId = "user123"; + const conversationId = "conv-clear-test"; + + // Create conversation and add messages + await memory.createConversation({ + id: conversationId, + userId, + resourceId: "agent1", + title: "Clear Test", + metadata: {}, + }); + + const messages: UIMessage[] = [ + { + id: "msg-a", + role: "user", + parts: [{ type: "text", text: "Remember my name is Sujal" }], + }, + { + id: "msg-b", + role: "assistant", + parts: [{ type: "text", text: "Got it, Sujal!" }], + }, + ]; + + await memory.addMessages(messages, userId, conversationId); + + const deleteBatchSpy = vi.spyOn(vector, "deleteBatch"); + + // Clear messages (should also clean up vectors) + await memory.clearMessages(userId, conversationId); + + // Verify vectors were deleted + expect(deleteBatchSpy).toHaveBeenCalledWith( + expect.arrayContaining([`msg_${conversationId}_msg-a`, `msg_${conversationId}_msg-b`]), + ); + + // Verify SQL messages are also gone + const remaining = await memory.getMessages(userId, conversationId); + expect(remaining).toHaveLength(0); + }); }); });