MemorySync
Getting Started

Pydantic AI Memory

One capability in the Agent constructor gives every run recalled context and automatic turn persistence — through Pydantic AI’s own capability channels, so memory never pollutes your message history and a failed run never leaves a half-remembered turn. Plus five agent tools, typed search results, and async helpers.

What the integration provides

LayerWhat it does
MemorySyncCapabilityRecall injected through the framework’s instructions channel before every run; completed turns persisted in after_run. One entry in Agent(capabilities=[...]).
Agent toolscreate_memorysync_tools() — five structured tools (add, search, list, update, delete) the model can call, which never raise.
create_memory_search_toolMemory search returning typed list[MemoryResult] Pydantic models instead of prose.
Helpersget_memory_context, search_memories, save_turn — async, for hand-wired setups.
Mem0SupermemoryZepMemorySync
Pydantic AI integration exists✗ nothing shipped✗ nothing shipped△ zep-pydantic-ai✓ pydantic-ai-memorysync
Compatible with pydantic-ai v2✗ pins pydantic-ai <2✓ built on the v2 capability system
Failed runs leave no half-turns✗ persists user turn mid-run✓ success-only by construction
Replay-safe injection✗ mutates message history✓ instructions channel, never a message part
Typed tool resultslist[MemoryResult]

Install

pip install pydantic-ai-memorysync

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

The capability

from pydantic_ai import Agent
from pydantic_ai_memorysync import MemorySyncCapability
agent = Agent(
"openai:gpt-5",
instructions="You are a helpful assistant.",
capabilities=[MemorySyncCapability(user_id="customer-7")],
)
result = await agent.run("What should I cook tonight?")
# The model saw:
# You are a helpful assistant.
#
# Relevant memories about this user from previous conversations:
# - human: I'm vegetarian and I fly aisle.
#
# ...and this exchange is now remembered for every future run.

Recall rides the instructions channel (ModelRequest.instructions) — visible to the model, never a conversation part. Replaying result.all_messages() into the next run can never stack stale memory blocks into the transcript, the classic failure mode of message-mutation integrations. Tool calls, retry prompts and thinking parts never persist; streaming runs persist the final streamed text. Turns are grouped by the run’s conversation_id, which the framework carries across message_history continuations — pass session_id="..." to pin the grouping yourself. persist=False gives read-only memory.

Identity from your deps

from dataclasses import dataclass
from pydantic_ai import Agent
from pydantic_ai_memorysync import MemorySyncCapability
@dataclass
class MyDeps:
user_id: str
agent = Agent(
"openai:gpt-5",
deps_type=MyDeps,
capabilities=[MemorySyncCapability()], # reads ctx.deps.user_id
)
await agent.run("hi", deps=MyDeps(user_id="customer-7"))
# Or resolve it yourself, from anything on your deps:
MemorySyncCapability(user_id_resolver=lambda ctx: ctx.deps.user_id)

Resolution order: user_id_resolver(ctx) when provided, then the static user_id, then a user_id attribute on ctx.deps. Without an identity the run proceeds memoryless and the miss is reported through on_error — guessing a shared namespace would silently mix users’ memories, the one unforgivable failure for a memory layer.

Agent memory tools

from pydantic_ai import Agent
from pydantic_ai_memorysync import create_memorysync_tools
agent = Agent(
"openai:gpt-5",
instructions="Use the memory tools to remember durable facts.",
tools=create_memorysync_tools(user_id="customer-7"),
)
await agent.run("Remember that I prefer aisle seats.")
# 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, LlamaIndex and Google ADK tool sets — an agent moved between frameworks keeps behaving the same way. In Pydantic AI a tool exception fails the whole run, so these tools return short readable strings instead — a memory lookup is never worth a dead run.

from pydantic_ai import Agent
from pydantic_ai_memorysync import MemoryResult, create_memory_search_tool
agent = Agent(
"openai:gpt-5",
tools=[create_memory_search_tool(user_id="customer-7")],
)
# The tool returns list[MemoryResult] — validated Pydantic models:
# MemoryResult(id="m_123", text="human: I'm vegetarian", score=0.62)
# For agents whose downstream code consumes tool results structurally.

Standalone helpers

from pydantic_ai_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 capability's degrading planes.
await save_turn(
user_id="customer-7",
user="I'm vegetarian",
assistant="Noted!",
session_id="thread-42",
)

Public API

ExportKindNotes
MemorySyncCapabilityAbstractCapabilityuser_id, user_id_resolver, k, template, persist, session_id, on_error — all optional; identity falls back to ctx.deps.user_id.
create_memorysync_toolsTool factoryFive plain async callables; read_only=True returns search + list only.
create_memory_search_toolTool factoryReturns typed list[MemoryResult]; failures return [] and report through on_error.
MemoryResultPydantic modelid, text, score — the typed search hit.
get_memory_context / search_memories / save_turnAsync helperssave_turn raises on failure; all share the capability’s seeds.
MemorySyncAPIErrorExceptionCarries the HTTP status and server detail.

Supported versions

PackageRegistryRequiresRuntime
pydantic-ai-memorysync 1.0.0PyPIpydantic-ai (or -slim) >=2 <3Python 3.10+

The test suite drives REAL agents (TestModel/FunctionModel) — instruction-channel injection and its replay safety, one-recall-per-run caching, success-only persistence, streaming, typed tool results — and CI re-runs it against the latest pydantic-ai 2.x release on every push, so a capability-system change upstream fails our pipeline before it can fail your agent.

Where to go next

Was this page helpful?