- turnId 由编排器生成、loop Run 复用(硬前提:否则 turn_begin 与流帧各铸一个,客户端两级分桶立即失配) - StateSink 增带 turnId 的变体(未接线时退化为现签名,不丢状态帧) - 上行 chat.parallel(WS chat 帧 + REST 三入口同形;省略 = 逐字不变) - 单会话额度默认 2 / 硬上限 3 / <2 关闭;额度满或未声明 ⇒ 落回既有排队语义 - I1′:checkpoint 归属、历史配对、忙态计数三处从会话级单例升为路级 - session_idle 收紧为「所有在途路收尾且 pending 空」(仅并行时生效;单路逐字不变) - 预留接口(形状定死、无实现无接线):tool/legfile.go 的 FileClaimer / ErrFileBusy / WithLegID - 顺手修一个真 bug:并行路 ctx 派生自提交请求作用域,REST 同步入口一写响应就把它连坐杀死(改 context.WithoutCancel 派生) - 23 个新用例(含 -race -count=3);变异 6 条(turnId 复用 / idle 时点 / 并行升级 / checkpoint 键 / 编排器交 id / pending_ack reason) - 契约变更点 10 条待监工升 v0.65(本人未改 Docs/**)
47 lines
2.2 KiB
Go
47 lines
2.2 KiB
Go
package gateway
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||
)
|
||
|
||
// 本文件归属会话编排器的**待处理队列**(design/15 §2.1/§2.2):忙时到达、且
|
||
// 未声明并行(或并行额度已满)的消息怎么入队、怎么落库、怎么回 ack。它从
|
||
// session_orchestrator.go 拆出,使那个文件继续只留状态机骨架(并守住
|
||
// 「函数 ≤80 行」的硬约束)。
|
||
|
||
// enqueueLocked 把一条忙时到达的用户消息放进有界待处理队列,并返回本次提交的
|
||
// 判定。它**必须在 o.mu 下调用**,并在返回前解锁:落库是慢操作,绝不能在状态
|
||
// 锁下做,因此它自己解锁后再做落库与状态帧广播(与 startParallelLocked 同一
|
||
// 约定,调用方直接 return 它的返回值)。
|
||
//
|
||
// 入队时携带该消息到达时的设备 / 模式 / 语音回调,使它的续跑回合与「它就是当前
|
||
// 回合」时逐项一致(design/16 §4.5、§5.1;协同 C1)。落库两段式的第一段在这里:
|
||
// 用户消息立刻进历史(抽取结论等回合跑起来才有,由续跑结算回写,见
|
||
// session_document_status.go)。
|
||
func (o *Orchestrator) enqueueLocked(ctx context.Context, st *sessionState, sessionID string, messages []model.Message, env loop.SessionEnv, deviceID, msgMode string, hint clientMsgHint) SubmitResult {
|
||
st.pendingSeq++
|
||
seq := st.pendingSeq
|
||
st.pending = append(st.pending, pendingMsg{
|
||
messages: messages,
|
||
env: env,
|
||
deviceID: deviceID,
|
||
mode: msgMode,
|
||
at: time.Now(),
|
||
seq: seq,
|
||
voice: voiceReplySinkFrom(ctx),
|
||
})
|
||
depth := len(st.pending)
|
||
mode := st.mode
|
||
o.mu.Unlock()
|
||
|
||
o.attachPendingDocuments(sessionID, seq, o.persistUserMessages(ctx, sessionID, messages, deviceID, msgMode, env.PersonaID))
|
||
// 声明了并行却落回排队时如实给出 reason,让客户端把气泡渲染成「排队中」而不是
|
||
// 「被拒」(design/15 §2.6);未声明时该键缺席,帧与今天逐字相同。
|
||
o.emitState(sessionID, EventPendingAck, pendingAckPayload(sessionID, mode, depth, parallelRequested(ctx)))
|
||
return SubmitResult{Accepted: true, Pending: true, QueueDepth: depth, MessageID: hint.MessageID}
|
||
}
|