Keine Beschreibung

Lukas Goldschmidt de41911699 /docs# vor 13 Stunden
mem0core de41911699 /docs# vor 13 Stunden
.gitignore 2dddbab7cf initial commit vor 4 Tagen
Dockerfile 3cbca7e125 improvements, separate knowledge base, tests vor 2 Tagen
README.md 2e1a19a386 refactored vor 14 Stunden
dashboard.html b9c43fda47 bugfixes vor 1 Tag
docker-compose.yml 2e1a19a386 refactored vor 14 Stunden
mem0server.py 2e1a19a386 refactored vor 14 Stunden
requirements.txt 2dddbab7cf initial commit vor 4 Tagen
reset_memory.py 5c27ac6471 dashboard, bugfixes vor 2 Tagen
tests.sh 3cbca7e125 improvements, separate knowledge base, tests vor 2 Tagen

README.md

mem0server

A lightweight FastAPI wrapper around mem0 providing persistent memory over a REST API, with dual-collection storage, metadata passthrough, and local reranking.

Architecture

Component Provider Address
LLM Groq (meta-llama/llama-4-scout-17b-16e-instruct) cloud
Vector store Chroma 192.168.0.200:8001
Embedder Ollama (nomic-embed-text) 192.168.0.200:11434
Reranker local REST server 192.168.0.200:5200

Collections

The server maintains two independent Chroma collections with separate extraction prompts:

Collection Chroma name Endpoint prefix Used by Extraction style
Conversational openclaw_mem /memories OpenClaw agent User-centric facts ("User prefers…")
Knowledge knowledge_mem /knowledge book-ingestor Objective, encyclopedic facts

Prompts for both collections are defined in mem0core/prompts.py, so you can re-tune the extraction/update wording without touching the routing layer.

Code layout

Module Role
mem0server.py backward-compatible entrypoint uvicorn mem0server:app that delegates to mem0core.create_app().
mem0core/app.py FastAPI factory that wires memory instances and mounts the router built in mem0core/routes.py.
mem0core/prompts.py, mem0core/config.py, mem0core/memory_factory.py, mem0core/storage.py, mem0core/reranker.py Shared config/prompt helpers, mem0 instance construction, storage patches, and reranking logic—good places to change when you swap DBs or vector stores.
mem0core/handlers.py, mem0core/responses.py Shared helper functions (metadata sanitization, add/search/recent shared flows, SafeJSONResponse).
mem0core/routes.py All endpoints plus docstring metadata so /docs presents concise summaries.

Environment variables

Variable Required Default Description
GROQ_API_KEY ✅ yes Groq API key
RERANKER_URL no http://192.168.0.200:5200/rerank Local reranker endpoint

Create a .env file (never commit it):

GROQ_API_KEY=your_key_here
RERANKER_URL=http://192.168.0.200:5200/rerank

Docker (recommended)

The recommended setup mounts mem0server.py and the new mem0core/ package so uvicorn reloads on local edits without rebuilding the image. Only touch docker compose build when requirements.txt changes.

docker-compose.yml

services:
  mem0server:
    build: .
    image: mem0server:latest
    container_name: mem0server
    ports:
      - "8420:8420"
    volumes:
      - ./mem0server.py:/app/mem0server.py:ro
      - ./mem0core:/app/mem0core:ro
      - ./dashboard.html:/app/dashboard.html:ro
    env_file:
      - .env
    restart: unless-stopped

Dockerfile

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# mem0server.py is mounted at runtime via docker-compose volume
EXPOSE 8420
CMD ["uvicorn", "mem0server:app", "--host", "0.0.0.0", "--port", "8420", "--reload"]

Workflow

Situation Command
First time / deps changed docker compose up --build
Code edit Just save — uvicorn reloads automatically
Restart container docker compose restart
View logs docker compose logs -f

⚠️ Never bake .env into the image. Always pass secrets at runtime via env_file or -e.


API

GET /health

