This commit is contained in:
@@ -315,6 +315,7 @@ RAG_EMBED_NORMALIZE=true
|
||||
PPT_VISION_ENABLED=true
|
||||
PPT_VISION_MODEL=minicpm-v:latest
|
||||
PPT_VISION_TIMEOUT=120
|
||||
PPT_VISION_MAX_SLIDES=1
|
||||
PPT_AUTO_GENERATION_ENABLED=true
|
||||
PPT_AUTO_REPORT_TYPES=all
|
||||
PPT_AUTO_DEFAULT_CATEGORY=美妝保養
|
||||
|
||||
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.232"
|
||||
SYSTEM_VERSION = "V10.233"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ logger = logging.getLogger(__name__)
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
PPT_VISION_MODEL = os.getenv('PPT_VISION_MODEL', 'minicpm-v:latest')
|
||||
PPT_VISION_TIMEOUT = int(os.getenv('PPT_VISION_TIMEOUT', '120'))
|
||||
PPT_VISION_MAX_SLIDES = int(os.getenv('PPT_VISION_MAX_SLIDES', '1'))
|
||||
PPT_VISION_IMAGE_MAX_EDGE = int(os.getenv('PPT_VISION_IMAGE_MAX_EDGE', '1280'))
|
||||
PPT_VISION_IMAGE_QUALITY = int(os.getenv('PPT_VISION_IMAGE_QUALITY', '82'))
|
||||
_AUDIT_LOCK = threading.Lock()
|
||||
@@ -182,6 +183,16 @@ def _is_recent_active_audit_run(run: Dict[str, Any] | None) -> bool:
|
||||
return age is None or age < _ACTIVE_AUDIT_TTL_SECONDS
|
||||
|
||||
|
||||
def _is_vision_infra_error(error: str | None) -> bool:
|
||||
text = (error or '').lower()
|
||||
return any(marker in text for marker in (
|
||||
'all 3 hosts failed',
|
||||
'connection',
|
||||
'ollama vision failed',
|
||||
'timeout',
|
||||
))
|
||||
|
||||
|
||||
def _public_audit_run_payload(run: Dict[str, Any] | None) -> Dict[str, Any] | None:
|
||||
if not run:
|
||||
return None
|
||||
@@ -316,7 +327,7 @@ class PPTVisionService:
|
||||
with open(image_path, 'rb') as f:
|
||||
return base64.b64encode(f.read()).decode('ascii')
|
||||
|
||||
def check_ppt_file(self, pptx_path: str, max_slides: int = 5) -> Dict[str, Any]:
|
||||
def check_ppt_file(self, pptx_path: str, max_slides: int | None = None) -> Dict[str, Any]:
|
||||
"""檢查整份 .pptx — Phase 26 整合到 PPT 生成流程。
|
||||
|
||||
流程:
|
||||
@@ -338,6 +349,7 @@ class PPTVisionService:
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
max_slides = max(1, int(max_slides or PPT_VISION_MAX_SLIDES))
|
||||
result = {
|
||||
'success': False, 'slides_checked': 0, 'total_issues': 0,
|
||||
'issues_by_slide': [], 'error': None,
|
||||
@@ -424,11 +436,16 @@ class PPTVisionService:
|
||||
result['total_issues'] += len(vr.issues_found)
|
||||
result['issues_by_slide'].append((idx + 1, vr.issues_found))
|
||||
else:
|
||||
slide_errors.append(f"slide {idx + 1}: {vr.error or 'vision model failed'}")
|
||||
message = f"slide {idx + 1}: {vr.error or 'vision model failed'}"
|
||||
slide_errors.append(message)
|
||||
if _is_vision_infra_error(vr.error):
|
||||
break
|
||||
except Exception as exc:
|
||||
message = f"slide {idx + 1}: {type(exc).__name__}: {str(exc)[:160]}"
|
||||
slide_errors.append(message)
|
||||
logger.warning(f"[PPTVision] slide {idx+1} check failed: {exc}")
|
||||
if _is_vision_infra_error(message):
|
||||
break
|
||||
|
||||
result['success'] = result['slides_checked'] > 0
|
||||
if not result['success'] and slide_errors:
|
||||
|
||||
Reference in New Issue
Block a user