Compare commits

...

15 Commits

Author SHA1 Message Date
slh
e5347cb946 Merge pull request 'Document the Mem0 production runbook' (#11) from docs/mem0-runbook into main
Reviewed-on: #11
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 23:38:47 +08:00
9d3b695f9b document Mem0 production runbook 2026-07-17 15:38:17 +00:00
slh
670db289b1 Merge pull request 'Publish Gitea through the FRP endpoint' (#10) from feat/gitea-frp-public-endpoint into main
Reviewed-on: #10
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 23:26:45 +08:00
1221bb5ead publish Gitea through the FRP endpoint 2026-07-17 15:24:46 +00:00
slh
de0af51d42 Merge pull request 'Drop incompatible encoding format for Voyage embeddings' (#9) from fix/voyage-encoding-format into main
Reviewed-on: #9
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 23:09:59 +08:00
ffff650e3a drop OpenAI encoding format for Voyage embeddings 2026-07-17 10:15:27 +00:00
slh
40eb38441f Merge pull request 'Install Mem0 PostgreSQL runtime library' (#8) from fix/mem0-libpq-runtime into main
Reviewed-on: http://localhost:13000/slh/infra/pulls/8
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 17:32:15 +08:00
1b6616e937 install Mem0 PostgreSQL runtime library 2026-07-17 09:15:51 +00:00
slh
f7be98d2f7 Merge pull request 'Reconcile GitHub Main while retaining Gitea state' (#7) from reconcile/github-main-fa05731 into main
Reviewed-on: http://localhost:13000/slh/infra/pulls/7
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 15:55:59 +08:00
eed1fe2c4e 2026-07-17 07:53:38 +00:00
slh
9af1b171d9 Merge pull request 'Move Python Worker ownership into Infra' (#6) from chore/migrate-python-worker-to-infra into main
Reviewed-on: http://localhost:13000/slh/infra/pulls/6
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 15:02:08 +08:00
1dd4b0adda stage Python Worker under Infra state directory 2026-07-17 07:00:20 +00:00
086d1e89e2 move Python Worker ownership into Infra 2026-07-17 06:34:12 +00:00
slh
7b8b7c469a Merge pull request 'Add authenticated Mem0 memory service' (#5) from feat/infra-mem0 into main
Reviewed-on: http://localhost:13000/slh/infra/pulls/5
Reviewed-by: slh <spenguin725@gmail.com>
2026-07-17 13:50:01 +08:00
fa05731d00 Add Mem0 service to docker-compose for persistent AI memory integration 2026-07-17 12:53:34 +08:00
6 changed files with 202 additions and 18 deletions

4
.gitignore vendored
View File

@ -19,3 +19,7 @@ git-bridge/state/
litellm/.env litellm/.env
mem0/.env mem0/.env
mem0/history/ mem0/history/
python_worker/app/
python_worker/runtime.env
python_worker/.migration-state
python_worker/.staged-from-n8n

View File

@ -46,11 +46,12 @@ services:
GITEA__database__USER: gitea GITEA__database__USER: gitea
GITEA__database__PASSWD__FILE: /run/secrets/gitea_db_password GITEA__database__PASSWD__FILE: /run/secrets/gitea_db_password
GITEA__database__SSL_MODE: disable GITEA__database__SSL_MODE: disable
GITEA__server__DOMAIN: localhost GITEA__server__DOMAIN: gitea.xyslh.xyz
GITEA__server__ROOT_URL: http://localhost:13000/ GITEA__server__ROOT_URL: https://gitea.xyslh.xyz/
GITEA__server__SSH_DOMAIN: localhost GITEA__server__SSH_DOMAIN: gitea.xyslh.xyz
GITEA__server__SSH_PORT: 2223 GITEA__server__SSH_PORT: 2222
GITEA__service__REQUIRE_SIGNIN_VIEW: "true" GITEA__server__PROTOCOL: http
GITEA__service__REQUIRE_SIGNIN_VIEW: "false"
GITEA__service__DISABLE_REGISTRATION: "true" GITEA__service__DISABLE_REGISTRATION: "true"
GITEA__security__INSTALL_LOCK: "true" GITEA__security__INSTALL_LOCK: "true"
depends_on: depends_on:
@ -63,8 +64,8 @@ services:
- /etc/timezone:/etc/timezone:ro - /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
ports: ports:
- "127.0.0.1:13000:3000" - "13000:3000"
- "127.0.0.1:2223:22" - "2223:22"
healthcheck: healthcheck:
test: ["CMD-SHELL", "grep -Eq '^INSTALL_LOCK[[:space:]]*=[[:space:]]*true' /data/gitea/conf/app.ini && grep -Eq '^DB_TYPE[[:space:]]*=[[:space:]]*postgres' /data/gitea/conf/app.ini && wget -q --spider http://127.0.0.1:3000/api/healthz"] test: ["CMD-SHELL", "grep -Eq '^INSTALL_LOCK[[:space:]]*=[[:space:]]*true' /data/gitea/conf/app.ini && grep -Eq '^DB_TYPE[[:space:]]*=[[:space:]]*postgres' /data/gitea/conf/app.ini && wget -q --spider http://127.0.0.1:3000/api/healthz"]
interval: 5s interval: 5s
@ -195,6 +196,38 @@ services:
networks: networks:
- git_forge - git_forge
python_worker:
container_name: python_worker
image: n8n-python_worker
pull_policy: never
restart: unless-stopped
stop_grace_period: 60s
working_dir: /app
env_file:
- ./python_worker/runtime.env
volumes:
- ./python_worker/app:/app
- /volume2/docker/n8n/files:/files
ports:
- "8000:8000"
command:
- /bin/sh
- -lc
- . /app/.venv/bin/activate && exec python -m uvicorn fastapi_app:app --host 0.0.0.0 --port 8000 --log-level debug
dns:
- 223.5.5.5
- 114.114.114.114
healthcheck:
test: ["CMD", "/app/.venv/bin/python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()"]
interval: 10s
timeout: 5s
retries: 18
start_period: 30s
networks:
n8n_backbone:
aliases:
- python_worker
networks: networks:
git_forge: git_forge:
name: git_forge name: git_forge
@ -202,6 +235,10 @@ networks:
external: true external: true
name: n8n_default name: n8n_default
n8n_backbone:
external: true
name: n8n_default
secrets: secrets:
postgres_superuser_password: postgres_superuser_password:
file: ./postgres/secrets/postgres_superuser_password file: ./postgres/secrets/postgres_superuser_password

View File

@ -79,6 +79,7 @@ model_list:
litellm_params: litellm_params:
model: voyage/voyage-4 model: voyage/voyage-4
api_key: os.environ/VOYAGE_API_KEY api_key: os.environ/VOYAGE_API_KEY
additional_drop_params: ["encoding_format"]
litellm_settings: litellm_settings:
aiohttp_trust_env: true aiohttp_trust_env: true

View File

@ -14,6 +14,9 @@ RUN git init \
FROM python:3.12-slim-bookworm FROM python:3.12-slim-bookworm
ARG MEM0AI_VERSION=2.0.12 ARG MEM0AI_VERSION=2.0.12
RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq5 \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 PYTHONUNBUFFERED=1
WORKDIR /app WORKDIR /app

View File

@ -1,14 +1,125 @@
# Mem0 # Mem0 记忆服务
Infra runs the authenticated Mem0 OSS REST API without the optional dashboard, Mem0 是 Infra Compose 管理的长期记忆服务。它只在宿主机
Neo4j, or Qdrant. PostgreSQL/pgvector stores data in isolated `mem0` and `127.0.0.1:8888` 暴露 REST API并启用 API Key 鉴权;不部署 Dashboard、
`mem0_app` databases. Model traffic goes through the existing LiteLLM gateway: Neo4j 或独立 Qdrant。
- memory extraction: `grok-fast` ## 当前架构
- embeddings: `voyage-4`, 1024 dimensions
The service is bound to `127.0.0.1:8888`. The OSS API paths do not use `/v1`. - 容器:`mem0`
Only `POSTGRES_PASSWORD`, `JWT_SECRET`, and `ADMIN_API_KEY` live in the ignored - Compose 真相源:`/volume2/docker/infra/docker-compose.yaml`
`mem0/.env` with mode `0600`. Non-secret runtime settings stay in Compose, and no - API`http://127.0.0.1:8888`
model-provider credential is copied into Mem0. The image source is pinned to the - 关系数据库Infra PostgreSQL 中独立的 `mem0_app` 数据库
upstream Mem0 `v2.0.12` release commit. - 向量数据Infra PostgreSQL 中启用 pgvector 的 `mem0` 数据库
- 记忆抽取模型LiteLLM 的 `grok-fast`
- EmbeddingLiteLLM 的 `voyage-4`,固定 1024 维
- LiteLLM 地址:容器内通过 `http://host.docker.internal:4000/v1` 访问
Mem0 不持有 Grok、Voyage 或 OpenAI 的供应商凭据。模型凭据只归 LiteLLM
管理Mem0 使用内部占位 Key 访问本机网关。
## 宿主机目录
- `mem0/Dockerfile`:固定 Mem0 OSS `v2.0.12` 源码提交并构建服务镜像
- `mem0/.env`:仅保存三个运行 Secret不进入 Git权限必须为 `0600`
- `mem0/history/`:服务的本地历史状态,不进入 Git
- PostgreSQL 数据:由 `postgres` 服务的数据目录统一持久化
`mem0/.env` 只允许以下键:
```text
POSTGRES_PASSWORD
JWT_SECRET
ADMIN_API_KEY
```
禁止将 `.env`、数据库文件、历史目录、模型 Key 或响应内容提交到仓库。
## 兼容性修复
最终运行镜像必须安装 Debian `libpq5``psycopg` 默认可退回纯 Python
实现,但仍需系统 `libpq`;缺失时 Alembic 会在容器启动阶段报
`no pq wrapper available`
Mem0 使用 OpenAI SDK 调用 embedding 时会发送
`encoding_format=\"float\"`,而 Voyage 仅接受 `base64`。因此 LiteLLM 的
`voyage-4` 路由必须配置:
```yaml
additional_drop_params: ["encoding_format"]
```
该规则只作用于 `voyage-4`,不得改成全局丢弃参数,避免影响 Grok、GPT 和
其他模型。
## 部署与完整测试
合并 Gitea PR 并完成 FF-only 同步后,执行:
```bash
cd /volume2/docker
sudo env INFRA_MEM0_DEPLOY=YES \
./ops/deploy-and-test-infra-mem0.sh
```
脚本会:
1. 校验 Secret 文件权限与键集合。
2. 幂等创建或更新 `mem0` 数据库角色以及 `mem0``mem0_app` 数据库。
3. 确认 pgvector 扩展存在。
4. 构建镜像并先验证 `psycopg` 能加载 pq 实现。
5. 仅重建 `mem0` 容器。
6. 配置 `grok-fast``voyage-4` 和 1024 维向量存储。
7. 创建一条临时记忆、检索验证后删除。
成功标志:
```text
memory_create_search_delete_smoke=passed
litellm_restarted=false
other_services_restarted=none
```
## 日常检查
```bash
sudo docker compose \
-f /volume2/docker/infra/docker-compose.yaml ps mem0
sudo docker inspect mem0 \
--format 'status={{.State.Status}} restart={{.RestartCount}} health={{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}'
curl -fsS --max-time 10 \
http://127.0.0.1:8888/openapi.json >/dev/null
```
API 业务请求必须携带 `X-API-Key`。检查时不得把 `ADMIN_API_KEY` 打印到终端、
命令历史或日志。
## 故障排查
模型链路异常时运行只读诊断:
```bash
cd /volume2/docker
sudo ./ops/diagnose-infra-mem0-provider.sh
```
诊断会从 Mem0 容器内分别测试 `grok-fast``voyage-4`,并输出脱敏后的
Mem0/LiteLLM 错误。常见故障:
- `no pq wrapper available`:镜像缺少 `libpq5`,检查 Dockerfile 与实际镜像。
- Voyage 拒绝 `encoding_format=float`:检查 LiteLLM 的单模型
`additional_drop_params` 配置是否已部署。
- HTTP 502 `provider_unavailable`:先直测 LiteLLM 对话与 embedding 路由,
不要直接修改数据库。
- 维度不一致:`voyage-4`、Mem0 embedder 和 pgvector collection 必须全部为
1024 维;不要在已有 collection 上直接改维度。
## 安全与变更边界
- 不直接编辑 PostgreSQL 数据目录。
- 不删除 `mem0``mem0_app` 数据库或 `mem0/history/` 来处理应用错误。
- 不把 Mem0 端口暴露到局域网或公网;跨服务访问应走受控网络或反向代理。
- 模型、维度、数据库结构和鉴权策略变更必须单独提 PR 并完成端到端测试。
- 失败时保留容器日志、镜像和数据库现场,先诊断,再决定回滚方案。

28
python_worker/README.md Normal file
View File

@ -0,0 +1,28 @@
# Python Worker
`python_worker` 是 n8n、OGame Agent 与其他自动化流程共同使用的 FastAPI
服务。本次迁移只调整运行归属和宿主机目录,不重构业务接口。
## 目录边界
- `app/`:从 `/volume2/docker/n8n/python_worker` 分两阶段同步的应用、虚拟
环境和运行状态,不纳入 Git。
- `runtime.env`:从旧容器提取的宿主机运行参数,权限必须为 `0600`,不包含
模型或平台凭据,不纳入 Git。
- `.migration-state`:切换成功后写入的回滚定位信息,不纳入 Git。
- `README.md`:唯一纳入 Gitea 的目录说明。
容器继续使用 `python_worker` 名称、8000 端口和 `n8n_default` 网络,因此现有
n8n 工作流及 OGame Agent 仍通过 `http://python_worker:8000` 访问,不需要修改
调用地址。`/files` 仍是 n8n 与 Worker 共用的数据目录,本阶段不迁移。
## 迁移策略
迁移前先在旧 Worker 在线时把约 921 MB 的目录预复制到
`/volume2/docker/infra/python_worker/app`。正式切换时停止旧容器,再执行一次
增量同步,随后由 Infra Compose 启动新容器。旧目录和停止状态的旧容器都会
保留,以便回滚。
当前沿用本地镜像 `n8n-python_worker` 和已有 `.venv`,这是降低首次迁移风险的
过渡方案。后续必须单独补充固定版本的生产 Dockerfile停止容器启动时联网安装
依赖,再将该服务纳入可复现构建。