Repository navigation
feat(memory): add batch_add, batch_update and batch_delete to Memory and AsyncMemory - #7599
Open
nothariharan wants to merge 1 commit into
Open
nothariharan wants to merge 1 commit into
nothariharan wants to merge 1 commit into
Conversation
…and AsyncMemory The OSS Memory surface had no batch methods, so callers had to loop over add/update/delete individually. The hosted client already shipped batch_update and batch_delete (mem0/client/main.py), which confirmed the gap and gave a payload shape to match. Each method runs its items concurrently but returns one entry per input item, in input order, and isolates per-item failures so a single bad item cannot take the batch down. max_workers (sync) / max_concurrency (async) caps concurrency, which is what an on-disk or embedded vector store needs - Qdrant's local mode is not thread-safe and concurrent writes raise 'cannot commit - no transaction is active'. Tests: 29 cases covering order preservation, metadata merging, per-item error isolation, the concurrency knobs, and the List import that an earlier attempt sank. Also reorders the mem0.exceptions imports so pre-commit isort passes - that block was already dirty on main, and touching this file requires it.
|
Hi @nothariharan, thanks for opening this pull request. This is just a soft check: you are not yet in this repo's vouched contributor list ( A maintainer can vouch for you by commenting |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linked Issue
Closes #3761
Description
Adds
batch_add,batch_updateandbatch_deleteto bothMemoryandAsyncMemory. The OSS surface had no batch methods, so callers had to loopadd/update/deleteone item at a time. The hosted client already shippedbatch_updateandbatch_delete(mem0/client/main.py:692,:719), which confirmed the gap and supplied the payload shape to match.Each method runs its items concurrently, but returns one entry per input item, in input order, and isolates per-item failures so a single bad item cannot take the batch down.
Two prior PRs were open against this issue and neither landed. Rather than pile on, this PR is built to be judged on its own, so here is where each one actually stands:
main. 89 days with no activity, zero reviews, its only comment is the stale bot.kartik-mem02026-08-03mainplus 30+ days of inactivity; closed in a backlog cleanup.#4804 does not compile on
mainmem0/memory/main.pydoes not importList(from typing import Any, Dict, Optionalat line 13) and has nofrom __future__ import annotations, so annotations are evaluated eagerly. Merging #4804 breaksimport mem0outright:I reproduced this by applying #4804's diff onto current
main(b7ad69af) in a throwaway worktree and importing it, so this is a measured result, not inferred.#4804 also carried no tests (
+237/-0, one file). It has been sitting unbuilt for 89 days because CI had nothing to run against it.What this PR does differently
main. Branch cut fromb7ad69af, 417 existing tests still pass, plus 29 new ones.id(input), which collapses on duplicate ids and returns a wrong default for anything unmatched. This version uses indexed futures, sobatch_update([{"memory_id": "same"...}, {"memory_id": "same"...}])stays aligned.max_workers/max_concurrencyescape hatch. My own end-to-end runs hitcannot commit - no transaction is active: Qdrant's local/embedded mode is not thread-safe (server and cloud Qdrant are). feat: add batch_add, batch_update, batch_delete to Memory and AsyncMemory (#3761) #4804 had no way to opt out, so it would have failed for every on-disk or embedded deployment.memory_idorindex), not just a message.On the open design question
The triage note asks whether
batch_addshould runadd()in parallel or batch the LLM extraction step. This PR takes paralleladd()calls, because it keepsadd()'s behaviour byte-identical and touches no extraction prompt. It is documented in the docstring and indocs/that this costs one LLM extraction and one set of vector-store writes per item. If the maintainers want the batched-extraction variant instead, that is a much larger change and I would rather not smuggle it in here.Type of Change
AI Assistance
Tool: opencode (anthropic-backed). I read every line of this diff myself and I can explain each one and how it interacts with
Memory.add/update/delete,SQLiteManager's locking, and the vector-store contract. What I checked manually: thatSQLiteManageris thread-safe (check_same_thread=Falseplus athreading.Lockon every operation) before parallelising anything, and that the hosted client'sbatch_update/batch_deletepayload shapes are the ones I mirrored. The two bugs my own testing caught (the unimported-Listbreakage in #4804, and the non-thread-safe local Qdrant writes) are both fixed here.While the code was written with agent assistance, I have read and verified the full diff, run the tests myself, and can walk through the design decisions above.
Breaking Changes
N/A - purely additive. No existing signature or behaviour changes.
Test Coverage
tests/memory/test_batch_operations.py- 29 cases covering input order preservation, batch-vs-per-item metadata merging (including not mutating the caller's dict), per-item error isolation, themax_workers/max_concurrencyknobs, duplicate ids staying aligned, and a regression test assertingListis importable so theNameErrorthat sank #4804 cannot come back.End-to-end verification
No mocked
add/update/delete. Run against a real in-process Qdrant store (qdrant_client, no server) and a real deterministic embedder, so every memory was genuinely written, searched, updated and deleted:Steps 5-8 pass
max_workers=1/max_concurrency=1, because the Qdrant local mode backing store is the one that is not thread-safe. That is the documented remedy and the reason the knob exists.Checklist