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

Đang mở
#7,109 8 bình luận 0 reaction 1 người được giao Được @surajksharma07 nhận Xem trên GitHub
core
Ngôn ngữ chính
Python
Star
21.5k
Fork
4k
Merge trung bình
1 ngày 14 giờ
Pull request đã merge (30 ngày)
37

Mô tả

## 🔴 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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.