fix: AI 控制台切页/刷新不丢——①EventSource onerror 不再误 endRun(断网/后台节流/瞬时抖动都会触发 onerror 而后端任务仍在跑;EventSource 自动重连 + 服务端队列保留积压事件,重连后补发,连接彻底关闭才提示可刷新恢复);②页面刷新恢复:GET /api/agent/run 增加 run_id/answer/error/history,前端 restoreAgentView 渲染历史轮次、running 时自动重订阅事件流、done/error 展示结果——切走再回来任务与对话都在
This commit is contained in:
+54
-2
@@ -19,6 +19,7 @@ function initAgentChat(){
|
||||
loadLiveDevices();
|
||||
loadAgentTargetDevices();
|
||||
startRunPoll();
|
||||
restoreAgentView();
|
||||
// 输入框快捷键
|
||||
const inp = document.getElementById('agent-input');
|
||||
inp.addEventListener('keydown', ev=>{
|
||||
@@ -29,6 +30,43 @@ function initAgentChat(){
|
||||
});
|
||||
}
|
||||
|
||||
// ================== 页面刷新/重进恢复 ==================
|
||||
// 刷新后聊天区是空的,但后端任务可能还在跑、历史还在——从这里恢复:
|
||||
// 1) 渲染会话历史(user/assistant 文本轮)
|
||||
// 2) 任务运行中 → 重新订阅事件流(服务端队列保留积压,重连后补发)
|
||||
function restoreAgentView(){
|
||||
const chat = document.getElementById('agent-chat');
|
||||
if(!chat) return;
|
||||
const hasContent = chat.children.length > 0 && !chat.querySelector('.agent-empty');
|
||||
if(hasContent) return; // 页面未刷新(切 Tab 回来 DOM 还在),无需恢复
|
||||
apiGet('/api/agent/run').then(r=>{
|
||||
if(!r||!r.ok) return;
|
||||
const hist = r.history || [];
|
||||
hist.forEach(h=>{
|
||||
const txt = (h.content || '').trim();
|
||||
if(!txt) return;
|
||||
if(h.role === 'user'){ addUserMsg(txt); }
|
||||
else{
|
||||
const div = newAssistantMsg();
|
||||
div.querySelector('.agent-text').textContent = txt;
|
||||
}
|
||||
});
|
||||
if(r.state === 'running' && r.run_id){
|
||||
const div = newAssistantMsg();
|
||||
div.querySelector('.agent-text').textContent = '↻ 已恢复连接,任务仍在后台运行(此前的内容可在上方查看)…';
|
||||
setRunning(true);
|
||||
listenStream(r.run_id);
|
||||
}else if(r.state === 'error' && r.error){
|
||||
const div = newAssistantMsg();
|
||||
div.querySelector('.agent-text').textContent = '⚠ ' + r.error;
|
||||
}else if(!hist.length && r.state === 'idle'){
|
||||
clearEmpty();
|
||||
document.getElementById('agent-chat').innerHTML =
|
||||
'<div class="agent-empty">👋 给 AI 下达指令…(先选 🎯 目标设备)</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ================== 配置 ==================
|
||||
function loadAgentConfig(){
|
||||
apiGet('/api/agent/config').then(r=>{
|
||||
@@ -284,8 +322,22 @@ function listenStream(runId){
|
||||
});
|
||||
|
||||
es.onerror = ()=>{
|
||||
// 事件流正常结束(done 后服务器关流)会触发一次 error——done 已处理则忽略
|
||||
if(_agentBusy) endRun();
|
||||
// 重要:不能在这里 endRun()——断网/后台标签页节流/服务端瞬时抖动都会触发
|
||||
// onerror,但后端任务仍在跑。EventSource 会自动重连,服务端事件队列保留
|
||||
// 积压(delta/step/done),重连成功后全部补发,界面无缝续上。
|
||||
// 只有连接彻底关闭(非自动重连态)且任务还在跑时才提示。
|
||||
if(_agentBusy && es.readyState === EventSource.CLOSED){
|
||||
const hint = document.getElementById('agent-target-hint');
|
||||
if(hint) hint.textContent = '⚠ 实时连接已中断,任务仍在后台运行——可刷新页面恢复查看';
|
||||
// 连接彻底失败时周期探测,恢复后重订阅
|
||||
setTimeout(()=>{
|
||||
if(!_agentStream && _agentBusy){
|
||||
apiGet('/api/agent/run').then(r=>{
|
||||
if(r&&r.ok&&r.state==='running'&&r.run_id) listenStream(r.run_id);
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -541,16 +541,24 @@ def agent_run():
|
||||
@bp.route("/api/agent/run", methods=["GET"])
|
||||
@admin_required
|
||||
def agent_run_status():
|
||||
"""当前 Agent 运行状态(多窗口/多人可见):idle / running + 任务摘要。
|
||||
"""当前 Agent 运行状态(多窗口/页面刷新恢复用):
|
||||
idle/running/done + run_id + 最终答案 + 会话历史。
|
||||
|
||||
前端轮询它同步「运行中」状态(换浏览器/他人启动的任务也能看到并停止)。
|
||||
前端刷新后据此:渲染历史轮次、running 时重新订阅事件流(服务端队列
|
||||
保留积压事件,重连后补发)、done 时展示最终答案。
|
||||
"""
|
||||
with _lock:
|
||||
hist = [{"role": h.get("role"), "content": (h.get("content") or "")[:4000]}
|
||||
for h in (_run.get("history") or [])]
|
||||
return jsonify({"ok": True,
|
||||
"state": _run.get("state", "idle"),
|
||||
"run_id": _run.get("id") or "",
|
||||
"prompt": _run.get("prompt", ""),
|
||||
"serial": _run.get("serial", ""),
|
||||
"started": _run.get("started", "")})
|
||||
"started": _run.get("started", ""),
|
||||
"answer": (_run.get("answer") or "")[:4000],
|
||||
"error": (_run.get("error") or "")[:400],
|
||||
"history": hist[-16:]})
|
||||
|
||||
|
||||
@bp.route("/api/agent/devices")
|
||||
|
||||
Reference in New Issue
Block a user