fix(v3): smart_video_router — remove hardcoded path, on_failure validates availability
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
load_dotenv(dotenv_path='D:/key/blog-writer.env.env')
|
load_dotenv() # load .env from current directory or parents
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).parent.parent.parent
|
BASE_DIR = Path(__file__).parent.parent.parent
|
||||||
LOG_DIR = BASE_DIR / 'logs'
|
LOG_DIR = BASE_DIR / 'logs'
|
||||||
@@ -76,6 +76,7 @@ class SmartVideoRouter:
|
|||||||
self.fallback_engine: str = router_cfg.get('fallback', 'ffmpeg_slides')
|
self.fallback_engine: str = router_cfg.get('fallback', 'ffmpeg_slides')
|
||||||
|
|
||||||
self.engine_opts: dict = opts # all engine option blocks
|
self.engine_opts: dict = opts # all engine option blocks
|
||||||
|
self._cfg: dict = video_cfg # full video_generation config block
|
||||||
self.state: dict = self._get_state()
|
self.state: dict = self._get_state()
|
||||||
|
|
||||||
# ── State management ────────────────────────────────────
|
# ── State management ────────────────────────────────────
|
||||||
@@ -113,9 +114,15 @@ class SmartVideoRouter:
|
|||||||
|
|
||||||
# ── Engine availability checks ───────────────────────────
|
# ── Engine availability checks ───────────────────────────
|
||||||
|
|
||||||
def _has_api_key(self, engine_name: str) -> bool:
|
def _has_api_key(self, engine_name_or_cfg) -> bool:
|
||||||
"""Return True if the engine's API key env var is set and non-empty."""
|
"""Return True if the engine's API key env var is set and non-empty.
|
||||||
cfg = self.engine_opts.get(engine_name, {})
|
|
||||||
|
Accepts either an engine name string or a dict with 'api_key_env' key.
|
||||||
|
"""
|
||||||
|
if isinstance(engine_name_or_cfg, dict):
|
||||||
|
cfg = engine_name_or_cfg
|
||||||
|
else:
|
||||||
|
cfg = self.engine_opts.get(engine_name_or_cfg, {})
|
||||||
key_env = cfg.get('api_key_env', '')
|
key_env = cfg.get('api_key_env', '')
|
||||||
if not key_env:
|
if not key_env:
|
||||||
# ffmpeg_slides has no API key requirement
|
# ffmpeg_slides has no API key requirement
|
||||||
@@ -209,22 +216,30 @@ class SmartVideoRouter:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def on_failure(self, engine: str, error: str) -> str:
|
def on_failure(self, engine: str, error: str) -> str:
|
||||||
"""
|
"""Called when engine fails. Returns next available engine."""
|
||||||
Called when an engine fails mid-generation.
|
logger.warning(f"[영상] 엔진 실패: {engine} — {error}")
|
||||||
Returns next engine to try, or 'ffmpeg_slides' as final fallback.
|
|
||||||
"""
|
priority = self._cfg.get('options', {}).get('smart_router', {}).get(
|
||||||
logger.warning(f"엔진 실패 처리: {engine} — {error}")
|
'priority', ['kling_free', 'veo3', 'seedance2', 'ffmpeg_slides']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Find next available engine after the failed one
|
||||||
try:
|
try:
|
||||||
idx = self.priority.index(engine)
|
idx = priority.index(engine)
|
||||||
next_engines = self.priority[idx + 1:]
|
candidates = priority[idx + 1:]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
next_engines = []
|
candidates = priority
|
||||||
|
|
||||||
for candidate in next_engines:
|
for candidate in candidates:
|
||||||
logger.info(f"다음 엔진 시도: {candidate}")
|
if candidate == 'ffmpeg_slides':
|
||||||
return candidate
|
return 'ffmpeg_slides' # always available
|
||||||
|
engine_opts = self._cfg.get('options', {}).get(candidate, {})
|
||||||
|
api_key_env = engine_opts.get('api_key_env', '')
|
||||||
|
if self._has_api_key({'api_key_env': api_key_env}):
|
||||||
|
logger.info(f"[영상] 다음 엔진으로 전환: {candidate}")
|
||||||
|
return candidate
|
||||||
|
|
||||||
logger.info("모든 엔진 소진 — ffmpeg_slides 최종 폴백")
|
logger.warning("[영상] 사용 가능한 엔진 없음 — ffmpeg_slides로 폴백")
|
||||||
return 'ffmpeg_slides'
|
return 'ffmpeg_slides'
|
||||||
|
|
||||||
# ── Engine implementations ────────────────────────────────
|
# ── Engine implementations ────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user