Returns server status, active collection names, and a preview of each extraction prompt.

{
  "status": "ok",
  "reranker_url": "http://192.168.0.200:5200/rerank",
  "collections": {
    "conversational": "openclaw_mem",
    "knowledge": "knowledge_mem"
  },
  "prompts": {
    "conversational": "You are a personal memory assistant…",
    "knowledge": "You are a knowledge extraction assistant…"
  }
}

/memories — conversational collection (OpenClaw)

POST /memories

Add a memory. Accepts plain text or a messages array.

{ "text": "I prefer Python over JavaScript.", "user_id": "alice" }
{
  "messages": [{"role": "user", "content": "I've used Vim for 10 years."}],
  "user_id": "alice"
}

POST /memories/search

Search with reranking. Fetches limit × 3 candidates, reranks locally, returns top limit.

{ "query": "editor preferences", "user_id": "alice", "limit": 5 }

Results include a rerank_score field.

POST /memories/recent

Return the most recently created memories for a user.

{ "user_id": "alice", "limit": 5 }

DELETE /memories

Delete memories by filter.

{ "filter": { "user_id": "alice" } }

/knowledge — knowledge collection (book-ingestor)

Same shape as /memories with two additions:

metadata — arbitrary key/value dict stored alongside the memory. Useful for provenance tagging:

{
  "text": "Silvio Gesell proposed demurrage to discourage currency hoarding.",
  "user_id": "knowledge_base",
  "metadata": { "source_file": "gesell_neo.pdf", "chapter": 3, "page": 47 }
}

infer: false — bypasses LLM extraction entirely and stores the text verbatim. Use for pre-summarised chunks where you want exact content preserved:

{
  "text": "MIDI SysEx messages use a 7-bit checksum.",
  "user_id": "knowledge_base",
  "infer": false,
  "metadata": { "source_file": "midi_spec.pdf", "chapter": 9, "page": 112 }
}

POST /knowledge/search

POST /knowledge/recent

DELETE /knowledge

Same request/response shape as their /memories counterparts.


POST /search — merged search (both collections)

Queries both collections simultaneously, tags each result with _source, then runs a single rerank pass over the merged pool. Intended for OpenClaw's autorecall webhook when it should draw on both conversational memory and ingested knowledge.

{ "query": "Gesell economic theory", "user_id": "knowledge_base", "limit": 8 }

Each result includes "_source": "conversational" or "_source": "knowledge".


Reranker contract

The server expects a reranker at RERANKER_URL accepting:

{
  "query": "...",
  "documents": ["doc1", "doc2"],
  "top_k": 5
}

And returning:

{
  "results": [
    { "text": "doc1", "score": 0.99 },
    { "text": "doc2", "score": 0.87 }
  ]
}

If the reranker is unreachable the server falls back gracefully to raw mem0 results — no crash, no error returned to the caller.


Resetting a collection

reset_memory.py deletes and recreates a collection directly via the Chroma HTTP API, then restarts the mem0server container so it re-acquires the fresh collection object:

python reset_memory.py openclaw_mem   # wipe conversational
python reset_memory.py knowledge_mem  # wipe knowledge
# reset_memory.py
import sys, subprocess, requests

base = "http://192.168.0.200:8001/api/v1"
name = sys.argv[1]
requests.delete(f"{base}/collections/{name}")
requests.post(f"{base}/collections", json={"name": name})
print(f"collection reset: {name}")
subprocess.run(["docker", "compose", "restart", "mem0server"])
print("mem0server restarted")

Testing

tests.sh exercises every endpoint. Run it after any server change:

bash tests.sh

Key assertions to eyeball manually:

  • /knowledge/search query "Gesell demurrage" → Gesell result ranked #1, MIDI result near the bottom
  • /knowledge/search query "free money currency hoarding" → Gesell rerank_score should be > 0.9, MIDI < 0.001
  • /search (merged) results should include _source field on every item
  • infer: false response body should show the text stored verbatim, not an LLM-rewritten version