The Python SDK ships sync and async clients with identical method surfaces. Requires Python 3.9+ and httpx.
Install
pip install mnueron
Quick start
from mnueron import Mnueron
with Mnueron(api_key="mnu_...") as client:
# Save
mem = client.save(
"User prefers concise replies over long explanations",
namespace="my-app",
tags=["preferences"],
)
# Search (BM25, server-side)
for r in client.search("how does the user like responses?",
namespace="my-app", k=5):
print(r.content, r.score)
# Partial update — metadata MERGED, pass {"key": None} to remove
client.update(mem.id,
tags=["preferences", "tone"],
metadata={"confidence": 0.9})
client.delete(mem.id)
Async client
import asyncio
from mnueron import AsyncMnueron
async def main():
async with AsyncMnueron() as client: # picks up MNUERON_API_KEY
await client.save("...", namespace="my-app")
for r in await client.search("...", namespace="my-app"):
print(r.content)
asyncio.run(main())
Date + metadata filters
Date values are epoch milliseconds. metadata_filter is a Python dict
that the server passes to Postgres' @> jsonb operator.
import time
yesterday = int((time.time() - 86400) * 1000)
hits = client.list(
namespace="meetings",
created_after=yesterday,
metadata_filter={"speaker": "sarah"},
)
Bulk search
results = client.bulk_search(
["onboarding flow", "billing edge cases", "JWT setup"],
namespace="work", k=5,
)
for r in results:
print(r.query, "→", len(r.hits))
Webhooks
Register an HTTPS endpoint and verify incoming HMAC-SHA256 signatures:
hook = client.create_webhook(
"https://example.com/mnueron-hook",
events=["memory.saved", "memory.deleted"],
)
print("Record this once — won't appear again:", hook.secret)
# In your webhook handler:
from mnueron import verify_webhook_signature
def handler(request):
sig = request.headers["X-Mnueron-Signature"]
if not verify_webhook_signature(secret, request.body, sig):
return Response(status_code=401)
# ... process payload
Combine with OpenAI / Anthropic
from openai import OpenAI
from mnueron import Mnueron
mem = Mnueron()
llm = OpenAI()
context = mem.search(user_msg, namespace="user-123", k=5)
prompt_context = "\n".join(m.content for m in context)
resp = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"Relevant context:\n{prompt_context}"},
{"role": "user", "content": user_msg},
],
)
# Opt into v0.2.9 fact extraction on the saved memory
mem.save(resp.choices[0].message.content,
namespace="user-123", source="auto",
metadata={"extract_facts": True})
Same shape works for Anthropic, Mistral, Gemini, or any other client.