The SurrealDB Agent Memory client ships as its own distribution, surrealdb-memory, pulled in through the surrealdb[memory] extra. It is versioned independently of the database driver and imported as surrealdb.memory.
Package naming: On PyPI, the client is surrealdb-memory, installed through the surrealdb[memory] extra rather than by name. The SDK lands in the 3.x line, which is still prerelease, so the install needs --pre - a bare pip install surrealdb resolves to the 2.x stable release, which has no memory extra.
Installation
pip install --pre 'surrealdb[memory]'
# Using uv
uv add --prerelease=allow 'surrealdb[memory]'Python 3.10+ recommended.
Clients
Memory is synchronous (uses requests). AsyncMemory is async (uses aiohttp). Both expose the same method names; add await on the async client.
from surrealdb.memory import Memory, AsyncMemory
with Memory(
context="acme-prod",
endpoint="https://api.memory.example",
api_key="sk-spec-...",
) as memory:
memory.remember("Alice was promoted to CTO.")
hits = memory.recall("What is Alice's role?", k=10)
for hit in hits.hits:
print(hit.score, hit.text)
async with AsyncMemory(
context="acme-prod",
endpoint="https://api.memory.example",
api_key="sk-spec-...",
) as memory:
await memory.remember("Alice was promoted to CTO.")
hits = await memory.recall("What is Alice's role?", k=10)Both clients are pinned to one context and call /api/v1/{context}/…. Pass context, endpoint, and api_key explicitly; the SDK does not read environment variables.
Scope
On the wire, scope is a ScopeSet: an ordered array of slash-path strings (for example ["org/acme/user/alice"]). Register paths with agent-memory scopes create before first use; see Contexts and scope.
The Python client accepts:
A single path:
scope="org/acme/user/alice"A list of paths:
scope=["org/acme/user/alice"]
| Argument | Default | Description |
|---|---|---|
context | required | Context id, e.g. "acme-prod". |
endpoint | required | SurrealDB Agent Memory host URL, e.g. "https://api.memory.example". |
api_key | required | Bearer token (Authorization: Bearer …). |
timeout | 30.0 | Per-request timeout in seconds. |
max_retries | 3 | Retries for GETs and idempotent writes. |
Remember (facts)
memory.remember("Alice was promoted to CTO.", infer="full", scopes=["org/acme/user/alice"])
memory.remember("Q3 board notes", labels=["topic=board"], memory_category="context")
memory.remember_many(
[
{"role": "user", "content": "I was promoted to CTO."},
{"role": "assistant", "content": "Congratulations!"},
],
extract="whole_conversation",
scopes=["org/acme/user/alice"],
)remember and remember_many send an Idempotency-Key header (derived from method, path, body, and a 30-second bucket) so safe retries collapse server-side.
Recall and context
result = memory.recall(
"What is Alice's role?",
k=10,
mode="hybrid",
lens=["org/acme/user/alice"],
)
block = memory.query_context(
"What is Alice's role?",
k=10,
lens=["org/acme/user/alice"],
)Optional filters include labels, lens, scope_view (strict | merged | crossTeam), temporal bounds (as_of, valid_from, …), and geo location.
Documents
upload = memory.documents.upload(
"policy.pdf",
content_type="application/pdf",
title="Returns policy",
scope=["org/acme/team/eng"],
labels=["team=eng"],
)
doc = memory.documents.get(upload.id)
hits = memory.documents.query("refund window", k=5, mode="hybrid")
chunks = memory.documents.chunks(upload.id)
memory.documents.keywords.search("returns policy", k=10)Chat (including streaming)
reply = memory.chat("Summarise what you know about Alice", scopes=["user/alice"])
print(reply.reply)
for chunk in memory.chat("Summarise what you know about Alice", stream=True):
if chunk.delta:
print(chunk.delta, end="", flush=True)
if chunk.done:
print("\n[trace]", chunk.trace_id)Other verbs and namespaces
Top-level methods: consolidate, reflect, elaborate, forget, state, whoami, profile, inspect, audit, health.
Grouped resources: memory.documents, memory.sessions, memory.entities, memory.scopes, memory.principals, memory.keys, memory.traces, memory.lifecycle.
→ Full method tables: Python SDK reference
Errors and retries
The SDK raises typed exceptions so you can handle auth, scope, and not-found cases precisely.
from surrealdb.memory import MemoryAPIError, MemoryAuthError, MemoryNotFoundError, MemoryScopeError
try:
hits = memory.recall("what is my name?", lens=["user/alice"])
except MemoryAuthError as exc:
print(exc.status_code, exc.message)
except MemoryScopeError as exc:
print(exc.status_code, exc.message)
except MemoryNotFoundError as exc:
print(exc.status_code, exc.message)
except MemoryAPIError as exc:
print(exc.status_code, exc.message, exc.trace_id, exc.body)| Exception | HTTP | When it occurs |
|---|---|---|
MemoryServiceError | n/a | Base class |
MemoryAPIError | Other non-2xx | Generic API failure; carries status_code, message, trace_id, body |
MemoryAuthError | 401 | Missing or invalid API key |
MemoryScopeError | 403 | Scope floor or principal rejects the call |
MemoryNotFoundError | 404 | Context, session, document, or other resource not found |
Responses of 400, 422, 429, and 5xx that are not mapped to a subclass surface as MemoryAPIError. Inspect status_code and body (RFC 7807 problem details) for validation and rate-limit information. The async client raises the same exceptions.
GET requests and idempotent writes (remember, remember_many) retry automatically on connection errors and 5xx responses: up to max_retries attempts (default 3) with 250 ms, 500 ms, 1000 ms backoff. Other writes and 4xx responses are not retried. Tune or disable on the constructor:
memory = Memory(..., max_retries=0, timeout=10.0)The default timeout is 30 seconds; streaming chat disables the read timeout while tokens arrive. On a 429, read the problem-detail body and back off before retrying manually. All errors follow RFC 7807 Problem Details.
Response types
Typed dataclasses include RememberResponse, RecallResponse, RecallHit, ChatResponse, Document, Chunk, StateResponse, and others. Import from surrealdb.memory when you need explicit types:
from surrealdb.memory import RecallResponse, RecallHitHarness adapters (zero prompt change)
For agent frameworks that should auto-record every turn, SurrealDB Agent Memory ships Python adapters that build on this SDK:
pip install agent-memory-crew-ai # CrewAI
pip install agent-memory-openai-agents-sdk # OpenAI Agents SDK
pip install agent-memory-strands-agents # Strands Agents
pip install agent-memory-google-adk # Google ADKCLI alternative
The spectron binary exposes the same operations without the SDK:
agent-memory remember "Alice was promoted to CTO."
agent-memory recall "What is Alice's role?" --json