refactor+feat(documents,channelbridge): 共享存储核心 + IM 入站附件摄取(主人 2.3)
- 抽共享存储核心:internal/runtime/documents/store.go(22 项白名单 + 字节嗅探唯一裁决 + 内容寻址 sha256[:32]+ext + 去重 + 限额 + 路径双检)+ store_meta.go(documents 表唯一写入实现)。
- gateway 改为调用 store,**对外行为零变化**(机器证明:既有 documents_test/chat_documents_test/chat_documents_bounds_test 三个文件 0 行改动且全绿;状态码/响应字段/错误码/审计动作名/限额文档口径全部逐字保留;maxChatDocuments 改为引用 documents.MaxPerMessage)。
- IM 入站附件摄取(channelbridge):文件类媒体 → store.Save → 挂 model.DocumentMessage(RoleUser) 部件 ⇒ 后续 resolveDocuments 自动抽取/注入/清单;挂载点在「会话解析 + 黑白名单 + 最小打扰」三重闸门之后(被拦消息绝不在盘上留文件);**不支持的格式不静默**(诚实提示行 + 带来源日志,只记 name/mime/size,名字做展示净化防伪造行);图片/语音保持既有链路。
- 主会话独立验收:隔离 worktree(M1 的 12 文件)build/vet 绿、全树 59 包 0 FAIL、双平台交叉编译绿、12 条 Inbound 用例 PASS;变异独立复现(关闭 IM 摄取 ⇒ 5 条红,含「落盘文件数 = [] want 1」)。
- ⚠️ 真机边界(需裁决):现网四个适配器上报的文件媒体**只带 URL**(telegram FileID / qq-obv11 file|url / weixin·kook CDN URL),Media.Data 恒空 ⇒ 本切片只摄**内联字节**,URL 一律「诚实提示,不代取远程」(避免第三份 SSRF 实现)。真机要让「她读到 IM 文件」,需各适配器入站时下载并内联(凭据在各自渠道里,推荐)或新增共享受守卫抓取器。
- 另登记:IM 附件暂未写 chat_messages.document 列(二手回合看不到引用,建议并入 S7);channelbridge 既有测试夹具在 -race 下有数据竞争(stubSessions.GetOrCreate,与本次无关)。
This commit is contained in:
1 parent
51c1672355
commit
2a43035f5d
12 files changed
+1743
-158
No files matched your search
@@ -23,6 +23,7 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/session"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/documents"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||||
@@ -148,6 +149,12 @@ type Options struct {
|
||||
// 被移入某个角色的作用域(design/22 §7-4)。为 nil 时所有渠道都使用全局
|
||||
// 默认角色。
|
||||
ChannelPersona func(platform, accountID string) string
|
||||
// DocumentStore 是 IM **入站附件**的落盘配置(design/24 §3;切片 M1):
|
||||
// 目录、单文件上限与 `documents` 元数据写入面。它由组合根给出(桥接器不猜
|
||||
// 目录布局),并与 Web 上传共用同一个存储核心。UploadsDir 与 DataDir 都为
|
||||
// 空即视为**未接线**:带文件的消息照旧能跑(模型会收到一行「读不到」的诚实
|
||||
// 提示),只是不落库、不抽取 —— 绝不假装读到了。
|
||||
DocumentStore documents.StoreConfig
|
||||
}
|
||||
|
||||
// Bridge 掌管渠道适配器生命周期与入站消息流水线。它
|
||||
@@ -195,6 +202,9 @@ type Bridge struct {
|
||||
// 都由 mu 保护。
|
||||
channelPersona func(platform, accountID string) string
|
||||
adapterPersona map[string]string
|
||||
// documentsCfg 是入站附件落盘配置(design/24 §3;切片 M1)。零值 = 未接线:
|
||||
// 带文件的消息照旧能跑,模型会收到一行「读不到」的诚实提示(绝不静默)。
|
||||
documentsCfg documents.StoreConfig
|
||||
// proactiveCursor 是主动推送「无信号」兜底时的轮转游标
|
||||
// (channelbridge/proactive.go),使默认渠道不会总是
|
||||
// 白名单中的第一个主人渠道。由 mu 保护。
|
||||
@@ -267,6 +277,7 @@ func New(opts Options) (*Bridge, error) {
|
||||
factory: opts.Factory,
|
||||
channelPersona: opts.ChannelPersona,
|
||||
adapterPersona: make(map[string]string),
|
||||
documentsCfg: opts.DocumentStore,
|
||||
cancel: func() {},
|
||||
sem: make(chan struct{}, maxConcurrent),
|
||||
}, nil
|
||||
|
||||
Reference in new issue
Block a user