mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 18:08:04 +00:00
fix: 流式渲染回合生命周期 + LLM 瞬断重试与 SSE body 兜底
问题一(webui 不是真流式): - sendChat 的 finally 在 POST 结束(15s ackTimer abort)时就复位 chatLoading,但 agent 生成窗口 15~190s,后续 SSE delta 全部走 全量重建路径、停止按钮提前消失、用户误发重复消息。 - GUI app.js 完全没有 content_delta/reasoning_delta 监听器, 只能等聚合帧一次性显示。 修复:三端统一回合生命周期——POST 只是触发,收尾由 SSE 驱动: - dashboard/GUI 新增 endChatTurn/armTurnWatchdog;拿到同步兜底 响应立即收尾,否则保持回合打开等 agent_output final / reset 帧 / 120s watchdog 兜底 - GUI 补齐 delta 监听器;agent_output 聚合分支 += 改覆盖; reasoning 聚合帧改覆盖(多轮工具调用时旧逻辑会重复累加) - agent_output 误杀分支(final 无 source 即 return 丢弃新输出) 改为内容比较去重,多轮连发时新一轮回复不再被吞 - waiter reasoning_delta reset 从清空全部消息改为 sealLastAgent 问题二(三条只成功一条): - handleChat 60s ctx 含排队时间,agent 串行处理下第 N 条必超时 (实测第 3 条 62s 超时 504);放宽到 300s(客户端 abort 时立即取消) - LLM 单 provider 瞬断无重试:process.go provider 循环内加同源 重试(2 次、退避 2s),401/403 凭证错误与用户中断不重试 - llmsproxy auto 链在非流式请求下可能返回 SSE body(上游恢复后 吐已生成的 chunk 流),非流式解析报 invalid character 'd' 丢掉 整段回复;新增 parseOpenAICompatibleSSEBody 拼接为完整响应 - 顺带修 normalizeStreamToolCalls 分片续传 bug:name 不重发时 argsRaw 被顶层 Arguments(nil) 覆盖丢失 function.arguments 验证: - 连发 3 条 + 单条共 4 条全部成功(首条 190s 重试扛住瞬断) - sse_body_test.go 锁定 SSE body 解析契约(content/usage/tool call 分片)
This commit is contained in:
@ -1342,8 +1342,11 @@ func (h *Handler) handleChat(w http.ResponseWriter, r *http.Request) {
|
||||
if body.ClientMsgID != "" {
|
||||
payload["client_msg_id"] = body.ClientMsgID
|
||||
}
|
||||
// 带超时的上下文,防止 InjectInputSync 长时间阻塞 HTTP 请求
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second)
|
||||
// 带超时的上下文,防止 InjectInputSync 长时间阻塞 HTTP 请求。
|
||||
// 注意:ctx 派生自 r.Context(),客户端提前断开(前端 15s ackTimer abort)时
|
||||
// 立即取消,不会真等满 300s;300s 只约束"连接保持 + agent 排队/长生成"场景
|
||||
// (agent 串行处理,后发消息的排队时间也计入,60s 曾导致连发第 3 条必超时)。
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 300*time.Second)
|
||||
defer cancel()
|
||||
|
||||
respCh := make(chan *agentIO.OutputEvent, 1)
|
||||
@ -1706,8 +1709,8 @@ func (h *Handler) handleOpenAICompletions(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
// 带超时的上下文
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second)
|
||||
// 带超时的上下文(同 handleChat:客户端断开立即取消;300s 约束长生成)
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 300*time.Second)
|
||||
defer cancel()
|
||||
|
||||
respCh := make(chan *agentIO.OutputEvent, 1)
|
||||
@ -1719,7 +1722,7 @@ func (h *Handler) handleOpenAICompletions(w http.ResponseWriter, r *http.Request
|
||||
select {
|
||||
case response = <-respCh:
|
||||
case <-ctx.Done():
|
||||
writeJSON(w, http.StatusGatewayTimeout, map[string]string{"error": "agent timeout (60s)"})
|
||||
writeJSON(w, http.StatusGatewayTimeout, map[string]string{"error": "agent timeout (300s)"})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user