Lukas Goldschmidt 4 дней назад
Сommit
2dddbab7cf
4 измененных файлов с 190 добавлено и 0 удалено
  1. 35 0
      .gitignore
  2. 137 0
      mem0server.py
  3. 7 0
      requirements.txt
  4. 11 0
      reset_memory.py

+ 35 - 0
.gitignore

@@ -0,0 +1,35 @@
+# Python
+__pycache__/
+*.py[cod]
+*.pyo
+*.pyd
+.Python
+
+# Virtual environments
+mem0env/
+venv/
+.venv/
+env/
+
+# Environment / secrets
+.env
+.env.*
+*.env
+
+# Chroma local data (if ever run locally)
+chroma_db/
+*.chroma
+
+# Logs
+*.log
+logs/
+
+# OS
+.DS_Store
+Thumbs.db
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo

+ 137 - 0
mem0server.py

@@ -0,0 +1,137 @@
+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]}

+ 7 - 0
requirements.txt

@@ -0,0 +1,7 @@
+fastapi==0.135.1
+Requests==2.32.5
+mem0ai==1.0.5
+chromadb==1.5.4
+groq==1.0.0
+ollama==0.6.1
+httpx

+ 11 - 0
reset_memory.py

@@ -0,0 +1,11 @@
+import sys
+import requests
+
+base = "http://192.168.0.200:8000/api/v1"
+
+name = sys.argv[1]
+
+requests.delete(f"{base}/collections/{name}")
+requests.post(f"{base}/collections", json={"name": name})
+
+print("collection reset:", name)