Implements the subset of the hosted mem0 platform API that mem0ai==2.0.2
MemoryClient calls, so MemoryClient(host=..., api_key=...) works against this
server. Verified end-to-end (construct/add/search/get_all/get/history/update/delete).
- platform_compat.py: GET /v1/ping/ (returns non-empty org_id/project_id, which
the SDK's Project init requires), POST /v3/memories/{add,search}/,
POST /v3/memories/ (paginated get_all), /v1/memories/{id}/ item ops, and
GET /v1/entities/ -- all mapped onto the existing mem0_manager.
- auth.get_current_user_platform: accepts Authorization: Token (mem0 SDK),
Bearer, or X-API-Key.
- main.py: include the platform router; remove the /v1/memories* aliases added
in ea07a82 (the SDK uses /v3 and trailing-slash /v1/memories/{id}/, not those
paths); keep /v1/chat/completions and the native /memories* routes.
- docker-compose: run uvicorn with --proxy-headers --forwarded-allow-ips=* so the
proxy's https scheme is honoured. This stops trailing-slash 307 redirects from
downgrading https->http and dropping the Authorization header -- the actual
cause of the reported "POST auth broken" symptom (auth was never broken).
- test_sdk_compat.py: end-to-end MemoryClient round-trip against the server.
102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
"""End-to-end compatibility test: drive the real mem0ai MemoryClient against
|
|
this server. Run INSIDE the backend container (which has mem0ai==2.0.2):
|
|
|
|
ssh beast 'cd ~/aistuff/mem0 && docker compose exec -T backend python' < test_sdk_compat.py
|
|
|
|
Requires MEM0_API_KEY in the environment (mapped to user 'pratik'). Exercises the
|
|
full SDK surface and cleans up the memories it creates (scoped to a throwaway
|
|
agent_id so it never touches real memories).
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
from mem0 import MemoryClient
|
|
|
|
KEY = os.environ.get("MEM0_API_KEY")
|
|
if not KEY:
|
|
sys.exit("set MEM0_API_KEY (mapped to user 'pratik')")
|
|
|
|
HOST = os.environ.get("MEM0_HOST", "https://memory.pratikn.com")
|
|
USER = os.environ.get("MEM0_TEST_USER", "pratik")
|
|
AGENT = "sdk_compat_test" # isolates test data from real memories
|
|
|
|
results = []
|
|
|
|
|
|
def check(name, cond, info=""):
|
|
results.append(bool(cond))
|
|
print(("PASS " if cond else "FAIL "), name, "--", str(info)[:240])
|
|
|
|
|
|
# --- construct (hits GET /v1/ping/, validates Token auth + org/project) ---
|
|
try:
|
|
c = MemoryClient(host=HOST, api_key=KEY)
|
|
check("construct", True, f"user_email={c.user_email} org={c.org_id} proj={c.project_id}")
|
|
except Exception as e:
|
|
check("construct", False, repr(e))
|
|
print("\nRESULT: RED (cannot construct client)")
|
|
sys.exit(1)
|
|
|
|
# --- add (POST /v3/memories/add/) ---
|
|
probe = f"SDK compat probe {int(time.time())}: Pratik is validating the mem0 SDK compatibility layer and uses FastAPI."
|
|
try:
|
|
r = c.add(probe, user_id=USER, agent_id=AGENT)
|
|
check("add", isinstance(r, dict), r)
|
|
except Exception as e:
|
|
check("add", False, repr(e))
|
|
|
|
time.sleep(3) # allow async extraction/indexing to settle
|
|
|
|
# --- search (POST /v3/memories/search/) ---
|
|
try:
|
|
s = c.search("mem0 SDK compatibility layer", filters={"user_id": USER, "agent_id": AGENT})
|
|
check("search.shape", isinstance(s, dict) and "results" in s, s)
|
|
except Exception as e:
|
|
check("search.shape", False, repr(e))
|
|
|
|
# --- get_all (POST /v3/memories/) ---
|
|
ids = []
|
|
try:
|
|
g = c.get_all(filters={"user_id": USER, "agent_id": AGENT})
|
|
ok = isinstance(g, dict) and "results" in g and "count" in g
|
|
check("get_all.shape", ok, g)
|
|
ids = [m.get("id") for m in (g.get("results") or []) if m.get("id")]
|
|
except Exception as e:
|
|
check("get_all.shape", False, repr(e))
|
|
|
|
mid = ids[0] if ids else None
|
|
print(f" (created {len(ids)} memory id(s) under agent={AGENT})")
|
|
|
|
# --- item ops (best-effort; depend on extraction producing >=1 fact) ---
|
|
if mid:
|
|
try:
|
|
one = c.get(mid)
|
|
check("get", isinstance(one, dict) and one.get("id") == mid, one)
|
|
except Exception as e:
|
|
check("get", False, repr(e))
|
|
try:
|
|
h = c.history(mid)
|
|
check("history.is_list", isinstance(h, list), h)
|
|
except Exception as e:
|
|
check("history.is_list", False, repr(e))
|
|
try:
|
|
u = c.update(mid, text="SDK compat probe (updated)")
|
|
check("update", isinstance(u, dict), u)
|
|
except Exception as e:
|
|
check("update", False, repr(e))
|
|
|
|
# --- cleanup: delete only the ids we created ---
|
|
deleted = 0
|
|
for i in ids:
|
|
try:
|
|
c.delete(i)
|
|
deleted += 1
|
|
except Exception as e:
|
|
print(" delete error", i, repr(e))
|
|
print(f" cleanup: deleted {deleted}/{len(ids)}")
|
|
|
|
green = results and all(results)
|
|
print("\nRESULT:", "GREEN" if green else "RED", f"({sum(results)}/{len(results)} checks passed)")
|
|
sys.exit(0 if green else 1)
|