#23 修复(服务端根治,五端客户端提交信号自动变安全): - finish 先冻结会话并停 usageLoop,再 flush provider——此前 usageLoop 在 waitResults(最长 3s)期间继续 tick,周期 usage 帧会抢在尾部 final 之前 下发,客户端以 partial 文本提前上屏、丢失 final 修正(12B 实测 8s 会话 4/4 复现;修复后 6/6 归零) - 桌面端收尾兜底超时 350ms → 800ms(实测 8s 会话 final flush 需 350~540ms, 350ms 会截丢尾 final) #24 预连接(12B 调优): - GummyProvider 常备一条已完成 run-task 握手的 spare 会话,start 直取, 后台异步补位 + 40s 定期换新(DashScope 空闲 60s 断连,实测 60s 存活/ 120s Idle timeout,留余量 45s) - 实测网关 start 处理 120~670ms → 3~4ms;消除 dial 抖动(实测 60ms~3.7s) 对 first_partial 尾部的放大 gummycheck 增加 -model / -pace / -idle 参数,支持模型对比与闲置存活实验 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -364,24 +364,26 @@ const finishWaitTimeout = 3 * time.Second
|
||||
// 注意 done/consumeMu 顺序(16D):先停 usageLoop 并 drain 在途 consumeDelta,
|
||||
// 再读 trialPart/balancePart 快照,避免漏记在途扣费。
|
||||
func (s *session) finish(ctx context.Context, canceled bool) {
|
||||
// 先冻结会话并停 usageLoop,再 flush provider(12B/#23):若 usageLoop 在
|
||||
// waitResults 期间(最长 3s)继续 tick,周期 usage 帧会抢在尾部 final 之前
|
||||
// 下发——客户端把"stop 后首个 final/usage"当提交信号,会以 partial 文本
|
||||
// 提前上屏、丢失 final 修正。冻结后 stop 之后唯一的 usage 是本函数末尾的
|
||||
// 结算帧,且必然在全部尾部 final 之后,客户端提交信号因此安全。
|
||||
s.mu.Lock()
|
||||
if s.finished {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
s.finished = true
|
||||
close(s.done)
|
||||
s.mu.Unlock()
|
||||
|
||||
_ = s.provider.Close()
|
||||
s.waitResults() // 尾部 final 全部下发后再收尾(带超时,16B)
|
||||
|
||||
// 先标记 finished 并停 usageLoop,再 drain 在途 consumeDelta(16D):
|
||||
// 取 consumeMu 会等待任何已过 finished 检查、正等 Redis 返回的 consumeDelta
|
||||
// 完成并把 trialPart/balancePart 计入,从而快照不漏账。
|
||||
s.mu.Lock()
|
||||
s.finished = true
|
||||
close(s.done)
|
||||
s.mu.Unlock()
|
||||
|
||||
// drain 在途 consumeDelta(16D):finished 已置位,取 consumeMu 会等待任何
|
||||
// 已过 finished 检查、正等 Redis 返回的 consumeDelta 完成并把
|
||||
// trialPart/balancePart 计入,从而快照不漏账。
|
||||
s.consumeMu.Lock() // drain:等当前在途 consume(若有)完成
|
||||
s.consumeMu.Unlock()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user