mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
fix(billing): Add error handling for LiteLLM API failures in get_credits (#12201)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -111,10 +111,24 @@ def calculate_credits(user_info: LiteLlmUserInfo) -> float:
|
|||||||
async def get_credits(user_id: str = Depends(get_user_id)) -> GetCreditsResponse:
|
async def get_credits(user_id: str = Depends(get_user_id)) -> GetCreditsResponse:
|
||||||
if not stripe_service.STRIPE_API_KEY:
|
if not stripe_service.STRIPE_API_KEY:
|
||||||
return GetCreditsResponse()
|
return GetCreditsResponse()
|
||||||
async with httpx.AsyncClient(verify=httpx_verify_option()) as client:
|
try:
|
||||||
user_json = await _get_litellm_user(client, user_id)
|
async with httpx.AsyncClient(verify=httpx_verify_option()) as client:
|
||||||
credits = calculate_credits(user_json['user_info'])
|
user_json = await _get_litellm_user(client, user_id)
|
||||||
return GetCreditsResponse(credits=Decimal('{:.2f}'.format(credits)))
|
credits = calculate_credits(user_json['user_info'])
|
||||||
|
return GetCreditsResponse(credits=Decimal('{:.2f}'.format(credits)))
|
||||||
|
except httpx.HTTPStatusError as e:
|
||||||
|
logger.error(
|
||||||
|
f'litellm_get_user_failed: {type(e).__name__}: {e}',
|
||||||
|
extra={
|
||||||
|
'user_id': user_id,
|
||||||
|
'status_code': e.response.status_code,
|
||||||
|
},
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail='Failed to retrieve credit balance from billing service',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Endpoint to retrieve user's current subscription access
|
# Endpoint to retrieve user's current subscription access
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|||||||
import pytest
|
import pytest
|
||||||
import stripe
|
import stripe
|
||||||
from fastapi import HTTPException, Request, status
|
from fastapi import HTTPException, Request, status
|
||||||
from httpx import HTTPStatusError, Response
|
from httpx import Response
|
||||||
from integrations.stripe_service import has_payment_method
|
from integrations.stripe_service import has_payment_method
|
||||||
from server.routes.billing import (
|
from server.routes.billing import (
|
||||||
CreateBillingSessionResponse,
|
CreateBillingSessionResponse,
|
||||||
@@ -78,8 +78,6 @@ def mock_subscription_request():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_credits_lite_llm_error():
|
async def test_get_credits_lite_llm_error():
|
||||||
mock_request = Request(scope={'type': 'http', 'state': {'user_id': 'mock_user'}})
|
|
||||||
|
|
||||||
mock_response = Response(
|
mock_response = Response(
|
||||||
status_code=500, json={'error': 'Internal Server Error'}, request=MagicMock()
|
status_code=500, json={'error': 'Internal Server Error'}, request=MagicMock()
|
||||||
)
|
)
|
||||||
@@ -88,11 +86,12 @@ async def test_get_credits_lite_llm_error():
|
|||||||
|
|
||||||
with patch('integrations.stripe_service.STRIPE_API_KEY', 'mock_key'):
|
with patch('integrations.stripe_service.STRIPE_API_KEY', 'mock_key'):
|
||||||
with patch('httpx.AsyncClient', return_value=mock_client):
|
with patch('httpx.AsyncClient', return_value=mock_client):
|
||||||
with pytest.raises(HTTPStatusError) as exc_info:
|
with pytest.raises(HTTPException) as exc_info:
|
||||||
await get_credits(mock_request)
|
await get_credits('mock_user')
|
||||||
|
assert exc_info.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
assert (
|
assert (
|
||||||
exc_info.value.response.status_code
|
exc_info.value.detail
|
||||||
== status.HTTP_500_INTERNAL_SERVER_ERROR
|
== 'Failed to retrieve credit balance from billing service'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user