Spaces:
Running
Running
Phase 4:Bug3主體——步驟列補上「失敗」視覺狀態
Browse files- make_initial_state 新增 failed_step 欄位(1-based,成功時維持 None)
- _build_header_html 新增 failed_step 參數,該步驟改用紅色警示樣式,
不再套用「進行中」的呼吸動畫(原本失敗時圓點會永遠卡在動畫)
- run_computation_pipeline_streaming 的指標計算/統計分析例外分支
分別標記 failed_step=5/6,錯誤訊息補上「請重新整理頁面重新開始」
的下一步指引(完整的重置機制留給 Phase 6 一併處理)
- 新增 .step-pill--failed CSS 樣式
已用直接呼叫 _build_header_html(failed_step=5) 驗證渲染邏輯正確;
Phase 3 的驗證強化讓最容易觸發此失敗路徑的成因(分組不完整)已被攔在
格式驗證階段,因此另外完整跑一次情境1 happy path 確認沒有回歸。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- app_agent.py +29 -4
app_agent.py
CHANGED
|
@@ -1370,6 +1370,7 @@ def make_initial_state(session_id: str) -> dict:
|
|
| 1370 |
"analysis_result": None,
|
| 1371 |
"done_steps": [], # 已完成步驟(1-based)
|
| 1372 |
"current_step": None, # 目前執行步驟
|
|
|
|
| 1373 |
}
|
| 1374 |
|
| 1375 |
|
|
@@ -1386,18 +1387,27 @@ _STEPS = [
|
|
| 1386 |
]
|
| 1387 |
|
| 1388 |
|
| 1389 |
-
def _build_header_html(done_steps: list = None, current_step: int = None
|
|
|
|
| 1390 |
"""
|
| 1391 |
產生完整標題區塊 HTML(標題+動態步驟列+進度條),確定性、不依賴 LLM。
|
| 1392 |
取代原本寫死不會變的步驟列,以及原本另外放在右欄、跟這裡資訊重複的
|
| 1393 |
「執行進度」面板——進度資訊現在只在這裡出現一次。
|
| 1394 |
done_steps: 已完成步驟(1-based list)
|
| 1395 |
current_step: 目前執行步驟(1-based int)
|
|
|
|
|
|
|
|
|
|
| 1396 |
"""
|
| 1397 |
done_steps = done_steps or []
|
| 1398 |
pills = []
|
| 1399 |
for num, name in _STEPS:
|
| 1400 |
-
if num
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1401 |
pills.append(
|
| 1402 |
f'<span class="step-pill step-pill--done">'
|
| 1403 |
f'<span class="step-num">✓</span>{name}</span>'
|
|
@@ -1588,7 +1598,8 @@ def run_computation_pipeline_streaming(state: dict):
|
|
| 1588 |
yield f" [OK] 指標計算完成({len(idx_df)} 個文本)", False, state
|
| 1589 |
except Exception as e:
|
| 1590 |
state["state"] = "COMPLETE"
|
| 1591 |
-
|
|
|
|
| 1592 |
return
|
| 1593 |
|
| 1594 |
# ── 步驟 2:統計分析 ─────────────────────────────────────────────────────
|
|
@@ -1612,7 +1623,8 @@ def run_computation_pipeline_streaming(state: dict):
|
|
| 1612 |
except Exception as e:
|
| 1613 |
state["state"] = "COMPLETE"
|
| 1614 |
state["output_files"] = results_paths
|
| 1615 |
-
|
|
|
|
| 1616 |
return
|
| 1617 |
|
| 1618 |
# ── 步驟 3:LLM 研究報告 ────────────────────────────────────────────────
|
|
@@ -1935,6 +1947,18 @@ _CUSTOM_CSS = """
|
|
| 1935 |
0%, 100% { box-shadow: 0 0 0 0 rgba(var(--c-accent-rgb),.35); }
|
| 1936 |
50% { box-shadow: 0 0 0 5px rgba(var(--c-accent-rgb),0); }
|
| 1937 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1938 |
/* 步驟列下方的整體進度條,取代原本另外放在右欄、跟步驟列資訊重複的執行進度面板 */
|
| 1939 |
.step-progress-track {
|
| 1940 |
height: 4px;
|
|
@@ -3159,6 +3183,7 @@ def create_app():
|
|
| 3159 |
value=_build_header_html(
|
| 3160 |
state.get("done_steps", []),
|
| 3161 |
state.get("current_step"),
|
|
|
|
| 3162 |
)
|
| 3163 |
)
|
| 3164 |
|
|
|
|
| 1370 |
"analysis_result": None,
|
| 1371 |
"done_steps": [], # 已完成步驟(1-based)
|
| 1372 |
"current_step": None, # 目前執行步驟
|
| 1373 |
+
"failed_step": None, # 執行失敗時卡在哪一步(1-based),成功時維持 None
|
| 1374 |
}
|
| 1375 |
|
| 1376 |
|
|
|
|
| 1387 |
]
|
| 1388 |
|
| 1389 |
|
| 1390 |
+
def _build_header_html(done_steps: list = None, current_step: int = None,
|
| 1391 |
+
failed_step: int = None) -> str:
|
| 1392 |
"""
|
| 1393 |
產生完整標題區塊 HTML(標題+動態步驟列+進度條),確定性、不依賴 LLM。
|
| 1394 |
取代原本寫死不會變的步驟列,以及原本另外放在右欄、跟這裡資訊重複的
|
| 1395 |
「執行進度」面板——進度資訊現在只在這裡出現一次。
|
| 1396 |
done_steps: 已完成步驟(1-based list)
|
| 1397 |
current_step: 目前執行步驟(1-based int)
|
| 1398 |
+
failed_step: 執行失敗時卡住的步驟(1-based int),失敗時該步驟改用失敗樣式,
|
| 1399 |
+
不再顯示「進行中」的呼吸動畫(原本失敗時圓點會永遠卡在動畫,
|
| 1400 |
+
使用者容易誤以為系統還在運算而手滑刷新頁面)
|
| 1401 |
"""
|
| 1402 |
done_steps = done_steps or []
|
| 1403 |
pills = []
|
| 1404 |
for num, name in _STEPS:
|
| 1405 |
+
if num == failed_step:
|
| 1406 |
+
pills.append(
|
| 1407 |
+
f'<span class="step-pill step-pill--failed">'
|
| 1408 |
+
f'<span class="step-num">!</span>{name}</span>'
|
| 1409 |
+
)
|
| 1410 |
+
elif num in done_steps:
|
| 1411 |
pills.append(
|
| 1412 |
f'<span class="step-pill step-pill--done">'
|
| 1413 |
f'<span class="step-num">✓</span>{name}</span>'
|
|
|
|
| 1598 |
yield f" [OK] 指標計算完成({len(idx_df)} 個文本)", False, state
|
| 1599 |
except Exception as e:
|
| 1600 |
state["state"] = "COMPLETE"
|
| 1601 |
+
state["failed_step"] = 5
|
| 1602 |
+
yield f"[ERROR] 指標計算失敗:{e}\n\n請重新整理頁面,重新開始分析。", True, state
|
| 1603 |
return
|
| 1604 |
|
| 1605 |
# ── 步驟 2:統計分析 ─────────────────────────────────────────────────────
|
|
|
|
| 1623 |
except Exception as e:
|
| 1624 |
state["state"] = "COMPLETE"
|
| 1625 |
state["output_files"] = results_paths
|
| 1626 |
+
state["failed_step"] = 6
|
| 1627 |
+
yield f"[ERROR] 統計分析失敗:{e}\n\n請重新整理頁面,重新開始分析。", True, state
|
| 1628 |
return
|
| 1629 |
|
| 1630 |
# ── 步驟 3:LLM 研究報告 ────────────────────────────────────────────────
|
|
|
|
| 1947 |
0%, 100% { box-shadow: 0 0 0 0 rgba(var(--c-accent-rgb),.35); }
|
| 1948 |
50% { box-shadow: 0 0 0 5px rgba(var(--c-accent-rgb),0); }
|
| 1949 |
}
|
| 1950 |
+
/* 執行失敗卡住的步驟:紅色警示樣式,且不套用 stepPulse 呼吸動畫——原本
|
| 1951 |
+
失敗時圓點會卡在「進行中」動畫永遠閃爍,讓使用者誤以為系統還在運算 */
|
| 1952 |
+
.step-pill--failed {
|
| 1953 |
+
background: rgba(220,38,38,.14);
|
| 1954 |
+
border-color: rgba(220,38,38,.4);
|
| 1955 |
+
color: #dc2626;
|
| 1956 |
+
font-weight: 700;
|
| 1957 |
+
}
|
| 1958 |
+
.step-pill--failed .step-num {
|
| 1959 |
+
background: #dc2626;
|
| 1960 |
+
color: #fff;
|
| 1961 |
+
}
|
| 1962 |
/* 步驟列下方的整體進度條,取代原本另外放在右欄、跟步驟列資訊重複的執行進度面板 */
|
| 1963 |
.step-progress-track {
|
| 1964 |
height: 4px;
|
|
|
|
| 3183 |
value=_build_header_html(
|
| 3184 |
state.get("done_steps", []),
|
| 3185 |
state.get("current_step"),
|
| 3186 |
+
state.get("failed_step"),
|
| 3187 |
)
|
| 3188 |
)
|
| 3189 |
|