neutralize upstream service branding
This commit is contained in:
90
README.md
90
README.md
@ -1,18 +1,18 @@
|
||||
# Memory Gateway
|
||||
|
||||
Memory Gateway 是一个轻量级 FastAPI 服务,用于在 EverOS 现有
|
||||
Memory Gateway 是一个轻量级 FastAPI 服务,用于在上游记忆服务现有
|
||||
`/api/v1/memory/add`、`/api/v1/memory/flush`、`/api/v1/memory/search`
|
||||
能力之上构建用户资源记忆层。
|
||||
|
||||
它只维护 Gateway 自己的 SQLite 元数据表、软删除记录和手动覆盖记录,
|
||||
不会直接修改 EverOS 的 Markdown、SQLite 或 LanceDB 内部文件。
|
||||
不会直接修改上游记忆服务的 Markdown、SQLite 或 LanceDB 内部文件。
|
||||
|
||||
## 功能范围
|
||||
|
||||
- 上传用户资源:文件、图片、音频、PDF、HTML、普通文档、纯文本。
|
||||
- 保存资源元数据到 SQLite。
|
||||
- 为每个资源生成独立 EverOS `session_id`。
|
||||
- 调用 EverOS `add` 和 `flush` 完成资源记忆摄入,并对临时失败做轻量重试。
|
||||
- 为每个资源生成独立的上游记忆服务 `session_id`。
|
||||
- 调用上游记忆服务的 `add` 和 `flush` 完成资源记忆摄入,并对临时失败做轻量重试。
|
||||
- 提供资源列表、详情、软删除。
|
||||
- 支持上传大小限制、MIME 白名单、同用户同 app/project 下按 sha256 幂等复用资源。
|
||||
- 编排记忆搜索,支持当前聊天、资源记忆、全部用户记忆。
|
||||
@ -28,7 +28,7 @@ Memory Gateway 是一个轻量级 FastAPI 服务,用于在 EverOS 现有
|
||||
│ ├── api.py # FastAPI 路由
|
||||
│ ├── config.py # 环境变量配置
|
||||
│ ├── db.py # SQLite schema 初始化
|
||||
│ ├── everos_client.py # EverOS HTTP client
|
||||
│ ├── backend_client.py # 上游记忆服务 HTTP client
|
||||
│ ├── repository.py # SQLite 读写
|
||||
│ └── service.py # 业务编排
|
||||
├── main.py # Python 启动入口
|
||||
@ -50,21 +50,21 @@ cp .env.example .env
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
|---|---|---|
|
||||
| `EVEROS_BASE_URL` | `http://127.0.0.1:1995` | EverOS API 服务地址;EverOS 可监听 `0.0.0.0:1995`,本机客户端通常连接 `127.0.0.1:1995` |
|
||||
| `MEMORY_GATEWAY_BACKEND_BASE_URL` | `http://127.0.0.1:1995` | 上游记忆服务 API 地址;服务端可监听 `0.0.0.0:1995`,本机客户端通常连接 `127.0.0.1:1995` |
|
||||
| `MEMORY_GATEWAY_DB_PATH` | `./data/memory_gateway.sqlite3` | Gateway 自己的 SQLite 数据库路径 |
|
||||
| `MEMORY_GATEWAY_STORAGE_DIR` | `./data/storage` | 用户上传原始文件保存路径 |
|
||||
| `MEMORY_GATEWAY_RESOURCE_SEARCH_BATCH_SIZE` | `50` | resources scope 搜索时每批 session_id 数量 |
|
||||
| `MEMORY_GATEWAY_MAX_UPLOAD_BYTES` | `26214400` | 单个上传文件最大字节数,默认 25MB |
|
||||
| `MEMORY_GATEWAY_ALLOWED_MIME_TYPES` | 常见图片、音频、PDF、HTML、文本和 Office 文档 | 逗号分隔的上传 MIME 白名单,支持 `image/*` 这类前缀匹配 |
|
||||
| `MEMORY_GATEWAY_EVEROS_INGEST_ATTEMPTS` | `3` | EverOS `add` 和 `flush` 各自最多重试次数 |
|
||||
| `MEMORY_GATEWAY_EVEROS_RETRY_DELAY_SECONDS` | `0.25` | EverOS 摄入重试间隔秒数 |
|
||||
| `MEMORY_GATEWAY_EVEROS_TIMEOUT_SECONDS` | `120` | 单次 EverOS HTTP 请求超时秒数 |
|
||||
| `MEMORY_GATEWAY_BACKEND_INGEST_ATTEMPTS` | `3` | 上游记忆服务 `add` 和 `flush` 各自最多重试次数 |
|
||||
| `MEMORY_GATEWAY_BACKEND_RETRY_DELAY_SECONDS` | `0.25` | 上游记忆服务摄入重试间隔秒数 |
|
||||
| `MEMORY_GATEWAY_BACKEND_TIMEOUT_SECONDS` | `120` | 单次上游记忆服务 HTTP 请求超时秒数 |
|
||||
| `MEMORY_GATEWAY_HOST` | `127.0.0.1` | Gateway API 监听地址 |
|
||||
| `MEMORY_GATEWAY_PORT` | `8010` | Gateway API 监听端口 |
|
||||
| `MEMORY_GATEWAY_RELOAD` | `false` | 是否启用 uvicorn reload,开发时可设为 `true` |
|
||||
|
||||
注意:`MEMORY_GATEWAY_DB_PATH` 和 `MEMORY_GATEWAY_STORAGE_DIR` 是 Gateway
|
||||
自己的存储位置,不要配置成 EverOS 的内部存储目录。
|
||||
自己的存储位置,不要配置成上游记忆服务的内部存储目录。
|
||||
|
||||
## 安装依赖
|
||||
|
||||
@ -133,7 +133,7 @@ Gateway 会通过 `memory_gateway.api` logger 为每个 API 请求输出一条 J
|
||||
GET /health
|
||||
```
|
||||
|
||||
该接口不需要 `user_id` 或 `user_key`,用于确认 Gateway API 是否可响应,以及上游 EverOS 是否可访问。
|
||||
该接口不需要 `user_id` 或 `user_key`,用于确认 Gateway API 是否可响应,以及上游记忆服务是否可访问。
|
||||
|
||||
请求示例:
|
||||
|
||||
@ -141,7 +141,7 @@ GET /health
|
||||
curl http://127.0.0.1:8010/health
|
||||
```
|
||||
|
||||
EverOS 正常时响应示例:
|
||||
上游记忆服务正常时响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -149,7 +149,7 @@ EverOS 正常时响应示例:
|
||||
"api": {
|
||||
"status": "ok"
|
||||
},
|
||||
"everos": {
|
||||
"backend": {
|
||||
"status": "ok",
|
||||
"base_url": "http://127.0.0.1:1995",
|
||||
"data": {
|
||||
@ -159,7 +159,7 @@ EverOS 正常时响应示例:
|
||||
}
|
||||
```
|
||||
|
||||
EverOS 不可访问时仍返回 HTTP 200,但 `status` 会变成 `degraded`,便于区分“Gateway API 活着”和“上游 EverOS 故障”:
|
||||
上游记忆服务不可访问时仍返回 HTTP 200,但 `status` 会变成 `degraded`,便于区分“Gateway API 活着”和“上游记忆服务故障”:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -167,7 +167,7 @@ EverOS 不可访问时仍返回 HTTP 200,但 `status` 会变成 `degraded`,
|
||||
"api": {
|
||||
"status": "ok"
|
||||
},
|
||||
"everos": {
|
||||
"backend": {
|
||||
"status": "unavailable",
|
||||
"base_url": "http://127.0.0.1:1995",
|
||||
"error": "Connection refused"
|
||||
@ -221,8 +221,8 @@ Content-Type: multipart/form-data
|
||||
|---|---|---|---|---|
|
||||
| `user_id` | string | 是 | 无 | 用户 ID |
|
||||
| `user_key` | string | 是 | 无 | 用户 key |
|
||||
| `app_id` | string | 否 | `default` | EverOS app scope |
|
||||
| `project_id` | string | 否 | `default` | EverOS project scope |
|
||||
| `app_id` | string | 否 | `default` | 上游记忆服务 app scope |
|
||||
| `project_id` | string | 否 | `default` | 上游记忆服务 project scope |
|
||||
| `file` | file | 是 | 无 | 上传资源文件 |
|
||||
| `title` | string | 否 | `null` | 资源标题 |
|
||||
| `description` | string | 否 | `null` | 资源描述 |
|
||||
@ -234,22 +234,22 @@ Content-Type: multipart/form-data
|
||||
3. 生成 `resource_id`。
|
||||
4. 生成 `session_id = resource:{user_id}:{resource_id}`。
|
||||
5. 写入 `user_resources`,状态为 `ingesting`。
|
||||
6. 根据 MIME 类型映射 EverOS content type。
|
||||
7. 构造 EverOS content item:文本类上传以内联 `text` 发送,非文本上传以内联 `base64` 发送,不要求 EverOS 访问 Gateway 本地 `file://` 路径。
|
||||
8. 调用 EverOS `/api/v1/memory/add`。
|
||||
9. 调用 EverOS `/api/v1/memory/flush`。
|
||||
6. 根据 MIME 类型映射上游记忆服务 content type。
|
||||
7. 构造上游记忆服务 content item:文本类上传以内联 `text` 发送,非文本上传以内联 `base64` 发送,不要求上游记忆服务访问 Gateway 本地 `file://` 路径。
|
||||
8. 调用上游记忆服务的 `/api/v1/memory/add`。
|
||||
9. 调用上游记忆服务的 `/api/v1/memory/flush`。
|
||||
10. 成功后状态改为 `extracted`,失败后状态改为 `failed`。
|
||||
|
||||
上传策略:
|
||||
|
||||
- 文件会按流式方式写入磁盘,超过 `MEMORY_GATEWAY_MAX_UPLOAD_BYTES` 会返回 `413`,不会写入资源记录。
|
||||
- MIME 类型不在 `MEMORY_GATEWAY_ALLOWED_MIME_TYPES` 白名单内会返回 `415`。
|
||||
- 同一用户在同一 `app_id`、`project_id` 下重复上传相同 sha256 的活跃资源,会直接返回已有资源,避免重复调用 EverOS 摄入。
|
||||
- EverOS `add` 和 `flush` 临时失败时会分别按配置重试;单次请求受 `MEMORY_GATEWAY_EVEROS_TIMEOUT_SECONDS` 控制;全部失败后资源状态为 `failed`,并记录 `error_message`。
|
||||
- 同一用户在同一 `app_id`、`project_id` 下重复上传相同 sha256 的活跃资源,会直接返回已有资源,避免重复调用上游记忆服务摄入。
|
||||
- 上游记忆服务的 `add` 和 `flush` 临时失败时会分别按配置重试;单次请求受 `MEMORY_GATEWAY_BACKEND_TIMEOUT_SECONDS` 控制;全部失败后资源状态为 `failed`,并记录 `error_message`。
|
||||
|
||||
content type 映射:
|
||||
|
||||
| 文件类型 | EverOS content type |
|
||||
| 文件类型 | 上游记忆服务 content type |
|
||||
|---|---|
|
||||
| `image/*` | `image` |
|
||||
| `audio/*` | `audio` |
|
||||
@ -406,7 +406,7 @@ DELETE /resources/{resource_id}?user_id={user_id}&user_key={user_key}
|
||||
- 设置 `status = deleted`。
|
||||
- 后续 `resources` scope 搜索会排除该资源的 `session_id`。
|
||||
- 清理 Gateway 自己在 `MEMORY_GATEWAY_STORAGE_DIR` 下保存的原始上传文件。
|
||||
- 不物理删除 EverOS 内部记忆或索引。
|
||||
- 不物理删除上游记忆服务内部记忆或索引。
|
||||
|
||||
请求示例:
|
||||
|
||||
@ -441,9 +441,9 @@ Content-Type: application/json
|
||||
| `query` | string | 是 | 无 | 搜索问题 |
|
||||
| `conversation_id` | string | 否 | `null` | `scope` 包含 `current_chat` 时使用 |
|
||||
| `scope` | string[] | 否 | `["current_chat", "resources"]` | 搜索范围 |
|
||||
| `top_k` | integer | 否 | `8` | 每次 EverOS 搜索返回数量,范围 `1..100` |
|
||||
| `app_id` | string | 否 | `default` | EverOS app scope |
|
||||
| `project_id` | string | 否 | `default` | EverOS project scope |
|
||||
| `top_k` | integer | 否 | `8` | 每次上游记忆服务搜索返回数量,范围 `1..100` |
|
||||
| `app_id` | string | 否 | `default` | 上游记忆服务 app scope |
|
||||
| `project_id` | string | 否 | `default` | 上游记忆服务 project scope |
|
||||
|
||||
`scope` 支持:
|
||||
|
||||
@ -472,9 +472,9 @@ curl -X POST http://127.0.0.1:8010/memories/search \
|
||||
|
||||
搜索编排逻辑:
|
||||
|
||||
1. `current_chat`:调用 EverOS search,过滤 `filters.session_id = chat:{conversation_id}`。
|
||||
2. `resources`:先查当前用户的 `user_resources`,只取 `status = extracted` 且未删除资源;再按批次调用 EverOS search,过滤这些资源的 `session_id`。
|
||||
3. `all_user_memory`:调用 EverOS search,不加 `session_id` 过滤。
|
||||
1. `current_chat`:调用上游记忆服务 search,过滤 `filters.session_id = chat:{conversation_id}`。
|
||||
2. `resources`:先查当前用户的 `user_resources`,只取 `status = extracted` 且未删除资源;再按批次调用上游记忆服务 search,过滤这些资源的 `session_id`。
|
||||
3. `all_user_memory`:调用上游记忆服务 search,不加 `session_id` 过滤。
|
||||
4. 合并结果。
|
||||
5. 过滤 `memory_tombstones` 命中的 `memory_id` 或 `session_id`。
|
||||
6. 应用 active `memory_overrides`,把 `text` 替换为 `override_text`。
|
||||
@ -495,7 +495,7 @@ curl -X POST http://127.0.0.1:8010/memories/search \
|
||||
"raw": {
|
||||
"id": "mem_abc",
|
||||
"session_id": "resource:u_123:r_xxx",
|
||||
"episode": "原始 EverOS 返回内容"
|
||||
"episode": "原始上游记忆服务返回内容"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -518,7 +518,7 @@ Content-Type: application/json
|
||||
| `session_id` | string | 是 | memory 所属 session,必须属于当前用户 |
|
||||
| `override_text` | string | 是 | 修正后的记忆文本 |
|
||||
|
||||
该接口只写入或更新 `memory_overrides`,不会修改 EverOS 原始文件。写入前会校验 `session_id` 属于当前用户:当前版本支持当前用户的 `resource:{user_id}:{resource_id}` 和 `memory_edit:{user_id}`。后续搜索结果命中该 `memory_id` 时,返回的 `text` 会替换为 `override_text`,但保留原始 memory id。
|
||||
该接口只写入或更新 `memory_overrides`,不会修改上游记忆服务原始文件。写入前会校验 `session_id` 属于当前用户:当前版本支持当前用户的 `resource:{user_id}:{resource_id}` 和 `memory_edit:{user_id}`。后续搜索结果命中该 `memory_id` 时,返回的 `text` 会替换为 `override_text`,但保留原始 memory id。
|
||||
|
||||
请求示例:
|
||||
|
||||
@ -559,7 +559,7 @@ Content-Type: application/json
|
||||
| `session_id` | string | 是 | memory 所属 session,必须属于当前用户 |
|
||||
| `reason` | string | 否 | 删除原因 |
|
||||
|
||||
该接口只写入 `memory_tombstones`,不会修改 EverOS 原始文件。写入前会校验 `session_id` 属于当前用户:当前版本支持当前用户的 `resource:{user_id}:{resource_id}` 和 `memory_edit:{user_id}`。后续搜索结果如果命中 tombstone 的 `memory_id` 或 `session_id`,会被过滤。
|
||||
该接口只写入 `memory_tombstones`,不会修改上游记忆服务原始文件。写入前会校验 `session_id` 属于当前用户:当前版本支持当前用户的 `resource:{user_id}:{resource_id}` 和 `memory_edit:{user_id}`。后续搜索结果如果命中 tombstone 的 `memory_id` 或 `session_id`,会被过滤。
|
||||
|
||||
请求示例:
|
||||
|
||||
@ -584,9 +584,9 @@ curl -X DELETE http://127.0.0.1:8010/memories/mem_abc \
|
||||
}
|
||||
```
|
||||
|
||||
## EverOS client 封装
|
||||
## 上游记忆服务客户端封装
|
||||
|
||||
Gateway 内部通过 `core/everos_client.py` 调用 EverOS:
|
||||
Gateway 内部通过 `core/backend_client.py` 调用上游记忆服务:
|
||||
|
||||
- `add_memory(payload)` -> `POST /api/v1/memory/add`
|
||||
- `flush_memory(session_id, app_id, project_id)` -> `POST /api/v1/memory/flush`
|
||||
@ -619,21 +619,21 @@ cd /home/tom/memory-gateway
|
||||
.venv/bin/python -B -m pytest -q -p no:cacheprovider
|
||||
```
|
||||
|
||||
默认测试不会访问真实 EverOS。若要对已部署的 EverOS 做 health 集成验证,先确认 EverOS 正在监听 `0.0.0.0:1995`,然后从 Gateway 所在机器用客户端可访问地址访问:
|
||||
默认测试不会访问真实上游记忆服务。若要对已部署的上游记忆服务做 health 集成验证,先确认上游记忆服务正在监听 `0.0.0.0:1995`,然后从 Gateway 所在机器用客户端可访问地址访问:
|
||||
|
||||
```bash
|
||||
cd /home/tom/memory-gateway
|
||||
RUN_EVEROS_INTEGRATION=1 \
|
||||
EVEROS_BASE_URL=http://10.6.80.123:1995 \
|
||||
.venv/bin/python -B -m pytest -q tests/test_everos_integration.py -p no:cacheprovider
|
||||
RUN_BACKEND_INTEGRATION=1 \
|
||||
MEMORY_GATEWAY_BACKEND_BASE_URL=http://10.6.80.123:1995 \
|
||||
.venv/bin/python -B -m pytest -q tests/test_backend_integration.py -p no:cacheprovider
|
||||
```
|
||||
|
||||
真实 add/flush 上传会写入 EverOS,且可能受上游解析、LLM、embedding 服务耗时影响。需要验证完整摄入链路时再打开第二层开关:
|
||||
真实 add/flush 上传会写入上游记忆服务,且可能受上游解析、LLM、embedding 服务耗时影响。需要验证完整摄入链路时再打开第二层开关:
|
||||
|
||||
```bash
|
||||
cd /home/tom/memory-gateway
|
||||
RUN_EVEROS_INTEGRATION=1 \
|
||||
RUN_EVEROS_INGEST_INTEGRATION=1 \
|
||||
EVEROS_BASE_URL=http://10.6.80.123:1995 \
|
||||
.venv/bin/python -B -m pytest -q tests/test_everos_integration.py -p no:cacheprovider
|
||||
RUN_BACKEND_INTEGRATION=1 \
|
||||
RUN_BACKEND_INGEST_INTEGRATION=1 \
|
||||
MEMORY_GATEWAY_BACKEND_BASE_URL=http://10.6.80.123:1995 \
|
||||
.venv/bin/python -B -m pytest -q tests/test_backend_integration.py -p no:cacheprovider
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user