fix(channelbridge): IM 入站附件引用写进落库的用户消息(安卓报的 P0)
根因:摄取结果只挂到「内存里的模型可见消息」(inbound.go 的 attachInboundAttachments), 落库时只传了 text、history.Message 没有 Document 字段 ⇒ 该列恒空 ⇒ 读侧 omitempty 导致 documents 键整个缺席 ⇒ 任何客户端只看到一条空消息。Web 侧不这样,是因为 gateway/session_persist.go 有 messageTextImageDocument 把 ContentTypeDocument 部件编码进该列 —— IM 路径缺的就是同一步。 修法:inboundAttachments.documentColumn() 复用 history.EncodeDocuments 同一口径写出该列; persistToMain/persistIsolated 增 document 参数并返回刚落库的消息;新增两段式第二段 persistTurnDocumentStatus 按 id 回写抽取结论(只补结论、按存储名匹配、匹配不到原样留), 与 gateway/session_document_status.go 逐字对齐。 契约零扩张:无新端点/帧/字段;0 附件 ⇒ 列空 ⇒ 键省略;形状即 web 的同一结构体。 测试 6 条(含元素 JSON 逐字断言、unreadable 带 reason、被拒不编造芯片、群聊隔离分支、 真循环端到端);变异两条(还原挂载 / 关闭结论回写)⇒ 各 4 条红。
This commit is contained in:
1 parent
f8f2e9c54c
commit
679cee04f7
7 files changed
+606
-19
No files matched your search
@@ -60,20 +60,28 @@ func (b *Bridge) notifyPersisted(msg history.Message) {
|
||||
// Web 来源的消息该字段为空。mood 是该轮角色的心情
|
||||
// (design/01 §2 情绪状态机),只挂在助手回复上(design/16 §3.3:
|
||||
// 助手消息承载 mode/mood);用户消息与未跟踪的心情传 ""。
|
||||
// 未装配历史存储时为空操作,因此桥接器在没有
|
||||
// 存储的情况下行为不变。
|
||||
func (b *Bridge) persistToMain(ctx context.Context, role, content, mood, platform, deviceID, adapterID string) {
|
||||
// document 是这条消息携带的文档引用数组(design/24 §2.4、§6.1),
|
||||
// 形状即 history.EncodeDocuments 的产物;没有附件时传 ""(那一列因此为
|
||||
// 空,读侧 `documents` 键随之省略)。IM 入站的附件与 Web 上传共用这一列,
|
||||
// 客户端两侧读到的附件芯片才是同一个东西。
|
||||
//
|
||||
// 它返回刚落库的完整消息(未装配历史存储、或写库失败时是零值),
|
||||
// 使调用方能在回合结束后按 id 把文档抽取结论补进同一列
|
||||
// (inbound_documents_persist.go 的两段式)。未装配历史存储时为空操作,
|
||||
// 因此桥接器在没有存储的情况下行为不变。
|
||||
func (b *Bridge) persistToMain(ctx context.Context, role, content, mood, document, platform, deviceID, adapterID string) history.Message {
|
||||
if b.history == nil {
|
||||
return
|
||||
return history.Message{}
|
||||
}
|
||||
full, err := b.history.AppendFull(ctx, history.Message{
|
||||
SessionID: history.MainSessionID, Role: role, Content: content, Mood: mood, Platform: platform, DeviceID: deviceID, AdapterID: adapterID, Mode: modeChat,
|
||||
SessionID: history.MainSessionID, Role: role, Content: content, Mood: mood, Document: document, Platform: platform, DeviceID: deviceID, AdapterID: adapterID, Mode: modeChat,
|
||||
})
|
||||
if err != nil {
|
||||
b.log.Warn("persist main session", "err", err, "role", role, "platform", platform, "device_id", deviceID, "adapter", adapterID)
|
||||
return
|
||||
return history.Message{}
|
||||
}
|
||||
b.notifyPersisted(full)
|
||||
return full
|
||||
}
|
||||
|
||||
// persistIsolated 把一条消息写入受限(群聊/陌生人)会话
|
||||
@@ -84,20 +92,24 @@ func (b *Bridge) persistToMain(ctx context.Context, role, content, mood, platfor
|
||||
// 该消息仍不会镜像进 Web 主会话(只有主人私聊
|
||||
// 消息才镜像到 Web);它只交给传输钩子,让
|
||||
// gateway 广播 `channel_persisted` 事件、IM 适配器日志页无需刷新
|
||||
// 即可追加。未装配历史存储时为空操作,因此桥接器在
|
||||
// 没有存储的情况下行为不变。
|
||||
func (b *Bridge) persistIsolated(ctx context.Context, role, content, deviceID string, sess session.Session, platform, senderName, adapterID string) {
|
||||
// 即可追加。document 是该消息携带的文档引用数组(design/24 §2.4),
|
||||
// 与 persistToMain 同一列、同一形状;没有附件时传 ""。
|
||||
//
|
||||
// 它返回刚落库的完整消息(未装配历史存储、或写库失败时是零值),
|
||||
// 供调用方在回合结束后补回文档抽取结论。未装配历史存储时为空操作,
|
||||
// 因此桥接器在没有存储的情况下行为不变。
|
||||
func (b *Bridge) persistIsolated(ctx context.Context, role, content, document, deviceID string, sess session.Session, platform, senderName, adapterID string) history.Message {
|
||||
if b.history == nil {
|
||||
return
|
||||
return history.Message{}
|
||||
}
|
||||
full, err := b.history.AppendFull(ctx, history.Message{
|
||||
SessionID: sess.SessionID, Role: role, Content: content, Platform: platform,
|
||||
SessionID: sess.SessionID, Role: role, Content: content, Document: document, Platform: platform,
|
||||
DeviceID: deviceID, SenderName: senderName, AdapterID: adapterID, Mode: modeChat,
|
||||
})
|
||||
if err != nil {
|
||||
b.log.Warn("persist isolated session", "err", err, "role", role,
|
||||
"session", sess.SessionID, "platform", platform, "sender_name", senderName, "adapter", adapterID)
|
||||
return
|
||||
return history.Message{}
|
||||
}
|
||||
// 把隔离写入作为 `channel_persisted` 事件广播,让 IM 适配器
|
||||
// 日志页实时追加。gateway 按会话区分事件类型:
|
||||
@@ -105,4 +117,5 @@ func (b *Bridge) persistIsolated(ctx context.Context, role, content, deviceID st
|
||||
// 消息本身仍只落在隔离的 sess-xxx 会话(绝不在
|
||||
// 主/`web` 会话,design/16 §4.6 §3)。
|
||||
b.notifyPersisted(full)
|
||||
return full
|
||||
}
|
||||
Reference in new issue
Block a user