fix(core): catch bare openai.APIError in handle_llm_error (#9468)
* fix(core): catch bare openai.APIError in handle_llm_error fallthrough
openai.APIError raised during streaming (e.g. OpenRouter credit
exhaustion) is not an APIStatusError, so it skipped the catch-all
at the end and fell through to LLMError("Unhandled"). Now bare
APIErrors that aren't context window overflows are mapped to
LLMBadRequestError.
Datadog: https://us5.datadoghq.com/error-tracking/issue/7a2c356c-0849-11f1-be66-da7ad0900000
🐾 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
* feat(core): add LLMInsufficientCreditsError for BYOK credit exhaustion
Adds dedicated error type for insufficient credits/quota across all
providers (OpenAI, Anthropic, Google). Returns HTTP 402 with
BYOK-aware messaging instead of generic 400.
- New LLMInsufficientCreditsError class and PAYMENT_REQUIRED ErrorCode
- is_insufficient_credits_message() helper detecting credit/quota strings
- All 3 provider clients detect 402 status + credit keywords
- FastAPI handler returns 402 with "your API key" vs generic messaging
- 5 new parametrized tests covering OpenRouter, OpenAI, and negative case
🐾 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
---------
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -54,6 +54,7 @@ from letta.errors import (
|
||||
LLMAuthenticationError,
|
||||
LLMBadRequestError,
|
||||
LLMError,
|
||||
LLMInsufficientCreditsError,
|
||||
LLMProviderOverloaded,
|
||||
LLMRateLimitError,
|
||||
LLMTimeoutError,
|
||||
@@ -705,6 +706,24 @@ def create_application() -> "FastAPI":
|
||||
},
|
||||
)
|
||||
|
||||
@app.exception_handler(LLMInsufficientCreditsError)
|
||||
async def llm_insufficient_credits_handler(request: Request, exc: LLMInsufficientCreditsError):
|
||||
is_byok = exc.details.get("is_byok") if isinstance(exc.details, dict) else None
|
||||
if is_byok:
|
||||
message = "Insufficient credits on your API key. Please add credits with your LLM provider."
|
||||
else:
|
||||
message = "Insufficient credits for LLM request. Please check your account."
|
||||
return JSONResponse(
|
||||
status_code=402,
|
||||
content={
|
||||
"error": {
|
||||
"type": "llm_insufficient_credits",
|
||||
"message": message,
|
||||
"detail": str(exc),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@app.exception_handler(LLMAuthenticationError)
|
||||
async def llm_auth_error_handler(request: Request, exc: LLMAuthenticationError):
|
||||
return JSONResponse(
|
||||
|
||||
Reference in New Issue
Block a user