feat(gateway): 接线 chat_messages.turn_id 写入(A–D 四点齐)
A 新起一路:publishLegStart 在 persistUserMessages 之前 withTurnID(ctx, leg.turnID)
(该行随 ef3dcb6 已进 HEAD)。
B 队首续跑路:completeTurn 在 o.mu.Unlock() 之后 assignTurnID(sessionID, runLeg.turnID, next.persisted)。
C 被 steer 吃进:consumePending 认领成功后 assignTurnID(sessionID, s.turnID, steerRows(candidates))。
D 助手回复:persistTurnReply 增 turnID 形参,completeTurn 传 leg.turnID —— 刻意**不用** turnIDOfLeg:
interrupt scope=all 会清空 st.legs,而被打断那一路已产出的正文仍要照原样落库(内容是真的),
按 gen 回查只能拿到空串 ⇒ 配对丢失。
删掉 history_turnid_test.go 的 t.Skip:TestSubmitWiresTurnIDForBothRows 真跑并通过。
新增 session_turnid_wiring_test.go(真 SQLite 内存存储 + 真编排器 + 可逐路放行 runner):
单路两条同值 / 并行两路各自同值且两两不同 / 续跑补写(先钉「补写前为空」)/ steer 补写(用该路自己的转向源)/
TestAssistantTurnIDSurvivesInterruptAll(钉住 D 的取值来源)。
变异 5 条(A/B/C/D 各自 + D′ 改回 turnIDOfLeg —— 只有那条守门用例红,证明它守的正是这个取舍)。
诚实边界:turnIDOfLeg 现无生产调用方(保留为兜底);先到先得竞态未构造对抗用例;
IM 渠道桥不走本编排器 ⇒ 其行 turn_id 留空属预期(契约里键缺席)。
This commit is contained in:
1 parent
3589f4b14f
commit
d804c4e675
5 files changed
+281
-10
No files matched your search
@@ -62,14 +62,20 @@ func (o *Orchestrator) turnIsCurrent(sessionID string, gen uint64) bool {
|
||||
// 带着这条说明 —— 队列里的下一条消息不会看到一个「什么都没说」的上下文。res 为
|
||||
// nil(回合报错)时它新建一个只带文案的 Result,调用方的 nil 语义不变(异步路径
|
||||
// 丢弃返回值,同步路径在此之前已按错误返回)。
|
||||
func (o *Orchestrator) persistTurnReply(sessionID string, gen uint64, res *loop.Result, runErr error, deviceID, mode, personaID string) *loop.Result {
|
||||
//
|
||||
// turnID 是本路的权威回合标识(design/15 §2.5.9),由调用方**透传**下来而不是在这里
|
||||
// 按世代号回查:InterruptScope(all) 会把 st.legs 整个清掉,而被打断那一路的助手文本
|
||||
// 仍要照原样落库(内容是真的),那时回查只能拿到空串、把这一行的配对丢掉。调用方
|
||||
// 手里就有 `*turnLeg`,它的 turnID 出生后不可变,因此透传是唯一不会漏标的取法。
|
||||
// 空串等价于加入该列之前的行为(列里留空)。
|
||||
func (o *Orchestrator) persistTurnReply(sessionID string, gen uint64, res *loop.Result, runErr error, deviceID, mode, personaID, turnID string) *loop.Result {
|
||||
if o.history == nil {
|
||||
return res
|
||||
}
|
||||
text := turnreply.Text(res)
|
||||
if res != nil && res.Text == text {
|
||||
// 真实文本:逐字落库,行为与加入本保证之前完全一致。
|
||||
o.persistAssistantReply(sessionID, res, deviceID, mode, personaID)
|
||||
o.persistAssistantReply(sessionID, res, deviceID, mode, personaID, turnID)
|
||||
return res
|
||||
}
|
||||
if errors.Is(runErr, context.Canceled) || !o.turnIsCurrent(sessionID, gen) {
|
||||
@@ -79,6 +85,6 @@ func (o *Orchestrator) persistTurnReply(sessionID string, gen uint64, res *loop.
|
||||
res = &loop.Result{}
|
||||
}
|
||||
res.Text = text
|
||||
o.persistAssistantReply(sessionID, res, deviceID, mode, personaID)
|
||||
o.persistAssistantReply(sessionID, res, deviceID, mode, personaID, turnID)
|
||||
return res
|
||||
}
|
||||
Reference in new issue
Block a user