Metadata-Version: 2.5
Name: google-adk-memorysync
Version: 1.1.0
Summary: MemorySync memory for Google ADK: a real BaseMemoryService (ingestion included) powering the native load_memory/preload_memory tools, a guaranteed per-turn context tool, a turn-capture callback, and agent memory tools.
Project-URL: Homepage, https://memorysync.io
Project-URL: Documentation, https://docs.memorysync.io/guides/google-adk
Project-URL: API Reference, https://docs.memorysync.io/api/overview
Project-URL: Changelog, https://docs.memorysync.io/release-notes
Project-URL: Support, https://docs.memorysync.io/debugging/support
Project-URL: Status, https://status.memorysync.io
Author: MemorySync
License: MIT
Keywords: adk,agent-development-kit,agent-memory,ai,ai-agents,google-adk,llm,long-term-memory,memory,memory-service,memorysync
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: google-adk<3,>=2
Requires-Dist: httpx<1.0,>=0.25
Requires-Dist: memorysync>=1.9
Description-Content-Type: text/markdown

# google-adk-memorysync

Long-term memory for [Google ADK](https://google.github.io/adk-docs/) (Agent Development Kit), backed by [MemorySync](https://memorysync.io) — a **real** `BaseMemoryService` whose ingestion methods actually store your sessions, powering ADK's native `load_memory` / `preload_memory` tools with durable, cross-session, cross-app memory.

- **`MemorySyncMemoryService`** — all four `BaseMemoryService` methods implemented: `add_session_to_memory`, `add_events_to_memory`, `add_memory`, `search_memory`. Plug it into `Runner(memory_service=...)` and ADK's own memory tools just work.
- **`MemorySyncContextTool`** — guaranteed memory injection before every turn plus automatic capture of the user's messages, no memory-tool calls left to model discretion.
- **`create_memory_callbacks`** — an `after_model_callback` that captures the user's message once the model has answered it.
- **Five agent tools** — add, search, list, update, delete; they never raise.
- **Async helpers** — `get_memory_context`, `search_memories`, `save_turn`.

What the user says is sent to fact extraction: MemorySync stores the durable facts a message contains (filler such as "ok" or "thanks" yields none), not the message itself. Assistant replies are not stored as memories, so no surface sends them.

```bash
pip install google-adk-memorysync google-adk
```

Set `MEMORYSYNC_API_KEY` in the environment (create a key at [app.memorysync.io](https://app.memorysync.io)), or pass `api_key` explicitly. Python 3.10+, `google-adk` 2.x.

## The memory service

```python
from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import preload_memory
from google_adk_memorysync import MemorySyncMemoryService

memory_service = MemorySyncMemoryService()  # reads MEMORYSYNC_API_KEY

agent = LlmAgent(
    name="assistant",
    model="gemini-2.5-flash",
    instruction="You are a helpful assistant.",
    tools=[preload_memory],  # ADK's native tool — our service powers it
)

runner = Runner(
    agent=agent,
    app_name="support",
    session_service=InMemorySessionService(),
    memory_service=memory_service,
)

# ... after a conversation, ingest the session:
session = await runner.session_service.get_session(
    app_name="support", user_id="customer-7", session_id=session_id
)
await memory_service.add_session_to_memory(session)
```

The next run — any session, any process, any deploy — `preload_memory` injects what MemorySync knows about the user before the model call.

**Ingestion is real.** `add_session_to_memory` sends every completed text message the user wrote to fact extraction (the agent's replies, streaming `partial` chunks, tool traffic and authorless events are skipped) and raises on failure — a memory service that silently drops sessions is data loss behind a green pipeline. Re-ingesting the same session is recognised server-side and extracts nothing twice: every message carries an idempotency seed derived from its ADK event id, plus the event's timestamp.

**Incremental ingestion.** `add_events_to_memory(app_name=..., user_id=..., events=[...])` sends the user's messages as they happen and converges with a later whole-session `add_session_to_memory` — same seeds on both paths, each message extracted exactly once.

**Search never raises.** `search_memory` returns proper `MemoryEntry` objects (`content`, `author` when the memory's metadata names one, `timestamp`, `custom_metadata.memory_id`) so ADK's `preload_memory` formatter renders them natively; failures degrade to an empty response through `on_error`.

**Scoping.** By default the same user's memory follows them across ADK apps (that's the point of a memory service); pass `scope_to_app=True` to silo each `app_name`.

## Guaranteed context injection

ADK's `load_memory` leaves recall to model discretion and `preload_memory` only injects — nothing captures what the user says as it happens. The context tool does both:

```python
from google_adk_memorysync import MemorySyncContextTool

agent = LlmAgent(
    name="assistant",
    model="gemini-2.5-flash",
    instruction="You are a helpful assistant.",
    tools=[MemorySyncContextTool()],  # inject memory + send user turns
)
```

Before every turn it recalls relevant memories for `tool_context.user_id` and appends them to the request instructions — once per invocation, even when a multi-tool turn makes several model calls. After injection it sends the user's message to fact extraction (`persist=False` for read-only). Outages degrade to a memoryless turn through `on_error`, never a crashed one.

To remember a message only once the model has answered it, capture with the callback instead and keep the tool for recall:

```python
from google_adk_memorysync import MemorySyncContextTool, create_memory_callbacks

agent = LlmAgent(
    name="assistant",
    model="gemini-2.5-flash",
    instruction="You are a helpful assistant.",
    tools=[MemorySyncContextTool(persist=False)],  # recall only
    **create_memory_callbacks(),  # after_model_callback sends the user's message
)
```

The callback sends the invocation's user message (`callback_context.user_content`) after the model's first complete text response — once per invocation; streaming chunks, failed model calls and function-call-only steps send nothing. The model's reply is not sent.

Retries are recognised server-side and extract nothing twice: the context tool, the callback and `save_turn` derive the same content seed for a message in a session (so the tool and the callback together still extract it once, and the second send is not counted), and the memory service keys each message on its ADK event id. The two seed schemes are separate — when you also call `add_session_to_memory` for a session, turn per-turn capture off (`persist=False`, no callback) so each message is sent once.

## Agent tools

```python
from google_adk_memorysync import create_memorysync_tools

agent = LlmAgent(
    name="assistant",
    model="gemini-2.5-flash",
    instruction="Use the memory tools to remember durable facts.",
    tools=create_memorysync_tools(user_id="customer-7"),
)

# Untrusted agents: search + list only.
create_memorysync_tools(user_id="customer-7", read_only=True)
```

`add_memory`, `search_memory`, `list_memories`, `update_memory`, `delete_memory` — the same five operations, same response strings as the MemorySync LangChain, AI SDK, CrewAI, Mastra, OpenAI Agents and LlamaIndex tool sets. Plain async callables that ADK auto-wraps as `FunctionTool`s; failures return short readable strings, never exceptions.

## Helpers

```python
from google_adk_memorysync import get_memory_context, save_turn, search_memories

context = await get_memory_context("what should I cook?", user_id="customer-7")
hits = await search_memories("dietary preferences", user_id="customer-7")
await save_turn(user_id="customer-7", user="I'm vegetarian", assistant="Noted!")
```

`save_turn` sends the `user` message to fact extraction; `assistant` is accepted so an exchange can be passed as-is, but it is not sent — assistant replies are not stored as memories. It raises on failure — an explicit persist call is owed the truth.

## Version support

| Package | Requires | Runtime |
| --- | --- | --- |
| `google-adk-memorysync` 1.1.0 | `google-adk` >=2,<3 | Python 3.10+ |

CI drives a REAL ADK `Runner` — native `preload_memory` end to end, repeated-ingestion convergence, partial-event filtering, per-invocation injection dedup — against the latest `google-adk` 2.x release on every push.

## Documentation

- [Google ADK Memory guide](https://docs.memorysync.io/guides/google-adk)
- [MemorySync docs](https://docs.memorysync.io)
- [Get an API key](https://app.memorysync.io)
