Repository navigation
Firestore session and memory services: fix nested state merge, package exports and memory duplicates聽#7192
Description
Activity
- addedservices[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc
on Sep 21, 2026 @vishal-bulbule Went through all seven points and see the same behaviour on v2.9.2 and current main, none of it is in a release yet so upgrading won't help.
Until #7195 is merged a suggested workaround for anyone hitting this: import from the full module path, keep nested dicts out of user:/app: state (flat keys instead) and add each session to memory only once. Could you verify that holds up on your Firestore setup?
Also before you mark #7195 ready it'd be good to re-run its tests once more on top of the latest main so the review goes smoothly.
- added 7 commits that reference this issue
on Sep 22, 2026 Thanks for going through all seven.
I rebased #7195 onto current main (3833976) and re-ran everything:
- Firestore unit tests: 50 passed
- Full unit suite: 15584 passed, 0 failed
- mypy on the Firestore package and pre-commit: clean
- Session contract suite against a real Firestore database: 27 passed, 4 failed, the same as before the rebase. The 4 are the
last_update_timesource (3 tests) and the collection-group index needed bylist_sessions(user_id=None)(1 test), both left out of scope in this issue.
#7195 is already marked ready for review, it was not opened as a draft.
On the workaround, I ran one script against a real Firestore database, once on current main and once with #7195:
# Point Workaround on current main With #7195 1 Package import Full module path works Package import works 2 Nested user:/app:state mergedFlat keys work. A removed field reads as Nonerather than absentNested dict is replaced 3 after_timestampfilters on write timeNo workaround: both events returned Only the newer event returned 4 get_user_stateNo workaround: NotImplementedErrorSupported 5 Positional-filter UserWarningNo workaround, harmless No warning 6 Duplicate memories Adding each session once works (4 docs, re-adding makes 8) Re-adding keeps 4 docs 7 add_events_to_memoryNo workaround: NotImplementedErrorSupported So the workaround holds for 1, 2 and 6. Points 3, 4, 5 and 7 need #7195.
@vishal-bulbule. Went through the diff and ran the Firestore tests on the branch: all 50 pass and 16 of them fail against the current main source so the new tests really do catch the old behaviour.
Nothing under integrations/firestore has changed on main since 3833976 and v2.9.2 is still the latest release so your table stands: the workaround covers 1, 2 and 6 and 3, 4, 5 and 7 have to wait for #7195.
One small thing for the review: an event added with add_events_to_memory without a session_id and later with one gets two different doc IDs so might be worth a line in the docstring.
@DeanChensj could you take a look at #7195 when you get a chance? Its 2 workflows are still waiting for approval.
Thanks for running it yourself.
Added the docstring note on
add_events_to_memory, since the session ID is hashed into the entry ID:Re-adding an event with the same session ID overwrites its entry. The session ID is part of that entry's ID, so an event added without one and later added with one is stored twice;
search_memoryreturns one copy.The second sentence is worth stating because
search_memoryde-duplicates on author, text and timestamp, so the extra document does not show up twice in results, only in the collection.The branch is rebased on main (3833976) and the 50 Firestore tests, the full unit suite, mypy and pre-commit all pass.
The docstring note reads well @vishal-bulbule and the second sentence matches what search_memory does since it drops repeats on (author, text, timestamp).
Merged the updated branch (987a012) onto current main (9625b06): merges cleanly, the 50 Firestore tests pass and the same 16 still fail when run against main's own Firestore code so nothing has drifted since 3833976.
Nothing else needed from your side. v2.9.2 is still the latest release so the workaround for 1, 2 and 6 stands until #7195 lands.
@DeanChensj #7195 is ready for review whenever you get to it, the 2 workflows still need approval.
- added a commit that references this issue
on Oct 8, 2026
馃敶 Required Information
Is your feature request related to a specific problem?
Yes. Python has shipped
FirestoreSessionServiceandFirestoreMemoryServicesince v1.31.0(#5088), but they don't behave like the other session and memory services, and there is no
Python documentation for them. Testing them against a real Firestore database on 2.9.1 and
main(f33d4923), I reproduced these problems:user:/app:state is merged instead of replaced. Updatinguser:profilefrom
{"name": "Alice", "role": "admin"}to{"name": "Alice"}stores{"name": "Alice", "role": "admin"}, so the removed key comes back on the nextget_session. The in-memory, SQLite and database session services replace the value.Cause:
user_states/app_statesare written withtransaction.set(..., merge=True)inboth
create_sessionandappend_event, and Firestore deep-merges nested maps.Session-scoped state is not affected (it is stored as JSON).
from google.adk.integrations.firestore import FirestoreSessionServiceraisesImportErrorbecause
integrations/firestore/__init__.pyexports nothing. It is the only package underintegrations/like this.GetSessionConfig(after_timestamp=...)returns the wrong events. Events are stored with"timestamp": firestore.SERVER_TIMESTAMP, so the filter and event ordering use the writetime instead of
event.timestamp.get_user_stateis not supported. It raisesNotImplementedError, while the in-memory,database, SQLite and Redis session services implement it.
add_session_to_memorystores duplicates. Each call writes a new document per event, soadding the same session again (for example after every turn) stores every memory again.
Search hides this by de-duplicating results, but storage keeps growing.
add_events_to_memoryis also not implemented;InMemoryMemoryServicesupports it.list_sessionsandget_sessionwithafter_timestampcall
.where("field", op, value), sogoogle-cloud-firestoreemitsUserWarning: Detected filter using positional argumentson every call.Describe the Solution You'd Like
Make the Firestore services behave like the other backends, with no change to their public
API other than implementing two existing base-class methods:
user:/app:state back whole so a new value replaces the old one.FirestoreSessionServiceandFirestoreMemoryServicefromgoogle.adk.integrations.firestore, loaded lazily (asintegrations/model_armordoes) soimporting the package still does not require
google-cloud-firestore.after_timestampand event ordering use event time.get_user_stateby reading theuser_statesdocument the service already writes.so re-adding a session overwrites instead of duplicating, and implement
add_events_to_memory.where(filter=FieldFilter(...)), asFirestoreMemoryServicealreadydoes.
Impact on your work
I build agents for Google Cloud customers, and Firestore is the natural serverless session
store for agents on Cloud Run. Today:
silently stays in Firestore and comes back on the next turn.
Firestore page covers Java only.
Willingness to contribute
Yes. I have the fix ready with unit tests, one commit per item above, and can open the PR
once this is triaged. I will also open a PR in
google/adk-docsadding Python usage to theFirestore page.
馃煛 Recommended Information
Describe Alternatives You've Considered
DatabaseSessionServicewith Cloud SQL or AlloyDB: works correctly, but needs a databaseinstance to run and manage, which Firestore avoids.
VertexAiSessionService: works, but ties sessions to Agent Engine.values in
user:/app:state, and add each session to memory only once. These avoid thesymptoms but are easy to miss, and nothing in the docs mentions them.
Proposed API / Implementation
No new public API. The changes stay inside
src/google/adk/integrations/firestore/and itsunit tests:
After the change, a local run of the shared session contract suite
(
tests/unittests/sessions/test_session_service.py) against a real Firestore database goesfrom 18 passed / 13 failed to 27 passed / 4 failed. The 4 remaining failures are covered below.
Additional Context
Minimal reproduction for item 1 (
pip install google-adk==2.9.1 google-cloud-firestore, aFirestore Native database):
Not proposed here, open questions:
last_update_timeusing Firestore'sserver
updateTimeinstead of the appended event's timestamp, which looks intentional afterFirestore Session Service does not handle timestamp correctly聽#5632 / fix(firestore): populate last_update_time in list_sessions from Firestore updateTime聽#5642. The fourth is
list_sessions(user_id=None), which needs a single-fieldcollection-group index on
sessions.appName; I would document that.--session_service_uri firestore://...and--memory_service_uri firestore://...? Today an unregistered session scheme falls back toDatabaseSessionServiceand fails withValueError: Invalid database URL format. I have aworking
services.pyregistration and can propose a built-in scheme if that is wanted.tests/unittests/sessions/_conformance.pyneeds a stateful Firestorefake. I can do that as a follow-up.