# S6: 消息编辑 + 重新生成
## 目标
为 AI 聊天页面(aichat.html)增加两条交互能力:
1. **双击编辑用户消息**:双击用户气泡 → inline 编辑 → Enter 保存 → 重新调 LLM
2. **重新生成按钮**:AI 消息旁出现"重新生成"按钮 → 删除旧回复 → 重新调 LLM
## 涉及文件
1. `local_db.py` — 新增 `msg_get()`、`msg_update()`、`msg_delete_since()`
2. `app.py` — 新增 `PUT /api/conversations//messages/`、`POST /api/conversations//regenerate`
3. `templates/aichat.html` — 双击编辑 + 重新生成按钮 UI
4. 测试文件:`test_s6_edit_regenerate.py`
## 数据流
```
编辑: 双击用户气泡 → contenteditable → Enter → PUT /api/.../messages/ {"content":new}
→ 前端重新渲染 → 可选自动触发 regenerate
重新生成: 点击 ⟳ 按钮 → POST /api/.../regenerate
→ 后端删除最后一条 assistant 消息 → 重新调 LLM → 流式返回新回复
```
## 步骤
### S6a: local_db 新增函数
- `msg_get(msg_id)` — 获取单条消息
- `msg_update(msg_id, content)` — 更新消息内容
- `msg_delete_since(conv_id, since_id)` — 删除指定 ID 之后的消息(用于 regenerate)
### S6b: API 路由
- `PUT /api/conversations//messages/` — 编辑消息,校验 role=user
- `POST /api/conversations//regenerate` — 删除最后 AI 回复 → 重新调 LLM → 流式返回
### S6c: 前端 UI
- 用户消息增加 data-msgid 属性,双击触发 inline edit
- AI 最后一条消息旁显示 ⟳ 重新生成按钮
- Enter 保存编辑,Escape 取消
## 验证命令
```bash
# 单元测试
python3 test_s6_edit_regenerate.py
# 页面回归
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/chat
# smoketest
python3 test_smoke.py
```
## 验收标准(Sprint Contract)
- [ ] TC1: `msg_get(id)` 返回正确消息,不存在时返回 None
- [ ] TC2: `msg_update(id, content)` 更新后 `msg_get` 返回新内容
- [ ] TC3: `msg_delete_since(conv_id, since_id)` 删除指定 ID 之后的消息(不含 since_id)
- [ ] TC4: `PUT /api/conversations//messages/` 编辑成功返回 200,校验非 user 消息返回 400
- [ ] TC5: `POST /api/conversations//regenerate` 删除最后 AI 消息 → 流式返回新回复
- [ ] TC6: 页面 `/chat` 返回 200,用户消息有 data-msgid 属性
- [ ] TC7: AI 消息区包含 regenerate 相关触发(函数名或按钮元素)
- [ ] TC8: JS 无转义 bug(style=\\\\\\"、onclick=\\")
- [ ] TC9: smoketest 全部通过(首页/articles/learn/reports 200)
## 风险
- 低风险:纯增量,不改现有路由
- 重新生成需要调用 Hermes:复用现有 `call_hermes_stream` 逻辑