fix(webui): sendChat 超时后 SSE 兜底渲染(对齐 GUI 模式)

问题:agent 长任务(159s)超过 15s ack 超时后,旧代码 return 导致
finally 清空 loading 并重置按钮,用户以为失败;SSE 回复虽到但
用户可能已离开/刷新页面。

修复(完全对齐 GUI renderer 的 sendChat 模式):
- 超时后 r=null 不 return,流程继续
- finally 总是清 loading + 恢复按钮(loading 只是 UI 提示)
- toast 明确提示「请求超时(可能已发送,请稍候勿重复发送)」
- r 有值则直接填充最终回复;r=null 则靠 SSE agent_output 流式渲染
This commit is contained in:
JianFeeeee
2026-08-24 22:36:45 +08:00
parent e1a94fc896
commit 8157772132

View File

@ -3593,12 +3593,10 @@ background:
btn.textContent = "";
rerenderChat(true);
// 触发式 POST短超时仅确认受理回复靠 SSE 流式渲染(对齐 GUI 行为)。
// 长 LLM 工具链不再因同步等全量响应而 60s 超时报错。
try {
var ctrl = new AbortController();
var acked = false; // 受理确认是否在超时内返回
var ackTimer = setTimeout(function () {
if (!acked) ctrl.abort();
ctrl.abort();
}, 15000);
var r = null;
try {
@ -3607,40 +3605,46 @@ background:
body: JSON.stringify({ message: text }),
signal: ctrl.signal,
});
acked = true;
} catch (ackErr) {
if (acked) throw ackErr;
// 受理确认超时agent 已在后台处理,保持 loading 等待 SSE 流式回包
console.log("[sendChat] ack timeout (15s), waiting for SSE stream");
state.chatStage = __("AI 处理中...(长任务,请稍候)", "AI processing... (long task)");
return; // 保持 chatLoading=trueSSE 完成事件负责收尾
// 同步超时/失败:不阻塞 UI等 SSE 兑底;明确提示"可能已发送"
console.warn("[sendChat] trigger failed: " + ackErr.message);
toast(
__(
"请求超时(可能已发送,请稍候或在收到回复前勿重复发送)",
"Request timeout (may have been sent; wait for reply before resending)",
),
true,
);
r = null;
} finally {
clearTimeout(ackTimer);
}
state.chatStage = "";
state.chatStage = __("AI 回复中...", "AI replying...");
var last = state.messages[state.messages.length - 1];
if (last && last.role === "assistant" && last._streaming) {
last.content = r.response || __("(无响应)", "(no response)");
last._grow = true;
if (!last.reasoning_content) {
last.reasoning_content = r.reasoning_content || "";
if (r && r.response) {
if (last && last.role === "assistant" && last._streaming) {
last.content = r.response || __("(无响应)", "(no response)");
last._grow = true;
if (!last.reasoning_content) {
last.reasoning_content = r.reasoning_content || "";
}
last._final = true;
delete last._streaming;
} else {
state.messages.push({
role: "assistant",
content: r.response || __("(无响应)", "(no response)"),
reasoning_content: r.reasoning_content,
tool_calls:
last && last.role === "assistant" && last.tool_calls
? last.tool_calls
: [],
_final: true,
_grow: true,
});
}
last._final = true;
delete last._streaming;
} else {
state.messages.push({
role: "assistant",
content: r.response || __("(无响应)", "(no response)"),
reasoning_content: r.reasoning_content,
tool_calls:
last && last.role === "assistant" && last.tool_calls
? last.tool_calls
: [],
_final: true,
_grow: true,
});
state.chatFinalIdx = state.messages.length - 1;
}
state.chatFinalIdx = state.messages.length - 1;
rerenderChat(true);
} catch (e) {
// 真实错误(非受理超时):展示错误信息