Firestore session and memory services: fix nested state merge, package exports and memory duplicates
Los mantenedores suelen responder en 5 días
@surajksharma07 ya está trabajando en esto.
Desde el 21/9/2026.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 58/100
Línea de trabajo
Start in src/google/adk/integrations/firestore/firestore_session_service.py, firestore_memory_service.py, and init.py, then review tests/unittests/sessions/test_session_service.py and the Firestore unit tests. Verify the state replacement, event timestamps, user-state lookup, package exports, idempotent memory writes, and FieldFilter queries against the shared session contract and relevant unit tests.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
🔴 Required Information
Is your feature request related to a specific problem?
Yes. Python has shipped FirestoreSessionService and FirestoreMemoryService since 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:
- Nested
user:/app:state is merged instead of replaced. Updatinguser:profile
from{"name": "Alice", "role": "admin"}to{"name": "Alice"}stores
{"name": "Alice", "role": "admin"}, so the removed key comes back on the next
get_session. The in-memory, SQLite and database session services replace the value.
Cause:user_states/app_statesare written withtransaction.set(..., merge=True)in
bothcreate_sessionandappend_event, and Firestore deep-merges nested maps.
Session-scoped state is not affected (it is stored as JSON). - The services can't be imported from their package.
from google.adk.integrations.firestore import FirestoreSessionServiceraisesImportError
becauseintegrations/firestore/__init__.pyexports nothing. It is the only package under
integrations/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 write
time instead ofevent.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, so
adding 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.- Positional-filter warnings.
list_sessionsandget_sessionwithafter_timestamp
call.where("field", op, value), sogoogle-cloud-firestoreemits
UserWarning: 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:
- Write
user:/app:state back whole so a new value replaces the old one. - Export
FirestoreSessionServiceandFirestoreMemoryServicefrom
google.adk.integrations.firestore, loaded lazily (asintegrations/model_armordoes) so
importing the package still does not requiregoogle-cloud-firestore. - Store each event's own timestamp so
after_timestampand event ordering use event time. - Implement
get_user_stateby reading theuser_statesdocument the service already writes. - Give each memory entry a stable document ID derived from app, user, session and event IDs,
so re-adding a session overwrites instead of duplicating, and implement
add_events_to_memory. - Pass query filters as
where(filter=FieldFilter(...)), asFirestoreMemoryServicealready
does.
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:
- A value removed from nested user or app state (for example a role or a permission flag)
silently stays in Firestore and comes back on the next turn. - Memory storage grows with every turn when sessions are added to memory after each turn.
- Moving an agent between session services changes its behavior.
- Python users have no documentation showing how to use these services: the adk.dev
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-docs adding Python usage to the
Firestore page.
🟡 Recommended Information
Describe Alternatives You've Considered
DatabaseSessionServicewith Cloud SQL or AlloyDB: works correctly, but needs a database
instance to run and manage, which Firestore avoids.VertexAiSessionService: works, but ties sessions to Agent Engine.- Workarounds on the current Firestore services: import from the full module path, avoid dict
values inuser:/app:state, and add each session to memory only once. These avoid the
symptoms 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 its
unit tests:
# 1. create_session / append_event: write the full, already-updated dict
transaction.set(user_ref, current_user) # was: transaction.set(user_ref, current_user, merge=True)
# 3. append_event: store the event's own time
"timestamp": datetime.fromtimestamp(event.timestamp, tz=timezone.utc) # was: firestore.SERVER_TIMESTAMP
# 5. memory: stable document ID per event
doc_id = sha256("\x00".join((app_name, user_id, session_id or "", event.id)))
After the change, a local run of the shared session contract suite
(tests/unittests/sessions/test_session_service.py) against a real Firestore database goes
from 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, a
Firestore Native database):
import asyncio, time
from google.adk.events.event import Event
from google.adk.events.event_actions import EventActions
from google.adk.integrations.firestore.firestore_session_service import FirestoreSessionService
from google.cloud import firestore
async def main():
client = firestore.AsyncClient(project="PROJECT", database="DATABASE")
svc = FirestoreSessionService(client=client)
s = await svc.create_session(app_name="demo", user_id="alice")
for i, value in enumerate(({"name": "Alice", "role": "admin"}, {"name": "Alice"})):
await svc.append_event(s, Event(
author="user", invocation_id=f"i{i}", timestamp=time.time(),
actions=EventActions(state_delta={"user:profile": value})))
print("same Session object right after update :", s.state["user:profile"])
fresh = await svc.get_session(app_name="demo", user_id="alice", session_id=s.id)
print("after reload (get_session) :", fresh.state["user:profile"])
asyncio.run(main())
same Session object right after update : {'name': 'Alice'}
after reload (get_session) : {'name': 'Alice', 'role': 'admin'}
Not proposed here, open questions:
- The 4 remaining contract failures: three come from
last_update_timeusing Firestore's
serverupdateTimeinstead of the appended event's timestamp, which looks intentional after
#5632 / #5642. The fourth islist_sessions(user_id=None), which needs a single-field
collection-group index onsessions.appName; I would document that. - Should the CLI accept
--session_service_uri firestore://...and
--memory_service_uri firestore://...? Today an unregistered session scheme falls back to
DatabaseSessionServiceand fails withValueError: Invalid database URL format. I have a
workingservices.pyregistration and can propose a built-in scheme if that is wanted. - Registering Firestore in
tests/unittests/sessions/_conformance.pyneeds a stateful Firestore
fake. I can do that as a follow-up.
- Lenguaje dominante
- Python
- Estrellas
- 21.6k
- Forks
- 4k
- Merge medio
- 12 h 6 min
- PR fusionados (30 d)
- 4
Preparar el entorno
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de google/adk-python
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
google/adk-python#7334 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
Redis append_event stamps last_update_time with the wall clock, not the event timestampPosiblemente ocupada @sanketpatil06 la tomó hace 1 día. Abiertoservices
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
google/adk-python#7292 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 5 días
-
GoogleOidcVerifier treats string "false" as a verified email claimPosiblemente ocupada @surajksharma07 la tomó hace 1 día. Abiertocore
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
google/adk-python#7289 · 5 comentarios · 1 asignado ·
Los mantenedores suelen responder en 5 días
-
RestApiTool raises uncaught KeyError when a required path param is omittedPosiblemente ocupada @llalitkumarrr la tomó hace 1 día. Abiertotools
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
google/adk-python#7282 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 5 días
-
CredentialsManager should also extract scopes when populating auth schemesPosiblemente ocupada @sanketpatil06 la tomó hace 3 días. Abiertocore needs review
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
google/adk-python#7266 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 5 días
Todos los issues de google/adk-python
Issues similares
-
ACK_WAITING HELP_WANTED UPDATE_CS
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
OWASP/CheatSheetSeries#2458 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 90/100
BasedHardware/omi#19711 ·
Los mantenedores suelen responder en 1 día
-
Qwen3_5MoeModel no longer returns router_logits, breaking aux loss with output_router_logits=TrueAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
huggingface/transformers#49172 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
vllm-project/vllm-metal#885 ·
Los mantenedores suelen responder en 1 día