補 PPT 視覺 QA stale recovery
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s

This commit is contained in:
OoO
2026-05-19 10:03:10 +08:00
parent 20d22b69ea
commit c383a37f3f
5 changed files with 96 additions and 1 deletions

View File

@@ -150,6 +150,7 @@ def _record_audit_run(run: Dict[str, Any]) -> Dict[str, Any]:
global _LAST_AUDIT_RUN
payload = dict(run)
payload['updated_at'] = payload.get('updated_at') or _now_label()
payload['pid'] = payload.get('pid') or os.getpid()
_LAST_AUDIT_RUN = payload
_write_persisted_audit_run(payload)
return payload
@@ -176,13 +177,40 @@ def _timestamp_age_seconds(value: str | None) -> float | None:
return None
def _pid_exists(pid: Any) -> bool:
try:
pid_int = int(pid or 0)
except Exception:
return False
if pid_int <= 0:
return False
try:
os.kill(pid_int, 0)
return True
except OSError:
return False
def _is_recent_active_audit_run(run: Dict[str, Any] | None) -> bool:
if not run or run.get('status') not in {'queued', 'running'}:
return False
if run.get('pid') and not _pid_exists(run.get('pid')):
return False
age = _timestamp_age_seconds(run.get('updated_at') or run.get('started_at') or run.get('queued_at'))
return age is None or age < _ACTIVE_AUDIT_TTL_SECONDS
def _mark_stale_audit_run(run: Dict[str, Any]) -> Dict[str, Any]:
payload = dict(run)
payload.update({
'ok': False,
'status': 'error',
'finished_at': payload.get('finished_at') or _now_label(),
'error': 'background worker no longer running; audit state marked stale',
})
return _record_audit_run(payload)
def _is_vision_infra_error(error: str | None) -> bool:
text = (error or '').lower()
return any(marker in text for marker in (
@@ -214,6 +242,7 @@ def _public_audit_run_payload(run: Dict[str, Any] | None) -> Dict[str, Any] | No
'started_at': run.get('started_at') or '',
'finished_at': run.get('finished_at') or '',
'updated_at': run.get('updated_at') or '',
'pid': run.get('pid') or None,
'filenames': [
os.path.basename(str(name))
for name in (run.get('filenames') or [])
@@ -235,6 +264,8 @@ def _public_audit_run_payload(run: Dict[str, Any] | None) -> Dict[str, Any] | No
def get_ppt_vision_audit_status() -> Dict[str, Any]:
"""Return the current/last background visual QA run without touching DB."""
raw_run = _load_last_audit_run()
if raw_run and raw_run.get('status') in {'queued', 'running'} and not _is_recent_active_audit_run(raw_run):
raw_run = _mark_stale_audit_run(raw_run)
running = _AUDIT_LOCK.locked() or _is_recent_active_audit_run(raw_run)
last_run = _public_audit_run_payload(raw_run)
if running: