Python SDK usage
August 24, 2026 · View on GitHub
Besides the CLI, MindMemOS ships a Python SDK in the same mindmemos-sdk package.
Use it when memory operations live inside a Python agent/app rather than a shell
call. The SDK and CLI hit the same API and share the same local config written by
mindmemos auth.
Setup
pip install mindmemos-sdk # exposes the `mindmemos` CLI too
Credentials resolve in this order: explicit constructor args → the local config
from mindmemos auth → empty (calls then raise AuthRequiredError). A configured
default user_id is inherited by operations that require a user, such as add,
but not by search. Search only uses its per-call user_id; omitting it selects
project-wide search.
Synchronous client
MindMemOSClient is the root entry; client.memory exposes the memory
operations. It is a context manager that releases the underlying HTTP transport
on exit.
from mindmemos_sdk import MindMemOSClient, DialogueMessage
from mindmemos_sdk import MindMemOSSDKError
# No args -> reads base_url / api_key / user_id from `mindmemos auth` config.
# Or override explicitly:
with MindMemOSClient(
# base_url="https://api.mindmemos.example.com",
# api_key="sk-...",
user_id="alice",
app_id="my-agent",
session_id="sess-42",
) as client:
# 1) Store a conversation turn
add_result = client.memory.add(
messages=[
DialogueMessage(role="user", content="I'm allergic to peanuts"),
DialogueMessage(role="assistant", content="Noted — I'll avoid peanuts."),
],
# mode="async", # enqueue and return; memories will be empty
# metadata={"channel": "chat"},
)
print(add_result.code, add_result.request_id)
for item in add_result.memories:
print(item.operation, item.memory_id, item.content)
# 2) Recall before answering
search_result = client.memory.search(
"what are the user's dietary restrictions?",
top_k=5,
user_id="alice",
search_strategy="fast", # or "agentic"
rerank=True,
score_threshold=0.5, # filter out low-relevance rerank results
token_budget=2000, # optional: strict token budget (enables retention); still capped by top_k
)
for hit in search_result.memories:
print(hit.id, hit.memory, hit.last_update_at)
# 3) Inspect / curate
got = client.memory.get(filters={"app_id": "my-agent"}, top_k=20)
client.memory.update("mem_123", "allergic to peanuts and shellfish")
client.memory.delete("mem_123")
# 4) Quality lifecycle
client.memory.feedback(
feedback="lunch rec was wrong; user dislikes spicy food",
messages=[
DialogueMessage(role="user", content="I do not like spicy food."),
DialogueMessage(role="assistant", content="Sorry, I will avoid spicy lunch recommendations."),
],
)
client.memory.dreaming() # background consolidation, no inputs
messages accepts typed objects (DialogueMessage, TextMessage, UrlMessage,
FileMessage) or equivalent dicts. Per-call kwargs (user_id, app_id,
agent_id, session_id) override the client defaults. Search is the exception:
its user_id is per-call only and does not inherit the client default. Omit it
for project-wide search. Request IDs are generated by the server and returned in
SDK results and API errors; callers cannot provide them.
Useful memory kwargs:
add(..., mode="sync"|"async", metadata={...}, skill_context=[...], score=0.8, task_id="...")search(..., search_strategy="fast"|"agentic", rerank=True, score_threshold=0.5, token_budget=2000, filters={...})feedback(feedback="...", mode="sync"|"async", messages=[...], recalled_memories=[...]); explicit feedback text requiresmessages, while omittingfeedbackruns implicit feedback from recent adds.dreaming(mode="async"|"sync")
Error handling — all API/SDK failures raise MindMemOSSDKError (subclasses:
AuthRequiredError, TransportError, ApiError); ApiError carries
request_id:
try:
client.memory.search("notes", user_id="alice")
except MindMemOSSDKError as exc:
print("memory call failed:", exc, getattr(exc, "request_id", None))
Asynchronous client
There is no async root client; build an AsyncHttpTransport and pass it to
AsyncMemoryClient. The transport is an async context manager.
import asyncio
from mindmemos_sdk import AsyncMemoryClient
from mindmemos_sdk.transport import AsyncHttpTransport
async def main():
async with AsyncHttpTransport(
base_url="https://api.mindmemos.example.com",
api_key="sk-...",
) as transport:
memory = AsyncMemoryClient(transport, default_user_id="alice", default_app_id="my-agent")
await memory.add(messages=[{"role": "user", "content": "prefers window seats"}])
result = await memory.search("seating preference", top_k=3, user_id="alice")
for hit in result.memories:
print(hit.id, hit.memory)
await memory.get(filters={"app_id": "my-agent"}, top_k=20)
await memory.update("mem_123", "prefers aisle seats")
await memory.delete("mem_123")
await memory.feedback(
feedback="seat preference was recalled correctly",
messages=[{"role": "user", "content": "Please book me a window seat next time."}],
)
await memory.dreaming(mode="async")
asyncio.run(main())
Result shapes
add→AddResult(code, request_id, memories=[{operation, memory_id, content}]). Inasyncmodememoriesis empty andcodeis"queued".search/get→SearchResult/GetResultwithmemories=[{id, memory, last_update_at, ...}].update/delete/feedback/dreaming→StatusResult(code, message, request_id).
Skill version commands
SDK-managed skills are controlled through the CLI:
mindmemos skill register ./skills/demo/SKILL.md --alias demo
mindmemos skill pull <skill_id_or_alias>
mindmemos skill update <skill_id_or_alias> --yes
mindmemos skill rollback <skill_id_or_alias> --to <version_id> --yes
mindmemos skill diff <skill_id_or_alias> --from <version_id> --to <version_id>
register accepts either a skill directory or its SKILL.md path; the SDK stores the parent directory as the managed path. register --alias stores a local alias in the SDK registry. All single-skill commands accept either the generated skill_id or that alias. update checks the cloud published head and applies the same checkout/backup flow as rollback. rollback downloads missing target content into the local cache, prints a replacement plan with file, hash, and backup path details, then applies only after confirmation unless --yes is set. diff is read-only: it compares cached or downloaded version content and never changes the working skill directory.
memory add also accepts explicit skill traces:
mindmemos memory add --messages-json-file turn.json \
--skill-context-json '[{"name":"demo","content_hash":"...","base_version_id":"v1","usage":"injected"}]'
When --skill-context-json is omitted, the sync SDK/CLI has a best-effort fallback for OpenClaw-style [tool_call] read|write|edit({"path":".../SKILL.md"}) messages in the same add payload: it computes the canonical bundle hash, ensures registered local skills when possible, and sends skill_context with the add request. Host integrations such as the OpenClaw plugin can still run their own skill detection and pass --skill-context-json explicitly.