MemorySync
Getting Started

Google ADK Memory

A real BaseMemoryService for Google’s Agent Development Kit: ingestion methods that actually store your sessions, powering ADK’s native load_memory and preload_memory tools with durable cross-session memory — plus a guaranteed per-turn context tool, persistence callbacks, five agent memory tools, and async helpers.

What the integration provides

LayerWhat it does
MemorySyncMemoryServiceAll 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.
MemorySyncContextToolGuaranteed memory injection before every turn plus automatic user-turn persistence — no memory-tool calls left to model discretion.
create_memory_callbacksAn after_model_callback that persists assistant turns as they happen.
Agent tools + helperscreate_memorysync_tools() — five never-raise tools — and async helpers get_memory_context, search_memories, save_turn.
Mem0SupermemoryZepMemorySync
ADK integration exists✗ nothing shipped✗ nothing shipped△ zep-adk✓ google-adk-memorysync
add_session_to_memory stores the session✗ documented no-op✓ real ingestion, raises on failure
Works without pre-provisioning✗ silent skip unless user/thread pre-created✓ first call just works
Guaranteed per-turn injection✓ context tool, deduped per invocation
Repeated ingestion converges✓ event-id idempotency seeds

Install

pip install google-adk-memorysync google-adk

Set MEMORYSYNC_API_KEY in the environment, or pass api_key explicitly. Requires google-adk 2.x (Python 3.10+). This is a Python surface — for TypeScript agents use the Vercel AI SDK or Mastra integrations.

The memory service

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="thread-42"
)
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.

add_events_to_memory persists events incrementally as they happen and converges with a later whole-session add_session_to_memory — same seeds on both paths, each event stored exactly once. search_memory returns proper MemoryEntry objects (content, author, timestamp) so ADK’s preload_memory formatter renders them natively; search failures degrade to an empty response through on_error, never a crashed turn. By default the same user’s memory follows them across ADK apps; pass scope_to_app=True to silo each app_name.

Guaranteed context injection

from google.adk.agents import LlmAgent
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 + persist user turns
)
# Read-only variant (inject, never write):
LlmAgent(
name="assistant",
model="gemini-2.5-flash",
instruction="You are a helpful assistant.",
tools=[MemorySyncContextTool(persist=False)],
)

ADK’s load_memory leaves recall to model discretion and preload_memory only injects — nothing persists turns as they happen. The context tool does both: it recalls for tool_context.user_id and appends the block to the request instructions — once per invocation, even when a multi-tool turn makes several model calls — then persists the user’s message. Outages degrade to a memoryless turn through on_error, never a crashed one.

Assistant-side persistence

from google.adk.agents import LlmAgent
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()], # user turns + recall
**create_memory_callbacks(), # assistant turns
)
# Every exchange now persists automatically — and every surface
# shares the same idempotency seeds, so nothing double-stores.

Agent memory tools

from google.adk.agents import LlmAgent
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)
ToolWhat it doesFailure behaviour
add_memorySave one durable fact; duplicate saves answer “already stored”.Readable error string — never raises.
search_memorySemantic search with relevance scores.Readable error string.
list_memoriesNewest-first listing.Readable error string.
update_memoryChange tags/importance. Memory text is immutable.Readable error string.
delete_memoryPermanent delete by id, scoped to the configured user.Readable error string.

Same five operations, same response strings as the LangChain, AI SDK, CrewAI, Mastra, OpenAI Agents and LlamaIndex tool sets — an agent moved between frameworks keeps behaving the same way. Plain async callables that ADK auto-wraps as FunctionTools.

Standalone helpers

from google_adk_memorysync import (
get_memory_context,
save_turn,
search_memories,
)
# 1. Prompt-ready context block ("" for a new user)
context = await get_memory_context(
"what should I cook?", user_id="customer-7"
)
# 2. Scored raw results
hits = await search_memories("dietary preferences", user_id="customer-7")
# 3. Explicit persistence — RAISES on failure (an explicit call is
# owed the truth), unlike the degrading tool and callback planes.
await save_turn(
user_id="customer-7",
user="I'm vegetarian",
assistant="Noted!",
session_id="thread-42",
)

Public API

ExportKindNotes
MemorySyncMemoryServiceBaseMemoryServiceAll four methods real; scope_to_app, k, on_error optional. Ingestion raises on failure; search never does.
MemorySyncContextToolBaseToolGuaranteed injection + user-turn persistence; persist=False for read-only; deduped per invocation.
create_memory_callbacksCallback factoryReturns {after_model_callback: ...} persisting assistant turns; unpack into LlmAgent(**...).
create_memorysync_toolsTool factoryFive plain async callables ADK auto-wraps; read_only=True returns search + list only.
get_memory_context / search_memories / save_turnAsync helperssave_turn raises on failure; all share the same idempotency seeds.
MemorySyncAPIErrorExceptionCarries the HTTP status and server detail.

Supported versions

PackageRegistryRequiresRuntime
google-adk-memorysync 1.0.0PyPIgoogle-adk >=2 <3Python 3.10+

The test suite drives a REAL ADK Runner — the native preload_memory flow end to end, repeated-ingestion convergence, partial-event filtering, per-invocation injection dedup — and CI re-runs it against the latest google-adk 2.x release on every push, so an interface change upstream fails our pipeline before it can fail your agent.

Where to go next

Was this page helpful?