fix(gateway): Web 活跃客户端主动推送走 turnreply 规整(先规整再落库,推帧与落库逐字相同)
M19 复审抓到的真缺口:internal/services/gateway/proactive.go 的 SendProactive -> persistProactiveToMain 这条 Web 活跃客户端主动推送路由 完全绕过 turnreply.NormalizeParagraphs —— 同一条主动消息走 IM 是规整过的、 走 Web 实时推还是原样(半包 <p> 在客户端按「存在完整 <p> 就不再按空行回退」 的契约下整条塌成一坨)。 - 接线:SendProactive 在落库之前先 msg.Content = turnreply.NormalizeParagraphs(msg.Content), 落库与 proactive_message 帧取同一个字符串 ⇒ 两端逐字相同;不在 gateway 另写实现。 - 新增 proactive_normalize_test.go:修前红(web 原样 / im 已规整),修后断言 ① 落库是规整后文本 ② 推帧 == 落库行 ③ 两条路由同源同文本 ④ <br>/<action> 语义不动。 - 既有两条断言按新口径更新(纯散文现在也被包成一个 <p>,与其它三处一致)。
This commit is contained in:
1 parent
4ed5925296
commit
1d3965aa80
5 files changed
+173
-7
No files matched your search
@@ -9,6 +9,7 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/auth"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/turnreply"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/services/backthink"
|
||||
)
|
||||
|
||||
@@ -43,10 +44,20 @@ type proactivePayload struct {
|
||||
// SendProactive 把 msg 投递给每个存活的 owner/admin 连接,并返回投递与落库
|
||||
// 两件事的结果(切片 P0,`backthink.ProactiveDelivery`)。
|
||||
//
|
||||
// 顺序是刻意的 —— **先落库、再发帧**:主动消息必须成为主会话
|
||||
// 顺序是刻意的 —— **先规整、再落库、再发帧**:主动消息必须成为主会话
|
||||
// (`history.MainSessionID`)里的一条真实消息(assistant 角色),否则刷新/
|
||||
// 重载后什么都补不回来(这正是「气泡恒落」的根);而帧要带上落库行的 id,
|
||||
// 客户端才能把实时气泡对齐到历史行。
|
||||
// 客户端才能把实时气泡对齐到历史行。规整必须落在落库**之前** —— 落库行是
|
||||
// 权威文本,先落库后规整只会在库里留下脏的、而帧与历史行逐字对不上。
|
||||
//
|
||||
// 文本与其它三处**同源**(Web 会话回合 / IM 出站 / 主动推送 IM 侧):一律走
|
||||
// `turnreply.NormalizeParagraphs`,本文件绝不另写一份实现(本仓已因「同一条
|
||||
// 规则两份写法」栽过多次)。修前的真缺口正是这条路由**完全绕过**它(M19 复审
|
||||
// 抓到):同一条主动消息走 IM 是规整过的、走 Web 实时推还是原样,半包 `<p>` 在
|
||||
// 客户端「只要存在完整 `<p>` 块就不再按空行回退切分」的契约下整条塌成一坨。
|
||||
// 规整是幂等的写入侧加法(已规范的文本逐字不变),因此 `<p>` = 一段一气泡、
|
||||
// `<br>` = 气泡内分隔、`<action>`/`<narration>` 随所在段落进同一个 `<p>` 这些
|
||||
// 既有语义一条不动。
|
||||
//
|
||||
// 落库失败**不静默**:记一条带来源的 WARN,并把错误返回给调用方,同时
|
||||
// Persisted=false —— 帧照发(实时气泡不能丢),但记录里绝不说「已推送」。
|
||||
@@ -60,6 +71,9 @@ func (h *hub) SendProactive(ctx context.Context, msg adapter.UnifiedMessage) (ba
|
||||
if err := ctx.Err(); err != nil {
|
||||
return backthink.ProactiveDelivery{}, err
|
||||
}
|
||||
// 先规整、再落库:msg 是调用方传来的值副本,就地改写只影响本路由;
|
||||
// 落库与推帧随后取的都是**同一个**字符串,两端因此逐字相同。
|
||||
msg.Content = turnreply.NormalizeParagraphs(msg.Content)
|
||||
persisted, persistErr := h.persistProactiveToMain(ctx, msg)
|
||||
if persistErr != nil {
|
||||
h.log.Warn("persist proactive message to main session failed",
|
||||
|
||||
Reference in new issue
Block a user