问数据事件日志:助手的心跳不落库、已结束的轮次按期清理。心跳(模型几秒没出声时的 heartbeat 操作)是纯保活,以前每 3 秒落一条库;现在只在本进程里记最近的一拍(chat/log.beat),流上发一条不带 seq 的,重连时流先补最近的那一拍,之后落了任何事件就作废;页面照收不带 seq 的心跳、不动收到的最后一个 seq。其余事件照落(第 2 波跨进程从库里按 seq 追)。已结束(成功、失败、取消、中断)超过 14 天的轮次整轮删事件日志(chat/log.purge,挂在调度循环的那一拍里,一小时最多一次):重开已结束的一轮读的是 conversation_turns 那一行,不读事件日志;任务行留着,seq 接着发号。README 数据位置写明

This commit is contained in:
ThearchyHelios
2026-10-19 09:34:14 +08:00
parent 8ba0b27097
commit edde451d19
8 changed files with 252 additions and 13 deletions
+6 -1
View File
@@ -1274,7 +1274,9 @@ async function driveTurn(T, epoch) {
for (const op of got.ops) {
if (op.gate) { await until(() => ctl[op.gate] || gone(T, epoch), 15000); continue }
if (gone(T, epoch)) return
emit(T, 'op', op)
// 心跳不落库、不带 seq(服务端 chat/log.beat):只记最近的一拍,流上发一条不带 seq 的;之后落了新事件就作废
if (op.op === 'heartbeat') T.beat = { op, at: T.events.length }
else emit(T, 'op', op)
if (op.op !== 'thinking' && op.op !== 'heartbeat') ops.push(op)
if (op.op === 'look') T.looks.push(op)
if (op.op === 'usage') T.usage.copilot = addTotal(T.usage.copilot, op)
@@ -1500,6 +1502,9 @@ async function streamTurn(route, T, after) {
await until(() => T.events.length > after || !turnActive(T) || T.gen !== srv.gen, 6000)
const fresh = T.events.filter((e) => e.seq > after)
let body = fresh.map((e) => `data: ${JSON.stringify(e)}\n\n`).join('')
if (turnActive(T) && T.beat && T.beat.at === T.events.length) {
body += `data: ${JSON.stringify({ type: 'op', data: T.beat.op, ts: Date.now() / 1000 })}\n\n`
}
if (!turnActive(T) && after + fresh.length >= T.events.length) {
body += `data: ${JSON.stringify({ type: 'stream.end', status: T.job.status, data: { status: T.job.status } })}\n\n`
}