mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
test(lint): ban blind pytest.raises(Exception) with ruff B017 (#37731)
* test(lint): ban blind pytest.raises(Exception) with ruff B017 A bare pytest.raises(Exception) accepts whatever the body throws. The TypeError a refactor introduces satisfies it exactly as well as the rejection the test was written for, so the crash reads as a pass and the test never goes red. All 111 existing sites are narrowed here. A runtime probe recorded the concrete exception each one actually catches, and each site now names that type. Where the code under test genuinely raises a bare Exception, the site pins a stable slice of the message with match= instead. Two sites tell on themselves. The shared responses-API cancel test raises "custom_llm_provider is required but passed as None" rather than talking to a provider at all, because cancel_responses takes a provider, not a model. And test_bedrock_guardrails_with_streaming was the only test in its file still passing without AWS credentials, because the NoCredentialsError boto3 raised long before the guardrail ran satisfied the blind raises. * fix(test): widen the openai batch-dispatch assertion to OpenAIError The narrowed NotFoundError only holds where OPENAI_API_KEY is set. Without one the SDK raises OpenAIError while building the client, long before any 404, so CI went red. OpenAIError covers both and still rejects a TypeError from a refactor.
This commit is contained in:
parent
6eacdbfbf0
commit
680bcfd8aa
58 changed files with 151 additions and 116 deletions
|
|
@ -14,6 +14,9 @@
|
|||
# B018 a bare attribute access or literal, usually a call missing its parens
|
||||
# PLW0127 `x = x` self-assignment, dead code that reads like a narrowing or a fixup
|
||||
# PLR0133 comparison of two constants, e.g. `assert True == True`
|
||||
# B017 `pytest.raises(Exception)` accepts the TypeError a refactor introduced just as
|
||||
# readily as the rejection under test, so a crash reads as a pass. Narrow to the
|
||||
# real type, or add `match=` where the code genuinely raises a bare Exception
|
||||
#
|
||||
# No target-version here on purpose: it resolves from requires-python (>=3.10), so
|
||||
# 3.11-only builtins like BaseExceptionGroup are correctly flagged in a tree that
|
||||
|
|
@ -21,4 +24,4 @@
|
|||
|
||||
line-length = 120
|
||||
|
||||
lint.select = ["F821", "B011", "B015", "B018", "PT015", "PLR0133", "PLW0127"]
|
||||
lint.select = ["F821", "B011", "B015", "B017", "B018", "PT015", "PLR0133", "PLW0127"]
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ async def test_bedrock_guardrails_block_responses_api():
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bedrock_guardrails_with_streaming():
|
||||
from fastapi import HTTPException
|
||||
from litellm.proxy.utils import ProxyLogging
|
||||
from litellm.types.guardrails import GuardrailEventHooks
|
||||
|
||||
|
|
@ -204,7 +205,7 @@ async def test_bedrock_guardrails_with_streaming():
|
|||
mock_user_api_key_cache = MagicMock(spec=DualCache)
|
||||
mock_user_api_key_dict = UserAPIKeyAuth()
|
||||
|
||||
with pytest.raises(Exception): # Assert that this raises an exception
|
||||
with pytest.raises(HTTPException):
|
||||
proxy_logging_obj = ProxyLogging(
|
||||
user_api_key_cache=mock_user_api_key_cache,
|
||||
premium_user=True,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_fil
|
|||
from litellm.types.proxy.guardrails.guardrail_hooks.litellm_content_filter import (
|
||||
ContentFilterCategoryConfig,
|
||||
)
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
# Test cases: (sentence, expected_result, reason)
|
||||
|
|
@ -275,7 +276,7 @@ class TestEUAIActEdgeCases:
|
|||
for sentence in sentences:
|
||||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await content_filter_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
@ -289,7 +290,7 @@ class TestEUAIActEdgeCases:
|
|||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
|
||||
# Should block (contains multiple violations)
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await content_filter_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_fil
|
|||
from litellm.types.proxy.guardrails.guardrail_hooks.litellm_content_filter import (
|
||||
ContentFilterCategoryConfig,
|
||||
)
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -228,7 +229,7 @@ class TestFrenchEdgeCases:
|
|||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
|
||||
# Should block (contains "build" and "système de crédit social")
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await content_filter_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
@ -257,7 +258,7 @@ class TestFrenchEdgeCases:
|
|||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
|
||||
# Should block (case-insensitive)
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await content_filter_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ sys.path.insert(0, os.path.abspath("../.."))
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
class TestRouteLoader:
|
||||
|
|
@ -307,7 +308,7 @@ class TestContentFilterSqlInjectionTemplate:
|
|||
@pytest.mark.asyncio
|
||||
async def test_sql_always_block(self, sql_injection_guardrail, sentence, reason):
|
||||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await sql_injection_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
@ -343,7 +344,7 @@ class TestContentFilterSqlInjectionTemplate:
|
|||
self, sql_injection_guardrail, sentence, reason
|
||||
):
|
||||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await sql_injection_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
@ -552,7 +553,7 @@ class TestContentFilterPromptInjectionTemplate:
|
|||
@pytest.mark.asyncio
|
||||
async def test_always_block(self, content_filter_guardrail, sentence, reason):
|
||||
request_data = {"messages": [{"role": "user", "content": sentence}]}
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
await content_filter_guardrail.apply_guardrail(
|
||||
inputs={"texts": [sentence]},
|
||||
request_data=request_data,
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ async def test_azure_image_generation_request_body():
|
|||
) as mock_post:
|
||||
mock_post.side_effect = Exception("test")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await aimage_generation(
|
||||
model="azure/gpt-image-1",
|
||||
prompt="test prompt",
|
||||
|
|
|
|||
|
|
@ -1334,7 +1334,7 @@ def test_validate_chat_completion_user_messages(messages, expected_bool):
|
|||
validate_chat_completion_user_messages(messages=messages)
|
||||
else:
|
||||
## Invalid message
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Invalid user message at index 0"):
|
||||
validate_chat_completion_user_messages(messages=messages)
|
||||
|
||||
|
||||
|
|
@ -1354,7 +1354,7 @@ def test_validate_chat_completion_tool_choice(tool_choice, expected_bool):
|
|||
if expected_bool:
|
||||
validate_chat_completion_tool_choice(tool_choice=tool_choice)
|
||||
else:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Invalid tool choice"):
|
||||
validate_chat_completion_tool_choice(tool_choice=tool_choice)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from openai.types.responses.response_create_params import (
|
|||
ResponseInputParam,
|
||||
)
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
|
||||
import openai
|
||||
|
||||
|
||||
def validate_responses_api_response(response, final_chunk: bool = False):
|
||||
|
|
@ -700,12 +701,12 @@ class BaseResponsesAPITest(ABC):
|
|||
base_completion_call_args = self.get_base_completion_call_args()
|
||||
|
||||
if sync_mode:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(openai.APIError):
|
||||
litellm.cancel_responses(
|
||||
response_id="invalid_response_id_12345", **base_completion_call_args
|
||||
)
|
||||
else:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(openai.APIError):
|
||||
await litellm.acancel_responses(
|
||||
response_id="invalid_response_id_12345", **base_completion_call_args
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1243,7 +1243,7 @@ def test_convert_to_model_response_object_with_error_code_only():
|
|||
},
|
||||
}
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception) as exc_info: # noqa: B017 # bare Exception raised, so status_code is the assertion
|
||||
convert_to_model_response_object(
|
||||
model_response_object=ModelResponse(),
|
||||
response_object=response_object,
|
||||
|
|
@ -1255,6 +1255,8 @@ def test_convert_to_model_response_object_with_error_code_only():
|
|||
convert_tool_call_to_json_mode=False,
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
def test_model_prefix_preservation():
|
||||
"""
|
||||
|
|
@ -2473,14 +2475,14 @@ class TestConvertToModelResponseObjectCompletion:
|
|||
assert "reasoning_content" not in (message.provider_specific_fields or {})
|
||||
|
||||
def test_response_none_raises(self):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Invalid response object"):
|
||||
convert_to_model_response_object(
|
||||
response_object=None,
|
||||
model_response_object=ModelResponse(),
|
||||
)
|
||||
|
||||
def test_model_response_none_raises(self):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Invalid response object"):
|
||||
convert_to_model_response_object(
|
||||
response_object={
|
||||
"choices": [
|
||||
|
|
|
|||
|
|
@ -1458,7 +1458,7 @@ def test_responses_gpt54_with_xhigh_reasoning():
|
|||
# Stop execution right after request generation to avoid external API calls.
|
||||
mock_responses.side_effect = RuntimeError("stop_after_request_build")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
litellm.completion(
|
||||
model="openai/responses/gpt-5.4",
|
||||
messages=[{"role": "user", "content": "What is 2+2?"}],
|
||||
|
|
|
|||
|
|
@ -569,5 +569,5 @@ class TestClaudeModelPatternMatching:
|
|||
)
|
||||
|
||||
set_fallback_generalizations([])
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.BadRequestError):
|
||||
litellm.get_llm_provider(model="claude-opus-4-9")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import httpx
|
||||
from openai import OpenAI, BadRequestError
|
||||
from openai import OpenAI, BadRequestError, APIStatusError
|
||||
import pytest
|
||||
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ def test_basic_response():
|
|||
print("DELETE response=", delete_response)
|
||||
|
||||
# expect an error when getting the response again since it was deleted
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(APIStatusError):
|
||||
get_response = client.responses.retrieve(response.id)
|
||||
|
||||
|
||||
|
|
@ -195,6 +195,6 @@ def test_cancel_streaming_response():
|
|||
|
||||
def test_cancel_invalid_response_id():
|
||||
client = get_test_client()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(APIStatusError):
|
||||
# Try to cancel a non-existent response ID
|
||||
client.responses.cancel("invalid_response_id_12345")
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ async def test_wildcard_access_after_cost_map_reload(key_models, model, expect_t
|
|||
Fix: each reload now calls litellm.add_known_models(model_cost_map=new_map)
|
||||
with the fetched map passed explicitly to avoid any reference ambiguity.
|
||||
"""
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.auth.auth_checks import can_key_call_model
|
||||
|
||||
# Build a new cost map that includes the brand-new model — exactly what
|
||||
|
|
@ -378,7 +379,7 @@ async def test_wildcard_access_after_cost_map_reload(key_models, model, expect_t
|
|||
llm_router=router,
|
||||
)
|
||||
else:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await can_key_call_model(
|
||||
model=model,
|
||||
llm_model_list=llm_model_list,
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ from litellm.proxy.auth.handle_jwt import JWTHandler, JWTAuthManager
|
|||
from litellm.proxy.management_endpoints.team_endpoints import new_team
|
||||
from litellm.proxy.proxy_server import chat_completion
|
||||
from typing import Literal, Optional
|
||||
from litellm.proxy._types import ProxyException
|
||||
|
||||
public_key = {
|
||||
"kty": "RSA",
|
||||
|
|
@ -1045,9 +1046,7 @@ async def test_allow_access_by_email(
|
|||
assert result is not None # Adjust this based on your actual response check
|
||||
else:
|
||||
# Expect the call to fail
|
||||
with pytest.raises(
|
||||
Exception
|
||||
): # Replace with the actual exception raised on failure
|
||||
with pytest.raises(ProxyException):
|
||||
resp = await user_api_key_auth(request=request, api_key=bearer_token)
|
||||
print(resp)
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ async def test_read_config_from_bad_file_path():
|
|||
"""
|
||||
proxy_config_instance = ProxyConfig()
|
||||
config_path = "non-existent-file.yaml"
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Config file not found"):
|
||||
config = await proxy_config_instance.get_config(config_file_path=config_path)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ from litellm.proxy.litellm_pre_call_utils import (
|
|||
_get_dynamic_logging_metadata,
|
||||
add_litellm_data_to_request,
|
||||
)
|
||||
from pydantic import ValidationError
|
||||
|
||||
pytestmark = pytest.mark.xdist_group("proxy_heavy")
|
||||
|
||||
|
|
@ -1695,13 +1696,13 @@ def test_update_key_request_validation():
|
|||
"""
|
||||
from litellm.proxy._types import UpdateKeyRequest
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
UpdateKeyRequest(
|
||||
key="test_key",
|
||||
temp_budget_increase=100,
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
UpdateKeyRequest(
|
||||
key="test_key",
|
||||
temp_budget_expiry="2024-01-20T00:00:00Z",
|
||||
|
|
@ -1848,7 +1849,7 @@ async def test_end_user_transactions_reset():
|
|||
mock_client.db.tx = AsyncMock(side_effect=Exception("DB Error"))
|
||||
|
||||
# Call function - should raise error
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
await ProxyUpdateSpend.update_end_user_spend(
|
||||
n_retry_times=0,
|
||||
prisma_client=mock_client,
|
||||
|
|
@ -1878,7 +1879,7 @@ async def test_spend_logs_cleanup_after_error():
|
|||
original_logs = mock_client.spend_log_transactions.copy()
|
||||
|
||||
# Call function - should raise error
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
await ProxyUpdateSpend.update_spend_logs(
|
||||
n_retry_times=0,
|
||||
prisma_client=mock_client,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from litellm.proxy import proxy_server
|
|||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.proxy.utils import PrismaClient, ProxyLogging
|
||||
from litellm.types.utils import LlmProviders
|
||||
import openai
|
||||
|
||||
proxy_logging_obj = ProxyLogging(user_api_key_cache=DualCache())
|
||||
|
||||
|
|
@ -254,7 +255,7 @@ async def test_delete_skill_sdk(prisma_client):
|
|||
assert result.type == "skill_deleted"
|
||||
|
||||
# Verify skill no longer exists
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(openai.APIError):
|
||||
await aget_skill(
|
||||
skill_id=created_skill.id,
|
||||
custom_llm_provider=LlmProviders.LITELLM_PROXY.value,
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ def test_ui_token_route_access(route, user_role, should_be_allowed):
|
|||
)
|
||||
assert result is True
|
||||
else:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Only proxy admin can be used to generate"):
|
||||
_is_api_route_allowed(
|
||||
route=route,
|
||||
request=request,
|
||||
|
|
|
|||
|
|
@ -520,13 +520,15 @@ async def test_circuit_breaker_covers_lua_script_execution(redis_no_ping):
|
|||
counted toward taking Redis out of the pool and kept paying a full socket timeout
|
||||
each, which is the traffic the outage hurts most.
|
||||
"""
|
||||
from redis.exceptions import ConnectionError as RedisConnectionError
|
||||
|
||||
from litellm.constants import REDIS_CIRCUIT_BREAKER_FAILURE_THRESHOLD
|
||||
|
||||
cache = RedisCache(host="127.0.0.1", port=_closed_port(), socket_timeout=0.5)
|
||||
run_script = cache.async_register_script("return 1")
|
||||
|
||||
for _ in range(REDIS_CIRCUIT_BREAKER_FAILURE_THRESHOLD):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(RedisConnectionError):
|
||||
await run_script(keys=["lit4930"], args=[1])
|
||||
|
||||
with pytest.raises(Exception, match="circuit breaker is open"):
|
||||
|
|
@ -603,8 +605,11 @@ async def test_only_connectivity_failures_open_the_breaker(error, opens_breaker)
|
|||
async def failing_call():
|
||||
raise raised
|
||||
|
||||
for _ in range(breaker.failure_threshold + 1):
|
||||
with pytest.raises(Exception):
|
||||
for _ in range(breaker.failure_threshold):
|
||||
with pytest.raises(type(raised)):
|
||||
await _run_under_circuit_breaker(breaker, "op", failing_call)
|
||||
|
||||
with pytest.raises(Exception, match="circuit breaker is open" if opens_breaker else "boom"):
|
||||
await _run_under_circuit_breaker(breaker, "op", failing_call)
|
||||
|
||||
assert breaker.is_open() is opens_breaker
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ class TestContainerAPI:
|
|||
"container_create_handler",
|
||||
side_effect=Exception("API Error"),
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
create_container(
|
||||
name="Error Test Container", custom_llm_provider="openai"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ class TestAsyncErrorWrapping:
|
|||
handler.create_agent.side_effect = RuntimeError("kaboom")
|
||||
|
||||
with patch(_HANDLER_PATH, handler):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await acreate(name="waverunner", api_key="AIza")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -323,7 +323,7 @@ class TestAsyncErrorWrapping:
|
|||
handler.get_agent.side_effect = RuntimeError("kaboom")
|
||||
|
||||
with patch(_HANDLER_PATH, handler):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await aget(name="waverunner", api_key="AIza")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -332,7 +332,7 @@ class TestAsyncErrorWrapping:
|
|||
handler.list_agents.side_effect = RuntimeError("kaboom")
|
||||
|
||||
with patch(_HANDLER_PATH, handler):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await alist(api_key="AIza")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -341,7 +341,7 @@ class TestAsyncErrorWrapping:
|
|||
handler.delete_agent.side_effect = RuntimeError("kaboom")
|
||||
|
||||
with patch(_HANDLER_PATH, handler):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await adelete(name="waverunner", api_key="AIza")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -350,5 +350,5 @@ class TestAsyncErrorWrapping:
|
|||
handler.list_agent_versions.side_effect = RuntimeError("kaboom")
|
||||
|
||||
with patch(_HANDLER_PATH, handler):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await alist_versions(name="waverunner", api_key="AIza")
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ sys.path.insert(0, os.path.abspath("../../.."))
|
|||
|
||||
import litellm
|
||||
import litellm.interactions as interactions
|
||||
import openai
|
||||
|
||||
# Test API key - should be set in environment
|
||||
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
||||
|
|
@ -258,7 +259,7 @@ class TestGoogleInteractionsErrorHandling:
|
|||
|
||||
def test_invalid_model(self, api_key):
|
||||
"""Test error handling for invalid model."""
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(openai.APIError):
|
||||
interactions.create(
|
||||
model="gemini/invalid-model-name-xyz",
|
||||
input="Hello",
|
||||
|
|
@ -267,7 +268,7 @@ class TestGoogleInteractionsErrorHandling:
|
|||
|
||||
def test_missing_model_and_agent(self, api_key):
|
||||
"""Test error when neither model nor agent is provided."""
|
||||
with pytest.raises(Exception): # Can be ValueError or APIConnectionError
|
||||
with pytest.raises((ValueError, litellm.APIConnectionError)):
|
||||
interactions.create(
|
||||
input="Hello",
|
||||
api_key=api_key,
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ def test_capability_info_backfills_requested_provider(restore_generalizations):
|
|||
def test_routing_only_match_does_not_resolve_model_info(restore_generalizations):
|
||||
restore_generalizations([{"name": "route", "pattern": r"^ceeco-", "model_info": {"litellm_provider": "openai"}}])
|
||||
litellm.get_model_info.cache_clear()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="This model isn't mapped yet"):
|
||||
litellm.get_model_info("ceeco-fast-1", custom_llm_provider="openai")
|
||||
|
||||
|
||||
|
|
@ -470,7 +470,7 @@ def test_shipped_adaptive_rule_requires_claude_prefix(shipped_cost_map):
|
|||
model = "openai/team-sonnet-5-1-alias"
|
||||
assert model not in litellm.model_cost
|
||||
assert match_capability_generalizations("team-sonnet-5-1-alias") is None
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="This model isn't mapped yet"):
|
||||
litellm.get_model_info(model)
|
||||
|
||||
|
||||
|
|
@ -496,7 +496,7 @@ def test_shipped_rules_lose_to_exact_entries_across_cost_ladder_variants(shipped
|
|||
from litellm.types.utils import ModelResponse, Usage
|
||||
|
||||
assert "claude-haiku-4-5-20251001" in litellm.model_cost
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="This model isn't mapped yet"):
|
||||
litellm.get_model_info("claude-haiku-4-5-20251001", custom_llm_provider="bedrock")
|
||||
|
||||
entry = litellm.model_cost["us.anthropic.claude-haiku-4-5-20251001-v1:0"]
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ def test_realtime_streaming_store_message():
|
|||
|
||||
# Test 3: Invalid message format
|
||||
invalid_msg = "invalid json"
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(json.JSONDecodeError):
|
||||
streaming.store_message(invalid_msg)
|
||||
|
||||
# Test 4: Message type not in logged events
|
||||
|
|
|
|||
|
|
@ -4176,7 +4176,7 @@ async def test_stream_wrapper_anext_max_duration_timeout_restores_consumer_corre
|
|||
|
||||
wrapper._stream_created_time = time.time() - 10
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.Timeout):
|
||||
await wrapper.__anext__()
|
||||
|
||||
assert trace_id_var.get() == "outer-trace-max-duration"
|
||||
|
|
@ -4323,7 +4323,9 @@ def test_handle_stream_fallback_error_restores_context_only_after_exception_mapp
|
|||
|
||||
monkeypatch.setattr("litellm.litellm_core_utils.streaming_handler.exception_type", fake_exception_type)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
from litellm.exceptions import MidStreamFallbackError
|
||||
|
||||
with pytest.raises(MidStreamFallbackError):
|
||||
wrapper._handle_stream_fallback_error(RuntimeError("boom"))
|
||||
|
||||
# The mapper ran while the stream's own ids were still active.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ def test_anthropic_messages_handler_skips_the_gateway_on_recursion():
|
|||
"litellm.llms.anthropic.experimental_pass_through.messages.mcp_handler.anthropic_messages_with_mcp",
|
||||
new=AsyncMock(return_value={"routed": True}),
|
||||
) as routed:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValueError):
|
||||
anthropic_messages_handler(
|
||||
max_tokens=100,
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
|
|
@ -80,7 +80,7 @@ def test_anthropic_messages_handler_leaves_native_tools_alone():
|
|||
"litellm.llms.anthropic.experimental_pass_through.messages.mcp_handler.anthropic_messages_with_mcp",
|
||||
new=AsyncMock(return_value={"routed": True}),
|
||||
) as routed:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValueError):
|
||||
anthropic_messages_handler(
|
||||
max_tokens=100,
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from litellm.types.videos.main import (
|
|||
VideoCreateOptionalRequestParams,
|
||||
)
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from pydantic import ValidationError
|
||||
|
||||
|
||||
class TestAzureVideoConfig:
|
||||
|
|
@ -299,7 +300,7 @@ class TestAzureVideoConfig:
|
|||
logging_obj = MagicMock()
|
||||
|
||||
# Test that error responses raise exceptions
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
self.config.transform_video_create_response(
|
||||
model=self.model, raw_response=mock_response, logging_obj=logging_obj
|
||||
)
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ async def test_asearch_does_not_leak_server_key_to_caller_api_base(
|
|||
new_callable=AsyncMock,
|
||||
) as mock_get,
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await litellm.asearch(
|
||||
query="secrets",
|
||||
search_provider="serper",
|
||||
|
|
@ -319,7 +319,7 @@ async def test_query_param_key_not_leaked_with_dummy_caller_key(
|
|||
"litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.get",
|
||||
fake_get,
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
await litellm.asearch(
|
||||
query="secrets",
|
||||
search_provider=provider,
|
||||
|
|
|
|||
|
|
@ -1077,7 +1077,7 @@ async def test_session_closed_retry_does_not_close_concurrent_replacement():
|
|||
raise StopAsyncIteration("stop after retry dispatch")
|
||||
|
||||
with patch.object(transport, "_make_aiohttp_request", side_effect=fake_make_request):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(StopAsyncIteration):
|
||||
await transport.handle_async_request(httpx.Request("GET", "http://example.com"))
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ class TestGithubCopilotTransformResponse:
|
|||
)
|
||||
model_response = ModelResponse()
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(json.JSONDecodeError):
|
||||
config.transform_response(
|
||||
model="github_copilot/claude-opus-4.7",
|
||||
raw_response=raw_response,
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ def test_huggingface_rerank_error_handling(mock_post):
|
|||
mock_response.text = "Unauthorized"
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
litellm.rerank(
|
||||
model="huggingface/BAAI/bge-reranker-base",
|
||||
query="hello",
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ def test_langflow_extra_body_cannot_inject_tweaks_into_run_payload():
|
|||
return resp
|
||||
|
||||
with patch.object(HTTPHandler, "post", side_effect=fake_post):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
litellm.completion(
|
||||
model="langflow/my-flow",
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ from litellm.llms.vertex_ai.files.transformation import (
|
|||
_openai_batch_jsonl_entry_to_vertex_rows,
|
||||
)
|
||||
from litellm.types.llms.openai import CreateFileRequest
|
||||
from litellm.llms.vertex_ai.common_utils import VertexAIError
|
||||
|
||||
|
||||
def _upload_stream(transformed) -> BaseFileUploadStream:
|
||||
|
|
@ -561,7 +562,7 @@ class TestStreamingMediaUpload:
|
|||
|
||||
async def test_failed_upload_raises(self):
|
||||
raw = _make_openai_jsonl_bytes(80)
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(VertexAIError):
|
||||
await self._run(raw, status=403)
|
||||
|
||||
async def test_request_timeout_is_forwarded(self):
|
||||
|
|
|
|||
|
|
@ -556,7 +556,7 @@ def test_get_llm_provider_uses_single_xai_provider(monkeypatch):
|
|||
|
||||
|
||||
def test_xai_oauth_alias_is_not_a_provider():
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.BadRequestError):
|
||||
get_llm_provider("xai_oauth/grok-4")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ from litellm.models.verification_token import (
|
|||
LiteLLM_DeletedVerificationToken,
|
||||
LiteLLM_VerificationToken,
|
||||
)
|
||||
from pydantic import ValidationError
|
||||
|
||||
|
||||
class TestBudget:
|
||||
|
|
@ -421,7 +422,7 @@ class TestBudgetTableFull:
|
|||
assert budget.max_budget == 10.0
|
||||
|
||||
def test_full_requires_created_at(self):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
LiteLLM_BudgetTableFull(budget_id="b1")
|
||||
|
||||
|
||||
|
|
@ -480,7 +481,7 @@ class TestMCPServerTable:
|
|||
assert server.env == {}
|
||||
|
||||
def test_mcp_server_requires_transport(self):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
LiteLLM_MCPServerTable(server_id="s1")
|
||||
|
||||
|
||||
|
|
@ -538,7 +539,7 @@ class TestManagedTables:
|
|||
assert table.flat_model_file_ids == ["file-abc"]
|
||||
|
||||
def test_managed_object_table_requires_purpose(self):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
LiteLLM_ManagedObjectTable(
|
||||
unified_object_id="o1", model_object_id="m1", file_object={}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4440,7 +4440,7 @@ async def test_call_mcp_tool_logs_failure_via_post_call_failure_hook():
|
|||
proxy_logging_mock,
|
||||
),
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="boom"):
|
||||
await call_mcp_tool(
|
||||
name="test_server-any_tool",
|
||||
arguments={"x": 1},
|
||||
|
|
|
|||
|
|
@ -2809,7 +2809,7 @@ def test_team_update_gate_rejects_without_org_context():
|
|||
request.method = "POST"
|
||||
request.query_params = {}
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Only proxy admin can be used to generate"):
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=LitellmUserRoles.INTERNAL_USER.value,
|
||||
|
|
@ -2829,7 +2829,7 @@ def test_team_update_gate_rejects_cross_org_admin_with_resolved_org():
|
|||
request.method = "POST"
|
||||
request.query_params = {}
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Only proxy admin can be used to generate"):
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=LitellmUserRoles.INTERNAL_USER.value,
|
||||
|
|
@ -2951,7 +2951,7 @@ def test_patch_team_gate_rejects_regular_internal_user():
|
|||
)
|
||||
valid_token = UserAPIKeyAuth(user_id="regular-user", user_role=LitellmUserRoles.INTERNAL_USER.value)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Only proxy admin can be used to generate"):
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=LitellmUserRoles.INTERNAL_USER.value,
|
||||
|
|
@ -2967,7 +2967,7 @@ def test_patch_team_gate_rejects_cross_org_admin():
|
|||
user_obj = _make_org_admin_user("org-1")
|
||||
valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Only proxy admin can be used to generate"):
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=LitellmUserRoles.INTERNAL_USER.value,
|
||||
|
|
@ -2987,7 +2987,7 @@ def test_patch_team_gate_rejects_view_only_admin():
|
|||
)
|
||||
valid_token = UserAPIKeyAuth(user_id="viewer", user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
RouteChecks.non_proxy_admin_allowed_routes_check(
|
||||
user_obj=user_obj,
|
||||
_user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value,
|
||||
|
|
|
|||
|
|
@ -979,7 +979,7 @@ async def test_create__exception_calls_failure_hook(harness, openai_env_creds):
|
|||
)
|
||||
harness.litellm_acreate.side_effect = ValueError("provider boom")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await call_create(harness)
|
||||
|
||||
harness.logging.post_call_failure_hook.assert_called_once()
|
||||
|
|
@ -1437,7 +1437,7 @@ async def test_retrieve__uses_aretrieve_batch_route_type(retrieve_harness, opena
|
|||
async def test_retrieve__exception_calls_failure_hook(retrieve_harness, openai_env_creds):
|
||||
retrieve_harness.litellm_aretrieve.side_effect = ValueError("provider boom")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await call_retrieve(retrieve_harness, "batch-raw-xyz")
|
||||
|
||||
retrieve_harness.logging.post_call_failure_hook.assert_called_once()
|
||||
|
|
@ -1844,7 +1844,7 @@ async def test_list__uses_alist_batches_route_type(list_harness):
|
|||
async def test_list__exception_calls_failure_hook(list_harness):
|
||||
list_harness.litellm_alist.side_effect = ValueError("provider boom")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await call_list(list_harness)
|
||||
|
||||
list_harness.logging.post_call_failure_hook.assert_called_once()
|
||||
|
|
@ -2233,7 +2233,7 @@ async def test_cancel__uses_acancel_batch_route_type(cancel_harness, openai_env_
|
|||
async def test_cancel__exception_calls_failure_hook(cancel_harness, openai_env_creds):
|
||||
cancel_harness.litellm_acancel.side_effect = ValueError("provider boom")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await call_cancel(cancel_harness, "batch-raw-xyz")
|
||||
|
||||
cancel_harness.logging.post_call_failure_hook.assert_called_once()
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ async def test_engine_confirmed_dead_persists_across_failed_heavy_reconnect(
|
|||
client._reap_all_zombies = MagicMock()
|
||||
|
||||
with patch.dict(os.environ, {"DATABASE_URL": "postgresql://test"}):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(RuntimeError):
|
||||
await client._run_reconnect_cycle(timeout_seconds=5.0)
|
||||
|
||||
# The flag must STILL be True so the next attempt re-enters the heavy
|
||||
|
|
|
|||
|
|
@ -2113,7 +2113,7 @@ async def test_make_bedrock_api_request_forwards_guardrail_action():
|
|||
):
|
||||
mock_post.return_value = mock_bedrock_response
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="blocked"):
|
||||
await guardrail.make_bedrock_api_request(
|
||||
source="INPUT",
|
||||
messages=request_data["messages"],
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from litellm.proxy.guardrails.guardrail_hooks.presidio import (
|
|||
from litellm.exceptions import GuardrailRaisedException
|
||||
from litellm.types.guardrails import LitellmParams, PiiAction, PiiEntityType
|
||||
from litellm.types.utils import Choices, Message, ModelResponse
|
||||
from litellm.exceptions import BlockedPiiEntityError
|
||||
|
||||
|
||||
def _make_mock_session_iterator(
|
||||
|
|
@ -1345,7 +1346,7 @@ def test_blocking_respects_threshold_filter():
|
|||
{"entity_type": PiiEntityType.CREDIT_CARD, "score": 0.95, "start": 0, "end": 4}
|
||||
]
|
||||
filtered_high = guardrail.filter_analyze_results_by_score(high_score_results)
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(BlockedPiiEntityError):
|
||||
guardrail.raise_exception_if_blocked_entities_detected(filtered_high)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ from litellm.types.proxy.management_endpoints.scim_v2 import (
|
|||
SCIMUserGroup,
|
||||
SCIMUserName,
|
||||
)
|
||||
from litellm.proxy._types import ProxyException
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -3216,7 +3217,7 @@ async def test_delete_user_surfaces_prune_failure_and_keeps_user(mocker):
|
|||
AsyncMock(side_effect=Exception("database connection lost")),
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await delete_user(user_id=user_id)
|
||||
|
||||
mock_prisma_client.db.litellm_usertable.delete.assert_not_awaited()
|
||||
|
|
|
|||
|
|
@ -668,6 +668,8 @@ def test_validate_sort_params():
|
|||
"""
|
||||
Test that validate_sort_params returns None if sort_by is None
|
||||
"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
from litellm.proxy.management_endpoints.internal_user_endpoints import (
|
||||
_validate_sort_params,
|
||||
)
|
||||
|
|
@ -676,7 +678,7 @@ def test_validate_sort_params():
|
|||
assert _validate_sort_params(None, "desc") is None
|
||||
assert _validate_sort_params("user_id", "asc") == {"user_id": "asc"}
|
||||
assert _validate_sort_params("user_id", "desc") == {"user_id": "desc"}
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(HTTPException):
|
||||
_validate_sort_params("user_id", "invalid")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from litellm.proxy.management_helpers.team_metadata_validation import (
|
|||
run_team_metadata_validation,
|
||||
validate_team_metadata_if_configured,
|
||||
)
|
||||
from pydantic import ValidationError
|
||||
|
||||
|
||||
def _registry_with(validator):
|
||||
|
|
@ -634,7 +635,7 @@ def test_parse_schema_round_trips_fields_in_order():
|
|||
],
|
||||
)
|
||||
def test_parse_schema_malformed_raises(raw):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
parse_team_metadata_schema(raw)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ async def test_pass_through_request_failure_handler():
|
|||
mock_user_api_key_dict = MagicMock()
|
||||
|
||||
# Call the function with a target that will trigger an HTTPError
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await pass_through_request(
|
||||
request=mock_request,
|
||||
target="http://test.com",
|
||||
|
|
@ -1154,7 +1154,7 @@ async def test_pass_through_request_uses_resolved_timeout():
|
|||
|
||||
mock_user_api_key_dict = MagicMock()
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
await pass_through_request(
|
||||
request=mock_request,
|
||||
target="http://test.com",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from litellm.integrations.custom_guardrail import (
|
|||
CustomGuardrail,
|
||||
ModifyResponseException,
|
||||
)
|
||||
from litellm.proxy._types import ProxyException
|
||||
|
||||
_PT_MOD = "litellm.proxy.pass_through_endpoints.pass_through_endpoints"
|
||||
_COLLECT = "litellm.proxy.pass_through_endpoints.passthrough_guardrails.PassthroughGuardrailHandler.collect_guardrails"
|
||||
|
|
@ -251,7 +252,7 @@ class TestPassthroughPostCallGuardrails:
|
|||
)
|
||||
|
||||
with _common_patches(mock_proxy_logging, mock_response):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ProxyException):
|
||||
await pass_through_request(
|
||||
request=_make_mock_request(),
|
||||
target="https://example.com/v1/generateContent",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from litellm.proxy.proxy_server import (
|
|||
)
|
||||
|
||||
from .conftest import normalize
|
||||
from pydantic import ValidationError
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _is_remote_module_url
|
||||
|
|
@ -393,7 +394,7 @@ def test_ProxyConfig__load_yaml_file_returns_parsed_dict(tmp_path):
|
|||
|
||||
def test_ProxyConfig__load_yaml_file_raises_on_missing_file():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Error loading yaml file"):
|
||||
pc._load_yaml_file("/no/such/file.yaml")
|
||||
|
||||
|
||||
|
|
@ -418,7 +419,7 @@ async def test_ProxyConfig__get_config_from_file_loads_yaml(tmp_path):
|
|||
@pytest.mark.asyncio
|
||||
async def test_ProxyConfig__get_config_from_file_missing_path_raises():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Config file not found"):
|
||||
await pc._get_config_from_file(config_file_path="/no/such/file.yaml")
|
||||
|
||||
|
||||
|
|
@ -476,7 +477,7 @@ async def test_ProxyConfig_save_config_invalid_path_raises(monkeypatch):
|
|||
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {})
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await pc.save_config({"x": 1})
|
||||
|
||||
|
||||
|
|
@ -641,7 +642,7 @@ def test_ProxyConfig__get_team_config_returns_match():
|
|||
|
||||
def test_ProxyConfig__get_team_config_missing_team_id_raises():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="team_id missing from team"):
|
||||
pc._get_team_config(team_id="t1", all_teams_config=[{"no_id_field": True}])
|
||||
|
||||
|
||||
|
|
@ -671,7 +672,7 @@ def test_ProxyConfig_load_team_config_no_settings_returns_empty():
|
|||
assert out == {}
|
||||
# Error-style: a misconfigured team list without team_id raises.
|
||||
pc.config = {"litellm_settings": {"default_team_settings": [{"no_id": True}]}}
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="team_id missing from team"):
|
||||
pc.load_team_config(team_id="anything")
|
||||
|
||||
|
||||
|
|
@ -698,7 +699,7 @@ def test_ProxyConfig__init_cache_sets_litellm_cache(monkeypatch):
|
|||
|
||||
def test_ProxyConfig__init_cache_invalid_params_raises():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
pc._init_cache(cache_params={"type": "this-cache-type-does-not-exist"})
|
||||
|
||||
|
||||
|
|
@ -765,7 +766,7 @@ async def test_ProxyConfig_get_config_missing_file_raises(monkeypatch):
|
|||
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False)
|
||||
monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False)
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Config file not found"):
|
||||
await pc.get_config(config_file_path="/no/such/path.yaml")
|
||||
|
||||
|
||||
|
|
@ -1041,7 +1042,7 @@ def test_ProxyConfig_load_credential_list_returns_items():
|
|||
|
||||
def test_ProxyConfig_load_credential_list_invalid_entry_raises():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
pc.load_credential_list({"credential_list": [{"missing_required": True}]})
|
||||
|
||||
|
||||
|
|
@ -1381,7 +1382,7 @@ async def test_ProxyConfig_load_config_missing_file_raises(monkeypatch):
|
|||
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False)
|
||||
monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False)
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="Config file not found"):
|
||||
await pc.load_config(router=None, config_file_path="/no/file.yaml")
|
||||
|
||||
|
||||
|
|
@ -1492,7 +1493,7 @@ async def test_ProxyConfig__init_non_llm_configs_empty_config():
|
|||
async def test_ProxyConfig__init_non_llm_configs_premium_invalid_worker_registry_raises(monkeypatch):
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True)
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
await pc._init_non_llm_configs(
|
||||
config={"worker_registry": [{"totally": "invalid"}]},
|
||||
config_file_path=None,
|
||||
|
|
@ -1572,7 +1573,7 @@ async def test_ProxyConfig__init_policy_engine_none_config_noop():
|
|||
# None config returns early without raising.
|
||||
await pc._init_policy_engine(config=None, prisma_client=None, llm_router=None)
|
||||
# Error-style: invalid policies value should raise.
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
await pc._init_policy_engine(
|
||||
config={"policies": "not-a-list"},
|
||||
prisma_client=None,
|
||||
|
|
@ -1601,7 +1602,7 @@ def test_ProxyConfig__load_alerting_settings_noop_when_no_alerting():
|
|||
|
||||
def test_ProxyConfig__load_alerting_settings_invalid_alerting_raises():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(RuntimeError):
|
||||
# alerting must be iterable — int triggers an error.
|
||||
pc._load_alerting_settings({"alerting": 12345})
|
||||
|
||||
|
|
@ -1823,7 +1824,7 @@ async def test_ProxyConfig__delete_deployment_invalid_models_raises(monkeypatch)
|
|||
fake_router.get_model_ids = MagicMock(return_value=[])
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router)
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
# Non-model objects without expected attrs trigger an error.
|
||||
await pc._delete_deployment(db_models=[{"not_a_model": True}])
|
||||
|
||||
|
|
@ -2721,7 +2722,7 @@ async def test_ProxyConfig__update_general_settings_none_input_noop():
|
|||
result = await pc._update_general_settings(db_general_settings=None)
|
||||
assert result is None
|
||||
# Error-style: dict() will fail on non-mapping non-None input.
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
await pc._update_general_settings(db_general_settings=12345) # type: ignore[arg-type]
|
||||
|
||||
|
||||
|
|
@ -2743,7 +2744,7 @@ def test_ProxyConfig__update_config_fields_merges_dict():
|
|||
|
||||
def test_ProxyConfig__update_config_fields_invalid_param_raises():
|
||||
pc = ProxyConfig()
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
# Missing required arg.
|
||||
pc._update_config_fields(current_config={}, param_name="general_settings") # type: ignore[call-arg]
|
||||
|
||||
|
|
|
|||
|
|
@ -1584,7 +1584,7 @@ async def test_prisma_health_check_failure_redacts_database_credentials(caplog):
|
|||
client._report_health_check_failure = AsyncMock()
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(Exception, match="could not connect to"):
|
||||
await PrismaClient.health_check(client)
|
||||
|
||||
emitted = [record.getMessage() for record in caplog.records if record.name == "LiteLLM Proxy"]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from litellm.llms.dashscope.image_generation.transformation import (
|
|||
)
|
||||
from litellm.types.utils import ImageObject, ImageResponse
|
||||
from litellm.utils import get_llm_provider
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -247,7 +248,7 @@ class TestDashScopeImageGenerationConfig:
|
|||
"message": "Size not supported",
|
||||
}
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(BaseLLMException):
|
||||
self.cfg.transform_image_generation_response(
|
||||
model="qwen-image-2.0",
|
||||
raw_response=mock_resp,
|
||||
|
|
@ -268,7 +269,7 @@ class TestDashScopeImageGenerationConfig:
|
|||
"message": "Size not supported",
|
||||
}
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(BaseLLMException):
|
||||
self.cfg.transform_image_generation_response(
|
||||
model="qwen-image-2.0",
|
||||
raw_response=mock_resp,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from litellm.litellm_core_utils.get_blog_posts import (
|
|||
GetBlogPosts,
|
||||
get_blog_posts,
|
||||
)
|
||||
from xml.etree import ElementTree
|
||||
|
||||
SAMPLE_RSS = """\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
|
@ -71,7 +72,7 @@ def test_parse_rss_to_posts_multiple():
|
|||
|
||||
|
||||
def test_parse_rss_to_posts_invalid_xml():
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ElementTree.ParseError):
|
||||
GetBlogPosts.parse_rss_to_posts("not xml")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import pytest
|
||||
from litellm.proxy._types import NewProjectRequest, UpdateProjectRequest
|
||||
from pydantic import ValidationError
|
||||
|
||||
|
||||
def test_new_project_request_tags():
|
||||
|
|
@ -21,11 +22,11 @@ def test_update_project_request_tags():
|
|||
|
||||
def test_new_project_request_invalid_tags_type():
|
||||
# tags must be a list — a string should raise a ValidationError
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
NewProjectRequest(project_id="test_proj", team_id="team_1", tags="not-a-list")
|
||||
|
||||
|
||||
def test_update_project_request_invalid_tags_type():
|
||||
# tags must be a list — a string should raise a ValidationError
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValidationError):
|
||||
UpdateProjectRequest(project_id="test_proj", tags="not-a-list")
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ class TestRouterFallbackFailureTracebackRedaction:
|
|||
raise ValueError(f"primary deployment failed api_key={secret}")
|
||||
except ValueError as original_exception:
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Router"):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValueError):
|
||||
await router.async_function_with_fallbacks_common_utils(
|
||||
e=original_exception,
|
||||
disable_fallbacks=False,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import pytest
|
|||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
|
||||
import litellm # noqa: E402
|
||||
import openai
|
||||
|
||||
ASYNC_INVOKE_ARN = "arn:aws:bedrock:us-west-2:123456789012:async-invoke/abc123def456"
|
||||
MIJ_ARN = "arn:aws:bedrock:us-west-2:123456789012:model-invocation-job/abc1234567"
|
||||
|
|
@ -134,7 +135,7 @@ def test_unrelated_bedrock_arn_falls_through_to_provider_config(mock_handlers):
|
|||
# Use a plausible-but-unsupported Bedrock ARN family.
|
||||
unrelated_arn = "arn:aws:bedrock:us-west-2:123456789012:provisioned-model/xyz"
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.BadRequestError):
|
||||
# Will raise because no provider_config exists for this path —
|
||||
# that's fine, we just need to assert neither bedrock handler ran
|
||||
# before the failure.
|
||||
|
|
@ -152,7 +153,7 @@ def test_non_bedrock_id_skips_bedrock_dispatch_entirely(mock_handlers):
|
|||
block — they belong to other providers' retrieve flows."""
|
||||
async_invoke, mij, _ = mock_handlers
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(openai.OpenAIError):
|
||||
litellm.retrieve_batch(
|
||||
batch_id="batch_abc123",
|
||||
custom_llm_provider="openai",
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ async def test_arouter_with_tags_and_fallbacks():
|
|||
enable_tag_filtering=True,
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.InternalServerError):
|
||||
response = await router.acompletion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Hello, world!"}],
|
||||
|
|
@ -5707,7 +5707,7 @@ async def test_team_scoped_model_fallback_cross_team_blocked():
|
|||
fallbacks=[{"primary-model": ["fallback-model"]}],
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.InternalServerError):
|
||||
await router.acompletion(
|
||||
model="primary-model",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ async def test_no_failover_when_flag_off():
|
|||
# enable_weighted_failover defaults to False
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.InternalServerError):
|
||||
await router.acompletion(
|
||||
model="test-model",
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
|
|
@ -515,7 +515,7 @@ async def test_failover_exhausted_raises_original_error_class():
|
|||
enable_weighted_failover=True,
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.InternalServerError):
|
||||
await router.acompletion(
|
||||
model="test-model",
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
|
|
@ -648,7 +648,7 @@ async def test_failover_skipped_for_non_simple_shuffle():
|
|||
enable_weighted_failover=True,
|
||||
)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.InternalServerError):
|
||||
await router.acompletion(
|
||||
model="test-model",
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class TestVideoGeneration:
|
|||
"video_generation_handler",
|
||||
side_effect=Exception("API Error"),
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
video_generation(prompt="Test video", model="sora-2")
|
||||
|
||||
def test_video_generation_provider_config(self):
|
||||
|
|
@ -739,7 +739,7 @@ class TestVideoGeneration:
|
|||
"video_status_handler",
|
||||
side_effect=Exception("API Error"),
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
video_status(video_id="test_video_id", model="sora-2")
|
||||
|
||||
def test_video_status_request_transformation(self):
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ def test_get_character__mock_response_short_circuits(seams):
|
|||
def test_unsupported_provider_raises_without_dispatch(seams):
|
||||
seams.get_config.return_value = None
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(litellm.APIConnectionError):
|
||||
videos_main.video_status(video_id=AZURE_VIDEO_ID)
|
||||
|
||||
seams.handler.video_status_handler.assert_not_called()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from tests.vector_store_tests.base_vector_store_test import BaseVectorStoreTest
|
|||
from litellm.llms.ragflow.vector_stores.transformation import RAGFlowVectorStoreConfig
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.vector_stores import VectorStoreCreateOptionalRequestParams
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
|
||||
|
||||
class TestRAGFlowVectorStore(BaseVectorStoreTest):
|
||||
|
|
@ -233,7 +234,7 @@ class TestRAGFlowVectorStore(BaseVectorStoreTest):
|
|||
"message": "Dataset name 'test-dataset' already exists",
|
||||
}
|
||||
|
||||
with pytest.raises(Exception): # Should raise BaseLLMException
|
||||
with pytest.raises(BaseLLMException):
|
||||
config.transform_create_vector_store_response(mock_response)
|
||||
|
||||
def test_transform_create_vector_store_response_missing_id(self):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue