docs: 同步事件时间轴删除视频功能到 README/PROGRESS
补充 §5.1/§5.2 新增的 POST /api/oracle/video/delete + DELETE /api/ui/videos/<id> 端点说明、FAM-UI 页面表格、§12 当前进度;PROGRESS.md 记录完整变更 + 端到端 验证过程。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
17
PROGRESS.md
17
PROGRESS.md
@@ -1,17 +1,28 @@
|
|||||||
# 项目进度追踪
|
# 项目进度追踪
|
||||||
|
|
||||||
> 最后更新: 2026-08-23
|
> 最后更新: 2026-08-24
|
||||||
|
|
||||||
## 服务运行状态
|
## 服务运行状态
|
||||||
|
|
||||||
| 服务 | 节点 | 地址 | 状态 | 验证结果 |
|
| 服务 | 节点 | 地址 | 状态 | 验证结果 |
|
||||||
|------|------|------|------|---------|
|
|------|------|------|------|---------|
|
||||||
| FAM-Core | NAS | 0.0.0.0:8000 | ✅ 运行中 | health=ok, gunicorn 单 worker;Oracle-Sync + MotionNotifier 轮询 SS 事件推送(游标 DB 续用/失败重试) |
|
| FAM-Core | NAS | 0.0.0.0:8000 | ✅ 运行中 | health=ok, gunicorn 单 worker;Oracle-Sync + MotionNotifier 轮询 SS 事件推送(游标 DB 续用/失败重试);已部署事件时间轴删除功能 |
|
||||||
| FAM-Edge | Oracle | 0.0.0.0:5000 | ✅ 运行中 | systemd 守护(fam-edge.service);素材→运动片段分割→只分析片段;问答改为转发 AI-Gateway;队列消费正常 |
|
| FAM-Edge | Oracle | 0.0.0.0:5000 | ✅ 运行中 | systemd 守护(fam-edge.service);素材→运动片段分割→只分析片段;问答改为转发 AI-Gateway;队列消费正常;已部署 `/api/oracle/video/delete` |
|
||||||
| **AI-Gateway** | Oracle | 0.0.0.0:5100 | ✅ 运行中 | systemd 守护(ai-gateway.service),2026-08-23 新增;独立仓库/独立部署;`/health` 正常,端到端问答实测成功(provider=nvidia) |
|
| **AI-Gateway** | Oracle | 0.0.0.0:5100 | ✅ 运行中 | systemd 守护(ai-gateway.service),2026-08-23 新增;独立仓库/独立部署;`/health` 正常,端到端问答实测成功(provider=nvidia) |
|
||||||
| MariaDB | NAS | 127.0.0.1:3306 | ✅ 运行中 | 10.11.11, 6 张表, utf8mb4 |
|
| MariaDB | NAS | 127.0.0.1:3306 | ✅ 运行中 | 10.11.11, 6 张表, utf8mb4 |
|
||||||
| Ollama | Oracle | 127.0.0.1:11434 | ✅ 运行中 | qwen2.5:7b,仅智能问答兜底(不参与视觉/融合);**被 AI-Gateway 调用,不再被 FAM-Edge 直接调用** |
|
| Ollama | Oracle | 127.0.0.1:11434 | ✅ 运行中 | qwen2.5:7b,仅智能问答兜底(不参与视觉/融合);**被 AI-Gateway 调用,不再被 FAM-Edge 直接调用** |
|
||||||
|
|
||||||
|
## 2026-08-24 事件时间轴支持删除视频会话
|
||||||
|
|
||||||
|
- **决策**:用户要求事件时间轴能删除某个视频会话;调研确认项目里没有任何 delete 类端点先例,且增量同步(`get_sync_delta`)只做 upsert 感知不到 Oracle 端的物理删除,NAS 镜像必须显式清理,不能靠 `trigger_now()` 拉增量。用户明确要求连同磁盘上的视频文件一起删除,明确选择不做"防止误删事件被重新分割"的额外保护机制(风险极低:素材一旦标记 done 就不会被生产者重新捡起)
|
||||||
|
- **三端实现**(commit `28397e9`):
|
||||||
|
- fam-edge:`oracle_db.py` 新增 `delete_video(video_id)`(写锁保护,先删 events 再删 videos,再删磁盘文件;不清理 `ss_motion_events` 源事件);`api_gateway.py` 新增 `POST /api/oracle/video/delete`
|
||||||
|
- fam-core:`oracle_sync.py` 新增 `push_video_delete()`;`db_layer.py` 新增 `delete_sync_video()`(项目里第一个"NAS 直接写自己镜像表"的函数);`ui_api.py` 新增 `DELETE /api/ui/videos/<id>`(先回推 Oracle 成功后才清本地镜像,回推失败 502 不改本地状态,避免数据不一致)
|
||||||
|
- fam-ui:`Timeline.vue` 详情卡片新增"🗑 删除会话"按钮(原生 `confirm()` 二次确认,删除成功后从本地列表移除 + 重新选中 + 刷新统计卡);`api.js` 新增 `deleteVideo()`
|
||||||
|
- **测试**:`test_oracle_db.py` 新增 6 个 `delete_video` 单元测试(正常删除/磁盘文件删除/文件已缺失不报错/不存在返回 None/不影响 ss_motion_events);fam-edge 全量 130/130 通过
|
||||||
|
- **端到端验证**:全程用自造测试数据(`motion_TESTDELETE*`),未触碰任何真实监控记录——Oracle 侧直接调 `/api/oracle/video/delete` 验证记录+文件删除+幂等 404;NAS 侧手动插入镜像行模拟已同步状态,登录后调 `DELETE /api/ui/videos/<id>`,验证 Oracle 记录+文件、NAS 镜像记录均被清理
|
||||||
|
- **已知限制**:前端浏览器 UI 层面未做可视化验证(fam-core 全站要求登录,出于"不代用户输入密码"的原则没有走浏览器交互式登录),只验证了 `npm run build` 编译通过 + 完整 API 链路
|
||||||
|
|
||||||
## 2026-08-23 问答链路抽离为独立 ai-gateway 服务
|
## 2026-08-23 问答链路抽离为独立 ai-gateway 服务
|
||||||
|
|
||||||
- **决策**:原本嵌在 FAM-Edge 里的问答模型降级链(NVIDIA 文字模型链 → Gemini 非 flash 文字模型链 → 本地 Ollama 兜底,含 key 轮换/熔断)跟视频分析业务无关,是通用能力,抽成独立服务(OpenAI 兼容协议 `/v1/chat/completions`),除了 FAM-Edge 自己(改为转发调用),别的项目也能直接接入
|
- **决策**:原本嵌在 FAM-Edge 里的问答模型降级链(NVIDIA 文字模型链 → Gemini 非 flash 文字模型链 → 本地 Ollama 兜底,含 key 轮换/熔断)跟视频分析业务无关,是通用能力,抽成独立服务(OpenAI 兼容协议 `/v1/chat/completions`),除了 FAM-Edge 自己(改为转发调用),别的项目也能直接接入
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
> 多模型容灾降级 + 交互式命名 + AI 对话的家庭监控系统。
|
> 多模型容灾降级 + 交互式命名 + AI 对话的家庭监控系统。
|
||||||
> 本文档为项目需求文档与 README 的整合版,按当前代码实际状态(v1.0 E2E 已打通)编写。
|
> 本文档为项目需求文档与 README 的整合版,按当前代码实际状态(v1.0 E2E 已打通)编写。
|
||||||
> 最后更新:2026-08-23(问答链路抽离为独立 ai-gateway 服务:FAM-Edge 不再自己维护问答模型降级链,改为转发调用)
|
> 最后更新:2026-08-24(事件时间轴支持删除视频会话)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ Vue3 + Vite + Tailwind SPA(`fam-ui/src/views/*.vue`,构建产物 `fam-ui/dis
|
|||||||
|
|
||||||
| 页面 | 功能 |
|
| 页面 | 功能 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 🕒 事件时间轴 | 运动片段会话列表(`motion_*.mp4`,只显示有内容的会话)+ 选中会话的事件时间线(时间点 + 描述 + 人物/关注徽章 + 事件帧图);支持 `?video=<id>` 定位跳转 |
|
| 🕒 事件时间轴 | 运动片段会话列表(`motion_*.mp4`,只显示有内容的会话)+ 选中会话的事件时间线(时间点 + 描述 + 人物/关注徽章 + 事件帧图);支持 `?video=<id>` 定位跳转;详情卡片支持**删除会话**(原生 `confirm()` 二次确认,2026-08-24 新增) |
|
||||||
| 👤 人物管理 | 按规范名/标签聚合,命名/合并(回推 Oracle);**人物卡含「运动片段」区块**(该人物出现过的运动片段:缩略图/时间/摘要/事件数,点击跳时间轴) |
|
| 👤 人物管理 | 按规范名/标签聚合,命名/合并(回推 Oracle);**人物卡含「运动片段」区块**(该人物出现过的运动片段:缩略图/时间/摘要/事件数,点击跳时间轴) |
|
||||||
| 💬 AI 对话 | 输入框 + 调 `/api/chat/ask`;按 queried_person 预设快捷提问 |
|
| 💬 AI 对话 | 输入框 + 调 `/api/chat/ask`;按 queried_person 预设快捷提问 |
|
||||||
| 📝 对话历史 | chat_history 倒序展示 |
|
| 📝 对话历史 | chat_history 倒序展示 |
|
||||||
@@ -306,6 +306,7 @@ chat_history 独立表(问答上下文摘要留存)
|
|||||||
- 401:token 校验失败
|
- 401:token 校验失败
|
||||||
|
|
||||||
**POST /api/oracle/people/correct** — 命名校正回推:`{"label":"人物A","canonical_name":"汤圆","token":...}`(manual 优先,不被 LLM 覆盖)→ `{"status":"ok"}`
|
**POST /api/oracle/people/correct** — 命名校正回推:`{"label":"人物A","canonical_name":"汤圆","token":...}`(manual 优先,不被 LLM 覆盖)→ `{"status":"ok"}`
|
||||||
|
**POST /api/oracle/video/delete**(2026-08-24 新增)— 删除视频会话(NAS 转发):`{"video_id":123,"token":...}` → 删 `events`+`videos` 行 + 磁盘上的运动片段文件 → `{"status":"ok","video_id":123}`;**不清理**对应的 `ss_motion_events` 源事件(那是硬件推送的原始事件记录,跟切出来的片段生命周期独立;素材一旦处理完就不会被生产者重新捡起,删片段不会触发重新分割);video_id 不存在返回 404
|
||||||
**POST /api/edge/chat/ask(/stream)** — 智能问答(FAM-Core Chat-Handler 调用):请求 `{"prompt","max_tokens"}` → 响应 `{"answer","provider"}`(流式版为 SSE,事件 `provider_trying`/`chunk`/`done`/`all_failed`);**2026-08-23 起 FAM-Edge 自己不跑模型,转发到同机 AI-Gateway `/v1/chat/completions`**,内部按 NVIDIA → Gemini → 本地 Ollama 顺序降级(对 FAM-Core 不可见,接口契约不变)
|
**POST /api/edge/chat/ask(/stream)** — 智能问答(FAM-Core Chat-Handler 调用):请求 `{"prompt","max_tokens"}` → 响应 `{"answer","provider"}`(流式版为 SSE,事件 `provider_trying`/`chunk`/`done`/`all_failed`);**2026-08-23 起 FAM-Edge 自己不跑模型,转发到同机 AI-Gateway `/v1/chat/completions`**,内部按 NVIDIA → Gemini → 本地 Ollama 顺序降级(对 FAM-Core 不可见,接口契约不变)
|
||||||
**POST /api/ss/motion** — 运动事件接收(NAS MotionNotifier 推送):`{"token", "events":[{event_id, camera_id, event_type, start_time, duration, thumbnail_url}]}` → 幂等落库 `ss_motion_events` + 刷新心跳;`events: []` 空数组即心跳
|
**POST /api/ss/motion** — 运动事件接收(NAS MotionNotifier 推送):`{"token", "events":[{event_id, camera_id, event_type, start_time, duration, thumbnail_url}]}` → 幂等落库 `ss_motion_events` + 刷新心跳;`events: []` 空数组即心跳
|
||||||
**GET /api/oracle/frame** — 事件帧图:`?video_id=&ts=&w=` → jpeg(`ts` 为绝对时间,`frame_service` 按 `ts − event_start_time` 偏移从视频文件取帧)
|
**GET /api/oracle/frame** — 事件帧图:`?video_id=&ts=&w=` → jpeg(`ts` 为绝对时间,`frame_service` 按 `ts − event_start_time` 偏移从视频文件取帧)
|
||||||
@@ -331,6 +332,7 @@ chat_history 独立表(问答上下文摘要留存)
|
|||||||
| `/api/ss/status` | GET | 运动监测状态:`poll_enabled` / `camera_loaded` / `camera_map` / `pushed_total` / `last_error` / 心跳 |
|
| `/api/ss/status` | GET | 运动监测状态:`poll_enabled` / `camera_loaded` / `camera_map` / `pushed_total` / `last_error` / 心跳 |
|
||||||
| `/api/ui/videos` | GET | 运动片段会话列表(`?date=&page=`;**只返回 `motion_` 前缀或有事件的会话**,0 段素材不展示) |
|
| `/api/ui/videos` | GET | 运动片段会话列表(`?date=&page=`;**只返回 `motion_` 前缀或有事件的会话**,0 段素材不展示) |
|
||||||
| `/api/ui/videos/<id>` | GET | 会话详情:`{video, events[]}`(事件含 person_list_json / person_appearances_json / is_attention_event) |
|
| `/api/ui/videos/<id>` | GET | 会话详情:`{video, events[]}`(事件含 person_list_json / person_appearances_json / is_attention_event) |
|
||||||
|
| `/api/ui/videos/<id>` | DELETE | **(2026-08-24 新增)**删除会话:先回推 Oracle 物理删除(含磁盘文件),成功后清本地镜像(`sync_events`+`sync_videos`,增量同步感知不到删除,必须显式清理);Oracle 回推失败返回 502,不改本地状态 |
|
||||||
| `/api/ui/stats` | GET | 统计卡:videos / events / people / attention(`?date=` 过滤,口径与列表一致) |
|
| `/api/ui/stats` | GET | 统计卡:videos / events / people / attention(`?date=` 过滤,口径与列表一致) |
|
||||||
| `/api/ui/people` | GET | 人物列表(按 canonical_name 聚合:display / labels / appearances / first_seen / features_json / display_uid) |
|
| `/api/ui/people` | GET | 人物列表(按 canonical_name 聚合:display / labels / appearances / first_seen / features_json / display_uid) |
|
||||||
| `/api/ui/people/clips` | GET | 人物运动片段:`?label=&limit=` → 该人物出现过的运动片段(video_id / event_start_time / duration / summary / first_ts 缩略图定位 / clip_events) |
|
| `/api/ui/people/clips` | GET | 人物运动片段:`?label=&limit=` → 该人物出现过的运动片段(video_id / event_start_time / duration / summary / first_ts 缩略图定位 / clip_events) |
|
||||||
@@ -827,8 +829,9 @@ export NVIDIA_API_KEY="nvapi-9cFAdO5xdbwPuxS8KGRTnlVimn1gJzbbbzWNhPwHa_Yl3pTe-Pf
|
|||||||
|
|
||||||
## 12. 当前进度与 v1.1 计划
|
## 12. 当前进度与 v1.1 计划
|
||||||
|
|
||||||
### 已完成(截至 2026-08-23)
|
### 已完成(截至 2026-08-24)
|
||||||
|
|
||||||
|
- **事件时间轴支持删除视频会话**(2026-08-24):三端联动,沿用"NAS 转发写请求到 Oracle"的既有模式(新增 `POST /api/oracle/video/delete` + `DELETE /api/ui/videos/<id>`);删 events+videos 行 + 磁盘文件,不清理 `ss_motion_events` 源事件;`db_layer.delete_sync_video()` 是项目里第一个"NAS 直接写自己镜像表"的函数(增量同步机制感知不到删除,不能靠 `trigger_now()` 拉增量清理)
|
||||||
- **问答链路抽离为独立 ai-gateway 服务**(2026-08-23):FAM-Edge 原本自己维护的问答模型降级链(NVIDIA→Gemini→Ollama,含 key 轮换/熔断)整个搬到独立仓库/独立部署单元 `ai-gateway`(OpenAI 兼容协议 `/v1/chat/completions`,Bearer token 鉴权,对外 `:5100`);FAM-Edge `qa.py` 重写为转发客户端,`/api/edge/chat/ask(/stream)` 对 FAM-Core 的契约不变;`OllamaAdapter` 从 FAM-Edge 删除
|
- **问答链路抽离为独立 ai-gateway 服务**(2026-08-23):FAM-Edge 原本自己维护的问答模型降级链(NVIDIA→Gemini→Ollama,含 key 轮换/熔断)整个搬到独立仓库/独立部署单元 `ai-gateway`(OpenAI 兼容协议 `/v1/chat/completions`,Bearer token 鉴权,对外 `:5100`);FAM-Edge `qa.py` 重写为转发客户端,`/api/edge/chat/ask(/stream)` 对 FAM-Core 的契约不变;`OllamaAdapter` 从 FAM-Edge 删除
|
||||||
- **运动事件驱动**(v3,2026-08-22):不再分析整段视频。NAS MotionNotifier 轮询 SS 事件列表(60s,游标续用/失败重试/心跳)推送 `ss_motion_events`;Oracle 整段素材按运动事件 ffmpeg 分割运动片段(`-c:v copy -c:a aac`,只分割已结束事件,`motion_event_id` 幂等),**只分析运动片段**;前端契约不变
|
- **运动事件驱动**(v3,2026-08-22):不再分析整段视频。NAS MotionNotifier 轮询 SS 事件列表(60s,游标续用/失败重试/心跳)推送 `ss_motion_events`;Oracle 整段素材按运动事件 ffmpeg 分割运动片段(`-c:v copy -c:a aac`,只分割已结束事件,`motion_event_id` 幂等),**只分析运动片段**;前端契约不变
|
||||||
- **数据迁移**(2026-08-22):整段提取的旧数据(Oracle videos/events/people + NAS 镜像)已清空并按运动视频重新提取;历史素材(NAS 轮询启动前)无运动事件,时间轴已过滤其空会话
|
- **数据迁移**(2026-08-22):整段提取的旧数据(Oracle videos/events/people + NAS 镜像)已清空并按运动视频重新提取;历史素材(NAS 轮询启动前)无运动事件,时间轴已过滤其空会话
|
||||||
|
|||||||
Reference in New Issue
Block a user