from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from mem0 import Memory app = FastAPI() config = { "llm": { "provider": "groq", "config": { "model": "llama-3.1-8b-instant", "temperature": 0.1, "max_tokens": 1500 } }, "vector_store": { "provider": "chroma", "config": { "host": "192.168.0.200", "port": 8001, "collection_name": "openclaw_mem" } }, "embedder": { "provider": "ollama", "config": { "model": "nomic-embed-text", "ollama_base_url": "http://192.168.0.200:11434" } } } memory = Memory.from_config(config) # Patch Chroma empty-filter crash (mem0 sometimes calls search with {} filters) orig_search = memory.vector_store.search def is_effectively_empty(filters): if not filters: return True if filters == {"AND": []} or filters == {"OR": []}: return True return False NOOP_WHERE = {"$and": [ {"user_id": {"$ne": ""}}, {"user_id": {"$ne": ""}} ]} def safe_search(query, vectors, limit=10, filters=None): if is_effectively_empty(filters): return memory.vector_store.collection.query( query_embeddings=vectors, n_results=limit, where=NOOP_WHERE ) try: return orig_search(query=query, vectors=vectors, limit=limit, filters=filters) except Exception as e: if "Expected where" in str(e): return memory.vector_store.collection.query( query_embeddings=vectors, n_results=limit, where=NOOP_WHERE ) raise memory.vector_store.search = safe_search @app.post("/memories") async def add_memory(req: Request): data = await req.json() text = data.get("text") user_id = data.get("userId") or data.get("user_id") or "default" if not text: return JSONResponse({"error": "Empty 'text' field"}, status_code=400) result = memory.add(text, user_id=user_id) print("add_memory:", {"user_id": user_id, "text": text[:80], "result": result}) return result @app.post("/memories/search") async def search(req: Request): data = await req.json() query = (data.get("query") or "").strip() user_id = data.get("userId") or data.get("user_id") or "default" if not query: return {"results": []} try: result = memory.search(query, user_id=user_id) except Exception: # fallback: get_all + simple text filter all_res = memory.get_all(user_id=user_id) if isinstance(all_res, dict): items = all_res.get("results", []) elif isinstance(all_res, list): items = all_res else: items = [] q = query.lower() items = [r for r in items if q in (r.get("memory", "").lower())] result = {"results": items} print("search:", {"user_id": user_id, "query": query, "count": len(result.get("results", []))}) limit = int(data.get("limit", 5)) items = result.get("results", []) items = sorted(items, key=lambda r: r.get("score", float("inf")))[:limit] result = {"results": items} print("search:", {"user_id": user_id, "query": query, "count": len(result.get("results", []))}) return result @app.delete("/memories") async def delete(req: Request): data = await req.json() return memory.delete(data.get("filter", {})) @app.post("/memories/recent") async def recent(req: Request): data = await req.json() user_id = data.get("userId") or data.get("user_id") or "default" if not user_id: return JSONResponse({"error":"Missing userId"}, status_code=400) print("recent payload:", data, "user_id:", user_id) limit = int(data.get("limit", 5)) try: results = memory.get_all(user_id=user_id) except Exception: results = memory.search(query="*", user_id=user_id) items = results.get("results", []) items = sorted(items, key=lambda r: r.get("created_at", ""), reverse=True) return {"results": items[:limit]}