google / google/adk-python

Memory has no removal path: delete_session() leaves what add_session_to_memory() copied, and BaseMemoryService exposes no way to remove it

Abierto
#7,109 8 comentarios 0 reacciones 1 asignado Reclamado por @surajksharma07 Ver en GitHub
core
Lenguaje dominante
Python
Estrellas
21.5k
Forks
4k
Merge medio
1 d 14 h
PR fusionados (30 d)
37

Descripción

## 🔴 Required Information

**Describe the Bug:**

`BaseSessionService` has `delete_session`, implemented by all five session backends. `BaseMemoryService` has three ingestion methods and one search method, and **no removal method at all**:

```
BaseMemoryService public API: ['add_events_to_memory', 'add_memory',
'add_session_to_memory', 'search_memory']
```

Since `add_session_to_memory()` copies the session's content into the memory service, deleting the session leaves that copy in place with no API to remove it. A caller who deletes a session reasonably expects the conversation to be gone; it is still returned verbatim by `search_memory()`.

The base class docstring describes the service as providing *"functionality to ingest conversation history into memory so that it can be used for user queries"* — it does not say the ingestion is one-way.

**Steps to Reproduce:**

1. `pip install google-adk==2.9.0`
2. Save the script under *Minimal Reproduction Code* as `repro.py`
3. `python repro.py`

**Expected Behavior:**

Either a way to remove what was ingested (for a session, a user, or a memory entry), or documentation stating that ingestion is one-way and that memory has to be cleaned up through the backend directly.

**Observed Behavior:**

```
before delete — in memory : True
after delete — session : gone
after delete — in memory : STILL THERE
retrieved verbatim : my-passport-number-is-AB1234567

BaseMemoryService public API: ['add_events_to_memory', 'add_memory', 'add_session_to_memory', 'search_memory']
any removal method? : False
```

The middle line is the negative control: the session really is deleted, so the surviving copy is the memory service's, not a stale session.

**Environment Details:**

- ADK Library Version: `google-adk 2.9.0`
- Desktop OS: macOS 26.6.2 (arm64)
- Python Version: 3.12.13

**Model Information:**

- Are you using LiteLLM: No
- Which model is being used: N/A — no model is involved

---

## 🟡 Optional Information

**Additional Context — scope, stated restrictively:**

- This is a **gap in the abstraction**, not a runtime failure. Nothing raises, and `InMemoryMemoryService` behaves exactly as written.
- I have **not** checked whether the hosted backends (Vertex AI RAG, Memory Bank) offer deletion **outside** ADK through their own APIs or consoles. They may well do. The asymmetry I am reporting is in ADK's own surface: one store is deletable through the abstraction and the other is not, while one feeds the other.
- I am deliberately **not** proposing an API shape. Whether removal is keyed by session, by user, or by memory entry is a design decision, and `search_memory` results do not currently carry an identifier that a delete call could take.
- I am not raising any compliance or regulatory claim; that is yours to judge. I am only showing that the two lifecycles are asymmetric and that the docstring does not say so.

**Minimal Reproduction Code:**

```python
"""delete_session() removes the session; what add_session_to_memory() copied stays,
and BaseMemoryService exposes no way to remove it."""
import asyncio, inspect
from google.genai import types
from google.adk.events.event import Event
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
from google.adk.memory.base_memory_service import BaseMemoryService

SECRET = 'my-passport-number-is-AB1234567'

async def main():
sessions, memory = InMemorySessionService(), InMemoryMemoryService()
s = await sessions.create_session(app_name='app', user_id='u1')
await sessions.append_event(s, Event(author='user', invocation_id='i1',
content=types.Content(role='user', parts=[types.Part(text=SECRET)])))
s = await sessions.get_session(app_name='app', user_id='u1', session_id=s.id)
await memory.add_session_to_memory(s)

hit = await memory.search_memory(app_name='app', user_id='u1', query='passport')
print('before delete — in memory :', bool(hit.memories))

await sessions.delete_session(app_name='app', user_id='u1', session_id=s.id)

gone = await sessions.get_session(app_name='app', user_id='u1', session_id=s.id)
print('after delete — session :', 'gone' if gone is None else 'STILL THERE') # negative control

hit = await memory.search_memory(app_name='app', user_id='u1', query='passport')
print('after delete — in memory :', 'STILL THERE' if hit.memories else 'gone')
if hit.memories:
print(' retrieved verbatim :', hit.memories[0].content.parts[0].text)

api = [n for n, _ in inspect.getmembers(BaseMemoryService, inspect.isfunction)
if not n.startswith('_')]
print('\nBaseMemoryService public API:', api)
print('any removal method? :',
any(k in n for n in api for k in ('delete','remove','purge','forget')))

asyncio.run(main())
```

**How often has this issue occurred?:**

- Always (100%) — deterministic; no model, no network, no timing involved.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.