fix(brain): ride out transient Claude 529 overloads
A single Claude 529 Overloaded dropped the voice turn straight to the apology
fallback. The anthropic SDK retries >=500/429 but only twice by default, which
a busy window can outlast. Raise max_retries (WSAI_BRAIN_MAX_RETRIES, default 4)
so transient overloads recover silently, and give overloads their own spoken
fallback ("서버가 붐벼서...") distinct from generic failures. Kept modest so a
sustained outage still fails fast instead of leaving the bot silent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,13 @@ from ..interfaces import Frame, Reply, ScreenObservation
|
|||||||
# this identity string; the real persona/instructions go in later blocks.
|
# this identity string; the real persona/instructions go in later blocks.
|
||||||
_CLAUDE_CODE_ID = "You are Claude Code, Anthropic's official CLI for Claude."
|
_CLAUDE_CODE_ID = "You are Claude Code, Anthropic's official CLI for Claude."
|
||||||
|
|
||||||
|
# Claude occasionally returns 529 Overloaded; the anthropic SDK retries >=500
|
||||||
|
# (and 429) with exponential backoff, but its default of 2 tries can be too few
|
||||||
|
# to ride out a busy window. Bump it so a transient overload doesn't drop the
|
||||||
|
# voice turn to the apology fallback. Kept modest so a *sustained* overload
|
||||||
|
# still fails fast rather than leaving the bot silent for many seconds.
|
||||||
|
_MAX_RETRIES = int(os.environ.get("WSAI_BRAIN_MAX_RETRIES", "4"))
|
||||||
|
|
||||||
|
|
||||||
def _load_oauth_token() -> str | None:
|
def _load_oauth_token() -> str | None:
|
||||||
path = os.environ.get("CLAUDE_CREDENTIALS_PATH")
|
path = os.environ.get("CLAUDE_CREDENTIALS_PATH")
|
||||||
@@ -59,7 +66,7 @@ class _Auth:
|
|||||||
key = self._explicit_key or os.environ.get("ANTHROPIC_API_KEY")
|
key = self._explicit_key or os.environ.get("ANTHROPIC_API_KEY")
|
||||||
if key:
|
if key:
|
||||||
if self._client is None:
|
if self._client is None:
|
||||||
self._client = self._anthropic.AsyncAnthropic(api_key=key)
|
self._client = self._anthropic.AsyncAnthropic(api_key=key, max_retries=_MAX_RETRIES)
|
||||||
self._oauth = False
|
self._oauth = False
|
||||||
return self._client
|
return self._client
|
||||||
tok = _load_oauth_token()
|
tok = _load_oauth_token()
|
||||||
@@ -70,7 +77,7 @@ class _Auth:
|
|||||||
)
|
)
|
||||||
if tok != self._token:
|
if tok != self._token:
|
||||||
self._token = tok
|
self._token = tok
|
||||||
self._client = self._anthropic.AsyncAnthropic(auth_token=tok)
|
self._client = self._anthropic.AsyncAnthropic(auth_token=tok, max_retries=_MAX_RETRIES)
|
||||||
self._oauth = True
|
self._oauth = True
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
|
|||||||
@@ -304,6 +304,10 @@ class Dashboard:
|
|||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
log.exception("brain failed")
|
log.exception("brain failed")
|
||||||
self.monitor.log("error", f"두뇌 응답 실패: {exc}")
|
self.monitor.log("error", f"두뇌 응답 실패: {exc}")
|
||||||
|
blob = f"{getattr(exc, 'status_code', '')} {exc}".lower()
|
||||||
|
if "529" in blob or "overload" in blob:
|
||||||
|
# Transient server overload survived the SDK retries.
|
||||||
|
return "지금 서버가 잠깐 붐벼서 생각이 늦네. 잠시 뒤에 다시 말해줄래?"
|
||||||
return "미안, 지금 잠깐 생각이 안 났어. 다시 말해줄래?"
|
return "미안, 지금 잠깐 생각이 안 났어. 다시 말해줄래?"
|
||||||
if not text:
|
if not text:
|
||||||
return "음, 뭐라고 해야 할지 모르겠어. 다시 말해줄래?"
|
return "음, 뭐라고 해야 할지 모르겠어. 다시 말해줄래?"
|
||||||
|
|||||||
Reference in New Issue
Block a user