|
|
14 stundas atpakaļ | |
|---|---|---|
| mem0core | 14 stundas atpakaļ | |
| .gitignore | 4 dienas atpakaļ | |
| Dockerfile | 2 dienas atpakaļ | |
| README.md | 14 stundas atpakaļ | |
| dashboard.html | 1 dienu atpakaļ | |
| docker-compose.yml | 14 stundas atpakaļ | |
| mem0server.py | 14 stundas atpakaļ | |
| requirements.txt | 4 dienas atpakaļ | |
| reset_memory.py | 2 dienas atpakaļ | |
| tests.sh | 2 dienas atpakaļ |
A lightweight FastAPI wrapper around mem0 providing persistent memory over a REST API, with dual-collection storage, metadata passthrough, and local reranking.
| 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 |
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.
| 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. |
| 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
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.ymlservices:
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
DockerfileFROM 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"]
| 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
.envinto the image. Always pass secrets at runtime viaenv_fileor-e.
GET /healthReturns 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 /memoriesAdd 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/searchSearch 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/recentReturn the most recently created memories for a user.
{ "user_id": "alice", "limit": 5 }
DELETE /memoriesDelete 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/searchPOST /knowledge/recentDELETE /knowledgeSame 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".
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.
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")
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 iteminfer: false response body should show the text stored verbatim, not an LLM-rewritten version