fix(webui): 附件消息历史持久化 + 用户文件上传(对齐 qq 插件收文件设计)

1. 附件展示链路补全
   - ChatMsg 新增 Attachment 字段(type/url/size/name),channel_output
     订阅提取 output_type/url/size 存档——修复刷新后附件变纯文本路径
     (如 '/tmp/homeagent.png')的问题
   - EventRawInput 订阅支持 upload_* 字段:用户上传的消息也带附件卡片

2. 用户→agent 文件上传(POST /api/v1/chat/file)
   设计对齐 qq 插件收文件模式:
   - multipart(file + message) 落盘 <data>/uploads/<原文件名>(重名加
     毫秒后缀,路径穿越消毒),单文件上限 64MB
   - 注入文本「[用户通过 webui 发送了图片: 名字 (大小)] + 保存路径」,
     agent 用 files_read 等工具按路径消费
   - 回复走与普通消息相同的 SSE 流式管道
   - GET /uploads/<name> 下载,与 /files/ 同一鉴权与安全模型

3. 前端
   - 输入区 📎 按钮;拖拽到聊天区直接发送;粘贴截图即发送
   - 本地预览用 URL.createObjectURL 即时显示
This commit is contained in:
JianFeeeee
2026-08-26 10:03:46 +08:00
parent 0afa84a13f
commit 534232b768
3 changed files with 323 additions and 12 deletions

View File

@ -48,6 +48,10 @@ func randomSecret(n int) string {
// 由插件 Start 时从 daemon.data_dir 推导注入。
var webFilesDir string
// uploadsDir 是用户经 webui 上传文件的存储目录(<data>/uploads
// handleChatFile 落盘、handleUploads 下载共用;参考 qq 插件 files_dir 收文件设计。
var uploadsDir string
// stageWebFile 把 agent 要发送的本地文件拷贝到 webui_files 中转目录,
// 返回可下载 URL 路径与字节数。image/file 的 payload 支持本地路径或 http(s) URL
// URL 直接透传给前端,不落盘)。文件名用随机 UUID 防路径猜测,扩展名保留自源文件。
@ -132,6 +136,7 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error {
if dd, err := s.Settings().GetCore("daemon.data_dir"); err == nil {
if s2, ok := dd.(string); ok && s2 != "" {
webFilesDir = filepath.Join(s2, "webui_files")
uploadsDir = filepath.Join(s2, "uploads")
}
}