This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.218 補 `/observability/ppt_audit_history` AiderHeal 去重鎖:同一份簡報已在背景修復時,再次點擊會回「已在執行中」,避免重複開 SSH / 模型 / git 修復流程。
|
||||
- V10.217 讓 `/observability/ppt_audit_history` 的 AiderHeal 派工改為非阻塞背景任務:頁面立即回「已排入」,修復工作在背景執行,避免瀏覽器與 Gunicorn worker 等 SSH、模型與 git push 到超時。
|
||||
- V10.216 修正 `/observability/ppt_audit_history` 的 AiderHeal 派工斷點:失敗簡報即使只有 `issues_found` 診斷摘要也能一鍵送修,並修正 `execute_code_fix` 參數與 dict 回傳解析,避免按鈕 400/500。
|
||||
- V10.215 強化 `/observability/ppt_audit_history` 視覺問題追蹤:將 `issues_found` 拆成投影片、問題類型、問題文字與回放入口,新增「視覺問題追蹤」面板,讓問題簡報能直接定位與預覽。
|
||||
|
||||
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.217"
|
||||
SYSTEM_VERSION = "V10.218"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ admin_observability_bp = Blueprint(
|
||||
url_prefix='/observability',
|
||||
)
|
||||
|
||||
_PPT_AIDER_HEAL_LOCK = threading.Lock()
|
||||
_PPT_AIDER_HEAL_ACTIVE = set()
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# /observability/overview — Phase 45 總覽(單頁聚合 6 項 KPI)
|
||||
@@ -1835,6 +1838,17 @@ def ppt_audit_trigger_aider_heal():
|
||||
'triggered_by': 'admin_observability',
|
||||
'issue_summary': issue_summary[:500],
|
||||
}
|
||||
heal_key = pptx_filename or diagnosis[:160] or 'manual'
|
||||
with _PPT_AIDER_HEAL_LOCK:
|
||||
if heal_key in _PPT_AIDER_HEAL_ACTIVE:
|
||||
return jsonify({
|
||||
'ok': True,
|
||||
'status': 'already_running',
|
||||
'action': 'CODE_FIX',
|
||||
'message': '這份簡報的 AiderHeal 已在背景執行中,請等 Telegram/Gitea/CD 結果回報。',
|
||||
'target_file': 'services/ppt_generator.py',
|
||||
}), 202
|
||||
_PPT_AIDER_HEAL_ACTIVE.add(heal_key)
|
||||
|
||||
def _heal_worker():
|
||||
try:
|
||||
@@ -1855,6 +1869,9 @@ def ppt_audit_trigger_aider_heal():
|
||||
"[PPTAudit] AiderHeal 背景任務失敗 | file=%s",
|
||||
pptx_filename or '-',
|
||||
)
|
||||
finally:
|
||||
with _PPT_AIDER_HEAL_LOCK:
|
||||
_PPT_AIDER_HEAL_ACTIVE.discard(heal_key)
|
||||
|
||||
thread_key = ''.join(ch for ch in pptx_filename if ch.isalnum())[:24] or 'manual'
|
||||
threading.Thread(
|
||||
|
||||
@@ -565,6 +565,51 @@ def test_ppt_audit_trigger_aider_heal_accepts_issue_summary(client, monkeypatch)
|
||||
assert captured['context']['issue_summary'] == 'S1: 圖表被切掉:右側圖例超出邊界'
|
||||
|
||||
|
||||
def test_ppt_audit_trigger_aider_heal_dedupes_same_file(client, monkeypatch):
|
||||
"""同一份 PPT 已在背景修復時,重複按鈕不應重開第二條 AiderHeal。"""
|
||||
from routes import admin_observability_routes as mod
|
||||
from services import aider_heal_executor as svc
|
||||
|
||||
calls = []
|
||||
|
||||
def fake_execute_code_fix(**kwargs):
|
||||
calls.append(kwargs)
|
||||
return {
|
||||
'success': True,
|
||||
'action': 'CODE_FIX',
|
||||
'message': '不應在此測試執行',
|
||||
'commit_sha': None,
|
||||
'reverted': False,
|
||||
}
|
||||
|
||||
class HoldingThread:
|
||||
def __init__(self, target, **_kwargs):
|
||||
self.target = target
|
||||
|
||||
def start(self):
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(svc, 'execute_code_fix', fake_execute_code_fix)
|
||||
monkeypatch.setattr(mod.threading, 'Thread', HoldingThread)
|
||||
mod._PPT_AIDER_HEAL_ACTIVE.clear()
|
||||
|
||||
payload = {
|
||||
'pptx_filename': 'ocbot_daily_20260517.pptx',
|
||||
'issue_summary': 'S1: 圖表被切掉:右側圖例超出邊界',
|
||||
}
|
||||
first = client.post('/observability/ppt_audit/trigger_aider_heal', json=payload)
|
||||
second = client.post('/observability/ppt_audit/trigger_aider_heal', json=payload)
|
||||
|
||||
try:
|
||||
assert first.status_code == 202
|
||||
assert first.get_json()['status'] == 'queued'
|
||||
assert second.status_code == 202
|
||||
assert second.get_json()['status'] == 'already_running'
|
||||
assert calls == []
|
||||
finally:
|
||||
mod._PPT_AIDER_HEAL_ACTIVE.clear()
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# /observability/host_health
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -596,7 +596,9 @@
|
||||
throw new Error(data.error || data.message || '觸發失敗');
|
||||
}
|
||||
if (triggerButton) {
|
||||
triggerButton.innerHTML = '<i class="fas fa-check me-1"></i>已排入';
|
||||
triggerButton.innerHTML = data.status === 'already_running'
|
||||
? '<i class="fas fa-clock me-1"></i>執行中'
|
||||
: '<i class="fas fa-check me-1"></i>已排入';
|
||||
}
|
||||
if (statusNode) {
|
||||
statusNode.classList.remove('is-working');
|
||||
|
||||
Reference in New Issue
Block a user