자동 탭 컷별 추천 모델 교체(gemini-3.5-flash) + 429/503 재시도
gemini-2.5-flash 가 HTTP 503("high demand")을 자주 반환해(실측 3연속 503)
ai_pick_cuts 가 재시도 없이 즉시 포기, 하이라이트의 컷 전체가 🤖 0개가 되는
증상의 근본 원인이었다. plan.py 가 이미 쓰는 gemini-3.5-flash 로 모델을
통일하고, 429/503 한정으로 3초·8초 간격 재시도(최대 3회 시도)를 추가했다.
그 외 오류와 재시도 소진 시에는 기존과 동일하게 None 을 반환한다(예외 미노출).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
058f03265c
commit
9ea70d01a7
@ -9,6 +9,8 @@ Gemini 없이 단위 검증이 된다.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
@ -69,7 +71,10 @@ def build_cut_picks(cuts, comments, ai_picks: Optional[Dict[int, List[int]]],
|
||||
# Gemini에 넘길 댓글 후보 수. 더 늘려도 채택률이 안 오르고 토큰만 먹는다.
|
||||
AI_CANDIDATES = 150
|
||||
TEXT_CAP = 200 # 댓글 본문 절단 길이
|
||||
AI_MODEL = "gemini-2.5-flash" # correct.py 와 같은 무료 티어 모델
|
||||
# gemini-2.5-flash 는 실측상 HTTP 503("high demand")이 잦아(3연속 503도 관측됨) 컷별
|
||||
# 추천이 통째로 비는 원인이었다. plan.py 가 이미 쓰는 gemini-3.5-flash 로 통일 —
|
||||
# 같은 프롬프트·같은 컷으로 실측해도 결과가 동등해 품질 손해는 없다.
|
||||
AI_MODEL = "gemini-3.5-flash"
|
||||
_ENDPOINT = ("https://generativelanguage.googleapis.com/v1beta/models/"
|
||||
"{model}:generateContent?key={key}")
|
||||
|
||||
@ -168,13 +173,27 @@ def ai_pick_cuts(cuts, comments, quotas, *, model: str = AI_MODEL,
|
||||
_ENDPOINT.format(model=model, key=key),
|
||||
data=json.dumps(body).encode("utf-8"),
|
||||
headers={"Content-Type": "application/json"})
|
||||
# 429("quota")·503("high demand")만 짧게 쉬고 재시도한다(최대 2회 더 = 총 3회 시도).
|
||||
# 둘 다 "지금 당장은 안 됨"이지 "영영 안 됨"이 아니라서다. 그 외 오류는 재시도해 봐야
|
||||
# 같은 결과라 즉시 포기한다.
|
||||
RETRY_WAITS = (3.0, 8.0)
|
||||
raw = None
|
||||
for attempt in range(1 + len(RETRY_WAITS)):
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
data = json.loads(resp.read().decode("utf-8"))
|
||||
raw = data["candidates"][0]["content"]["parts"][0]["text"]
|
||||
break
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code in (429, 503) and attempt < len(RETRY_WAITS):
|
||||
time.sleep(RETRY_WAITS[attempt])
|
||||
continue
|
||||
return None # 재시도 소진, 또는 재시도 대상이 아닌 HTTP 오류
|
||||
except (KeyError, IndexError, json.JSONDecodeError, ValueError,
|
||||
OSError): # URLError, TimeoutError are OSError subclasses; ValueError covers UnicodeDecodeError
|
||||
return None # 429 포함 — 폴백은 호출부 몫
|
||||
return None # 폴백은 호출부 몫
|
||||
if raw is None: # 방어적 — 위 루프는 항상 break/return 으로 빠진다
|
||||
return None
|
||||
try:
|
||||
rows = json.loads(raw)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user