mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-26 01:12:21 +00:00
fix: satisfy stable/1.101.x lint and api-sync gates for the jev backport
This commit is contained in:
parent
662a89290f
commit
7e6d9e89ed
15 changed files with 193 additions and 172 deletions
|
|
@ -5229,7 +5229,9 @@ async def _check_team_member_model_access(
|
|||
):
|
||||
return # no per-member restriction — inherit team-level check
|
||||
|
||||
member_allowed_models: Final[list[str]] = loaded_membership.litellm_budget_table.allowed_models
|
||||
member_allowed_models: Final[list[str]] = (
|
||||
loaded_membership.litellm_budget_table.allowed_models
|
||||
) # mutable-ok: allowed_models is a prisma model list consumed read-only
|
||||
try:
|
||||
_can_object_call_model(
|
||||
model=model,
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class TeamGrants(TypedDict, total=False):
|
|||
team_tpd_limit: ReadOnly[int | None]
|
||||
team_max_budget: ReadOnly[float | None]
|
||||
team_soft_budget: ReadOnly[float | None]
|
||||
team_model_max_budget: ReadOnly[dict[str, object] | None]
|
||||
team_model_max_budget: ReadOnly[dict[str, object] | None] # mutable-ok: prisma JSON column deserialized per-request
|
||||
team_spend: ReadOnly[float | None]
|
||||
team_models: ReadOnly[Sequence[str]]
|
||||
team_blocked: ReadOnly[bool]
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ def cross_entry_family_error(
|
|||
"""
|
||||
if not callback_vars:
|
||||
return None
|
||||
stored_by_var: Final = {
|
||||
stored_by_var: Final = { # mutable-ok: config dict is updated in place per callback entry
|
||||
var: value for entry in stored_vars_by_entry for var, value in entry.items() if _family_of(var) is not None
|
||||
}
|
||||
family_values: Final = frozenset(
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
def _coerce_event_hook(
|
||||
mode: str | list[str] | Mode,
|
||||
) -> GuardrailEventHooks | list[GuardrailEventHooks] | Mode:
|
||||
mode: str | list[str] | Mode, # mutable-ok: event hook unions accept an ordered list
|
||||
) -> GuardrailEventHooks | list[GuardrailEventHooks] | Mode: # mutable-ok: event hook unions accept an ordered list
|
||||
if isinstance(mode, Mode):
|
||||
return mode
|
||||
if isinstance(mode, list):
|
||||
|
|
|
|||
|
|
@ -60,14 +60,14 @@ _STR_OBJECT_DICT_ADAPTER: Final = TypeAdapter(dict[str, object])
|
|||
_OBJECT_LIST_ADAPTER: Final = TypeAdapter(list[object])
|
||||
|
||||
|
||||
def _as_str_object_dict(value: object) -> dict[str, object] | None:
|
||||
def _as_str_object_dict(value: object) -> dict[str, object] | None: # mutable-ok: parsed dict feeds the JSON log record
|
||||
try:
|
||||
return _STR_OBJECT_DICT_ADAPTER.validate_python(value)
|
||||
except ValidationError:
|
||||
return None
|
||||
|
||||
|
||||
def _as_object_list(value: object) -> list[object] | None:
|
||||
def _as_object_list(value: object) -> list[object] | None: # mutable-ok: tool call list is serialized to JSON
|
||||
try:
|
||||
return _OBJECT_LIST_ADAPTER.validate_python(value)
|
||||
except ValidationError:
|
||||
|
|
@ -120,7 +120,7 @@ def _question_instructions(question_id: str) -> str:
|
|||
)
|
||||
|
||||
|
||||
def _tool_call_entry(tool_call: object) -> dict[str, object] | None:
|
||||
def _tool_call_entry(tool_call: object) -> dict[str, object] | None: # mutable-ok: entry dict is serialized to JSON
|
||||
parsed_call = _as_str_object_dict(tool_call)
|
||||
if parsed_call is None:
|
||||
return None
|
||||
|
|
@ -129,7 +129,9 @@ def _tool_call_entry(tool_call: object) -> dict[str, object] | None:
|
|||
return {"name": fn.get("name"), "arguments": fn.get("arguments")} # mutable-ok: serialized to JSON
|
||||
|
||||
|
||||
def _tool_call_entries(assistant_message: Mapping[str, object]) -> tuple[dict[str, object], ...]:
|
||||
def _tool_call_entries(
|
||||
assistant_message: Mapping[str, object],
|
||||
) -> tuple[dict[str, object], ...]: # mutable-ok: entry dicts are serialized to JSON
|
||||
tool_calls: Final = _as_object_list(assistant_message.get("tool_calls"))
|
||||
if tool_calls is None:
|
||||
return ()
|
||||
|
|
@ -158,7 +160,10 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
max_result_chars_in_state: int | None = None,
|
||||
unreachable_fallback: str | None = None,
|
||||
guardrail_name: str | None = None,
|
||||
event_hook: GuardrailEventHooks | list[GuardrailEventHooks] | Mode | None = None,
|
||||
event_hook: GuardrailEventHooks
|
||||
| list[GuardrailEventHooks]
|
||||
| Mode
|
||||
| None = None, # mutable-ok: event hook unions accept an ordered list
|
||||
default_on: bool = False,
|
||||
async_handler: AsyncHTTPHandler | None = None,
|
||||
) -> None:
|
||||
|
|
@ -190,7 +195,7 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
default_on=default_on,
|
||||
)
|
||||
|
||||
def _handle_failure(self, error: str, log_detail: dict[str, object]) -> None:
|
||||
def _handle_failure(self, error: str, log_detail: dict[str, object]) -> None: # mutable-ok: log detail record
|
||||
"""fail_open logs and returns; fail_closed raises a generic 502 (upstream bodies stay in server logs)."""
|
||||
if self.unreachable_fallback == "fail_open":
|
||||
verbose_proxy_logger.warning(
|
||||
|
|
@ -202,7 +207,9 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
verbose_proxy_logger.error("TypeSafe: %s. detail=%s", error, log_detail)
|
||||
raise HTTPException(status_code=502, detail={"error": error}) # mutable-ok: FastAPI wants a dict detail
|
||||
|
||||
def _candidate_exchanges(self, messages: Sequence[dict[str, object]]) -> tuple[tuple[int, ...], ...]:
|
||||
def _candidate_exchanges(
|
||||
self, messages: Sequence[dict[str, object]]
|
||||
) -> tuple[tuple[int, ...], ...]: # mutable-ok: message dicts come from the request payload
|
||||
"""Completed tool exchanges eligible for evaluation: unprotected, and long enough to be worth a call."""
|
||||
protected: Final = _protected_indices(messages)
|
||||
candidates: Final = tuple(
|
||||
|
|
@ -216,7 +223,9 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
return candidates[-_MAX_EXCHANGES_EVALUATED:]
|
||||
|
||||
@staticmethod
|
||||
def _exchange_tool_text(messages: Sequence[dict[str, object]], group: tuple[int, ...]) -> str:
|
||||
def _exchange_tool_text(
|
||||
messages: Sequence[dict[str, object]], group: tuple[int, ...]
|
||||
) -> str: # mutable-ok: message dicts come from the request payload
|
||||
return "".join(
|
||||
content_to_text(messages[index].get("content"))
|
||||
for index in group[1:]
|
||||
|
|
@ -224,8 +233,10 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
)
|
||||
|
||||
def _build_state(
|
||||
self, messages: Sequence[dict[str, object]], candidates: tuple[tuple[int, ...], ...]
|
||||
) -> dict[str, object]:
|
||||
self,
|
||||
messages: Sequence[dict[str, object]],
|
||||
candidates: tuple[tuple[int, ...], ...], # mutable-ok: candidate groups index request message dicts
|
||||
) -> dict[str, object]: # mutable-ok: result dict feeds the JSON log record
|
||||
task: Final = next(
|
||||
(
|
||||
content_to_text(messages[index].get("content"))
|
||||
|
|
@ -249,7 +260,9 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
return {"task": task, "system": system, "tool_exchanges": tool_exchanges} # mutable-ok: serialized to JSON
|
||||
|
||||
async def _call_systemone(
|
||||
self, state: dict[str, object], question_ids: Sequence[str]
|
||||
self,
|
||||
state: dict[str, object],
|
||||
question_ids: Sequence[str], # mutable-ok: state dict is the parsed log record
|
||||
) -> _JevSystemOneResponse | None:
|
||||
"""Returns the response, or None when the service failed and fail_open applies."""
|
||||
payload: Final[dict[str, object]] = { # mutable-ok: serialized to JSON by httpx
|
||||
|
|
@ -275,8 +288,8 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as e:
|
||||
detail: Final[dict[str, object]] = (
|
||||
except Exception as e: # noqa: BLE001 # a logging-path failure must never break the guardrail call
|
||||
detail: Final[dict[str, object]] = ( # mutable-ok: log detail record
|
||||
{ # mutable-ok: log detail record
|
||||
"error_type": type(e).__name__,
|
||||
"detail": str(e),
|
||||
|
|
@ -318,7 +331,7 @@ class TypeSafeGuardrail(CustomGuardrail):
|
|||
async def apply_guardrail(
|
||||
self,
|
||||
inputs: GenericGuardrailAPIInputs,
|
||||
request_data: dict[str, object],
|
||||
request_data: dict[str, object], # mutable-ok: request-shaped dict
|
||||
input_type: Literal["request", "response"],
|
||||
logging_obj: LiteLLMLoggingObj | None = None,
|
||||
) -> GenericGuardrailAPIInputs:
|
||||
|
|
|
|||
|
|
@ -356,7 +356,8 @@ async def add_team_callbacks(
|
|||
decrypted_logging: Final = decrypt_callback_vars(team_metadata).get("logging")
|
||||
stored_entries: Final = decrypted_logging if isinstance(decrypted_logging, list) else ()
|
||||
stored_entry_vars: Final = [ # mutable-ok: read-only input to the check, never stored
|
||||
entry.get("callback_vars") or {} for entry in stored_entries
|
||||
entry.get("callback_vars") or {}
|
||||
for entry in stored_entries # mutable-ok: callback settings dict is assembled from the request body
|
||||
]
|
||||
family_error: Final = cross_entry_family_error(data.callback_vars, stored_entry_vars)
|
||||
if family_error is not None:
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class TypeSafePassthroughLoggingHandler:
|
|||
cache_hit: bool,
|
||||
request_body: Mapping[str, object],
|
||||
custom_llm_provider: str,
|
||||
**kwargs: object,
|
||||
**kwargs: object, # kwargs-ok: logging handler receives the standard logging kwargs
|
||||
) -> PassThroughEndpointLoggingTypedDict:
|
||||
response: Final = _parse_typesafe_response(response_body)
|
||||
response_model: Final = response.model
|
||||
|
|
|
|||
|
|
@ -93,10 +93,12 @@ class _LiteLLMParamsDictView:
|
|||
return dict(self._params)
|
||||
|
||||
|
||||
async def _push_increments_to_redis(redis_cache: RedisCache, queued: list[RedisPipelineIncrementOperation]) -> None:
|
||||
async def _push_increments_to_redis(
|
||||
redis_cache: RedisCache, queued: list[RedisPipelineIncrementOperation]
|
||||
) -> None: # mutable-ok: pipeline ops list is built once and consumed by redis
|
||||
try:
|
||||
await redis_cache.async_increment_pipeline(increment_list=queued)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa: BLE001 # a budget push failure must not abort the pipeline
|
||||
log_redis_failure(verbose_router_logger, logging.ERROR, "Error syncing in-memory cache with Redis", e)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ def _human_text(content: object, marker_pairs: tuple[tuple[str, str], ...] = _DE
|
|||
def _encrypted_classifier_task(
|
||||
request_kwargs: Mapping[str, object] | None,
|
||||
marker_pairs: tuple[tuple[str, str], ...],
|
||||
) -> dict[str, object] | None:
|
||||
) -> dict[str, object] | None: # mutable-ok: task dict mirrors the request payload shape
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import resolve_structured_messages
|
||||
|
||||
raw_input: Final = (request_kwargs or EMPTY_MAPPING).get("input")
|
||||
|
|
@ -511,7 +511,9 @@ def _encrypted_classifier_task(
|
|||
(
|
||||
item
|
||||
for item in reversed(items)
|
||||
if (messages := resolve_structured_messages(messages=None, request_kwargs={"input": [item]}))
|
||||
if (
|
||||
messages := resolve_structured_messages(messages=None, request_kwargs={"input": [item]})
|
||||
) # mutable-ok: request-shaped literal
|
||||
and any(_iter_human_asks_newest_first(messages, marker_pairs))
|
||||
),
|
||||
None,
|
||||
|
|
@ -524,9 +526,11 @@ def _encrypted_classifier_task(
|
|||
return None
|
||||
if not any(part.get("type") == "encrypted_content" and part.get("encrypted_content") for part in parts):
|
||||
return None
|
||||
return {
|
||||
return { # mutable-ok: request-shaped dict
|
||||
**current,
|
||||
"content": [part for part in parts if part.get("type") in ("input_text", "encrypted_content")],
|
||||
"content": [
|
||||
part for part in parts if part.get("type") in ("input_text", "encrypted_content")
|
||||
], # mutable-ok: request-shaped dict
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2133,7 +2137,9 @@ class ComplexityRouter(CustomLogger):
|
|||
if llm_config is None or classifier_system_prompt is None or classifier_response_format is None:
|
||||
raise ValueError("classifier_llm_config is not set")
|
||||
|
||||
marker_pairs: Final = self._reminder_markers_for_request(request_kwargs or {})
|
||||
marker_pairs: Final = self._reminder_markers_for_request(
|
||||
request_kwargs or {}
|
||||
) # mutable-ok: empty kwargs fallback
|
||||
encrypted_task: Final = _encrypted_classifier_task(request_kwargs, marker_pairs)
|
||||
user_payload: Final = self._classifier_context_payload(
|
||||
prompt, system_prompt, request_kwargs, messages, encrypted_task=encrypted_task is not None
|
||||
|
|
|
|||
|
|
@ -1102,7 +1102,9 @@ def test_async_http_handler(mock_async_client):
|
|||
concurrent_limit = 2
|
||||
|
||||
# Mock the transport creation to return a specific transport
|
||||
with mock.patch.object(AsyncHTTPHandler, "_create_async_transport") as mock_create_transport:
|
||||
with mock.patch.object(
|
||||
AsyncHTTPHandler, "_create_async_transport"
|
||||
) as mock_create_transport: # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock_transport = mock.MagicMock()
|
||||
mock_create_transport.return_value = mock_transport
|
||||
|
||||
|
|
@ -1548,7 +1550,9 @@ def test_get_valid_models_openai_proxy(monkeypatch):
|
|||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = mock_response_data
|
||||
|
||||
with patch.object(litellm.module_level_client, "get", return_value=mock_response) as mock_post:
|
||||
with patch.object(
|
||||
litellm.module_level_client, "get", return_value=mock_response
|
||||
) as mock_post: # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
valid_models = get_valid_models(check_provider_endpoint=True)
|
||||
assert "litellm_proxy/gpt-5.5" in valid_models
|
||||
|
||||
|
|
@ -1625,7 +1629,9 @@ def test_get_valid_models_fireworks_ai(monkeypatch):
|
|||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = mock_response_data
|
||||
|
||||
with patch.object(litellm.module_level_client, "get", return_value=mock_response) as mock_post:
|
||||
with patch.object(
|
||||
litellm.module_level_client, "get", return_value=mock_response
|
||||
) as mock_post: # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
valid_models = get_valid_models(check_provider_endpoint=True)
|
||||
print("valid_models", valid_models)
|
||||
mock_post.assert_called_once()
|
||||
|
|
|
|||
|
|
@ -308,22 +308,21 @@ class TestModelManagementAuthChecks:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_new_model_rejects_credential_attach_for_non_admin(self):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
add_new_model,
|
||||
)
|
||||
|
||||
mock_prisma = MagicMock()
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", mock_prisma
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: prior auth check needs a live DB; only the credential check is under test
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call",
|
||||
new=AsyncMock(return_value=None),
|
||||
|
|
@ -345,7 +344,6 @@ class TestModelManagementAuthChecks:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_model_rejects_credential_attach_for_non_admin(self):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
patch_model,
|
||||
)
|
||||
|
|
@ -358,18 +356,18 @@ class TestModelManagementAuthChecks:
|
|||
model_info={"id": model_id},
|
||||
)
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", MagicMock()
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.llm_router", MagicMock()
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: stubs the DB row fetch; only the credential check is under test
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.get_db_model",
|
||||
new=AsyncMock(return_value=db_model),
|
||||
|
|
@ -990,9 +988,6 @@ class TestUpdateModel:
|
|||
model-level guardrails (and any other litellm_params change) silently no-op
|
||||
until the APScheduler reload tick fires ~30 s later.
|
||||
"""
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
update_model,
|
||||
)
|
||||
from litellm.types.router import (
|
||||
ModelInfo,
|
||||
updateDeployment,
|
||||
|
|
@ -1354,7 +1349,9 @@ class TestTeamModelUpdate:
|
|||
patch(
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.team_model_add"
|
||||
) as mock_team_model_add,
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.update_team") as mock_update_team,
|
||||
patch(
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.update_team"
|
||||
) as mock_update_team, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
):
|
||||
result = await _update_team_model_in_db(
|
||||
db_model=db_model,
|
||||
|
|
@ -1409,7 +1406,9 @@ class TestTeamModelUpdate:
|
|||
)
|
||||
|
||||
with (
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete") as mock_delete,
|
||||
patch(
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete"
|
||||
) as mock_delete, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_add") as mock_add,
|
||||
):
|
||||
await _update_existing_team_model_assignment(
|
||||
|
|
@ -1450,7 +1449,9 @@ class TestTeamModelUpdate:
|
|||
)
|
||||
|
||||
with (
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete") as mock_delete,
|
||||
patch(
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete"
|
||||
) as mock_delete, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_add") as mock_add,
|
||||
):
|
||||
await _update_existing_team_model_assignment(
|
||||
|
|
@ -1505,9 +1506,9 @@ class TestTeamModelUpdate:
|
|||
"litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.allow_team_model_action",
|
||||
AsyncMock(return_value=True),
|
||||
),
|
||||
patch(
|
||||
patch( # test-quality-ok: team models are premium-gated through a proxy global with no injection seam
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: team models are premium-gated through a proxy global with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: the team list write is the collaborator whose ordering is asserted
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.team_model_add",
|
||||
side_effect=team_add,
|
||||
|
|
@ -1567,7 +1568,9 @@ class TestTeamModelUpdate:
|
|||
)
|
||||
|
||||
with (
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete") as mock_delete,
|
||||
patch(
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete"
|
||||
) as mock_delete, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_add") as mock_add,
|
||||
):
|
||||
await _update_existing_team_model_assignment(
|
||||
|
|
@ -3248,9 +3251,6 @@ class TestPatchModelBlockedAuthGate:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_team_admin_cannot_toggle_blocked(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
patch_model,
|
||||
)
|
||||
|
||||
non_admin = UserAPIKeyAuth(
|
||||
user_id="team_admin",
|
||||
|
|
@ -3290,9 +3290,6 @@ class TestPatchModelBlockedAuthGate:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_proxy_admin_can_toggle_blocked(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
patch_model,
|
||||
)
|
||||
|
||||
admin = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN)
|
||||
existing_row = MagicMock()
|
||||
|
|
@ -3340,9 +3337,6 @@ class TestPatchModelRowDeletedBeforeWrite:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_model_404s_when_update_returns_none(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
patch_model,
|
||||
)
|
||||
from litellm.proxy.proxy_server import ProxyException
|
||||
|
||||
admin = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN)
|
||||
|
|
@ -3360,18 +3354,18 @@ class TestPatchModelRowDeletedBeforeWrite:
|
|||
mock_prisma.db.litellm_proxymodeltable.update = AsyncMock(return_value=None)
|
||||
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.prisma_client", mock_prisma
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.llm_router", MagicMock(**{"get_model_ids.return_value": ["m1"]})
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
),
|
||||
patch( # test-quality-ok: stubs the auth gate so the test exercises the not-found branch under test
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call",
|
||||
new=AsyncMock(return_value=None),
|
||||
|
|
@ -3438,7 +3432,6 @@ class TestWriteSurfacesReloadDrop:
|
|||
|
||||
def test_raise_if_reload_degraded_serving_contract(self, monkeypatch):
|
||||
import litellm
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
raise_if_reload_degraded_serving,
|
||||
)
|
||||
|
|
@ -3484,7 +3477,6 @@ class TestWriteSurfacesReloadDrop:
|
|||
all the desired set is unknown, so every drop is reported.
|
||||
"""
|
||||
import litellm
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
raise_if_reload_degraded_serving,
|
||||
reload_serving_verdict,
|
||||
|
|
@ -3630,7 +3622,6 @@ class TestConcurrentModelWritesDoNotEvictEachOther:
|
|||
request's reload for it is the 500 that made concurrent model creates fail.
|
||||
"""
|
||||
import litellm
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
raise_if_reload_degraded_serving,
|
||||
reload_serving_verdict,
|
||||
|
|
@ -4121,7 +4112,6 @@ class TestStrategyRouterWriteValidation:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_model_rejects_double_prefix(self):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
patch_model,
|
||||
)
|
||||
|
|
@ -4161,7 +4151,6 @@ class TestStrategyRouterWriteValidation:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_new_model_rejects_prefixed_model_without_config(self):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
add_new_model,
|
||||
)
|
||||
|
|
@ -4526,12 +4515,12 @@ class TestStrategyRouterWriteValidation:
|
|||
self._live_router_holding_one_capability(limit, config_config) if config_config is not None else None
|
||||
)
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: the guard reads the proxy license singleton with no injection seam
|
||||
"litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: limit
|
||||
), # test-quality-ok: the guard reads the proxy license singleton with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: the guard reads the proxy router global with no injection seam
|
||||
"litellm.proxy.proxy_server.llm_router", live_router
|
||||
), # test-quality-ok: the guard reads the proxy router global with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: the cross-pod publish is the side effect under test; redis is not configured here
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.publish_config_change",
|
||||
new=AsyncMock(),
|
||||
|
|
@ -4615,7 +4604,6 @@ class TestStrategyRouterWriteValidation:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_new_model_refuses_a_second_heuristic_v2_router_before_the_db_write(self) -> None:
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
add_new_model,
|
||||
)
|
||||
|
|
@ -4624,21 +4612,21 @@ class TestStrategyRouterWriteValidation:
|
|||
fake = self._FakeDb(["auto_router/complexity_router"])
|
||||
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", fake
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.llm_router", None
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: 1
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: prior auth check needs a live DB; only the license limit is under test
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call",
|
||||
new=AsyncMock(return_value=None),
|
||||
|
|
@ -4666,7 +4654,6 @@ class TestStrategyRouterWriteValidation:
|
|||
@pytest.mark.asyncio
|
||||
async def test_model_less_patch_rejects_router_config_on_a_regular_model(self) -> None:
|
||||
"""PATCH rejects the poison before its row write or the capability slot."""
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import patch_model
|
||||
from litellm.types.router import updateLiteLLMParams
|
||||
|
||||
|
|
@ -4679,18 +4666,18 @@ class TestStrategyRouterWriteValidation:
|
|||
)
|
||||
fake = self._FakeDb([])
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", fake
|
||||
), # test-quality-ok: endpoint reads proxy globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: config-based lookup must be absent to drive the stored-row branch
|
||||
"litellm.proxy.proxy_server.llm_router", None
|
||||
), # test-quality-ok: config-based lookup must be absent to drive the stored-row branch
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reaches its DB-write branch only with this process setting
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reaches its DB-write branch only with this process setting
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: authorization branch reads the proxy-wide premium flag
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: authorization branch reads the proxy-wide premium flag
|
||||
),
|
||||
patch( # test-quality-ok: inject stored regular row without a database
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.get_db_model",
|
||||
new=AsyncMock(return_value=regular),
|
||||
|
|
@ -4718,7 +4705,6 @@ class TestStrategyRouterWriteValidation:
|
|||
@pytest.mark.asyncio
|
||||
async def test_model_less_legacy_update_rejects_router_config_on_a_regular_model(self) -> None:
|
||||
"""The legacy update endpoint enforces the same boundary before its row write or slot."""
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import update_model
|
||||
from litellm.types.router import ModelInfo, updateLiteLLMParams
|
||||
|
||||
|
|
@ -4734,18 +4720,18 @@ class TestStrategyRouterWriteValidation:
|
|||
existing_row.litellm_params = regular.litellm_params.model_dump()
|
||||
fake = self._FakeDb([], existing_row=existing_row)
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", fake
|
||||
), # test-quality-ok: endpoint reads proxy globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: config-based lookup must be absent to drive the stored-row branch
|
||||
"litellm.proxy.proxy_server.llm_router", None
|
||||
), # test-quality-ok: config-based lookup must be absent to drive the stored-row branch
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reaches its DB-write branch only with this process setting
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reaches its DB-write branch only with this process setting
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: authorization branch reads the proxy-wide premium flag
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: authorization branch reads the proxy-wide premium flag
|
||||
),
|
||||
patch( # test-quality-ok: endpoint must reject before database authorization needs a live store
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call",
|
||||
new=AsyncMock(return_value=None),
|
||||
|
|
@ -4771,9 +4757,6 @@ class TestStrategyRouterWriteValidation:
|
|||
"""patch_model relays HTTPException as-is, so the license refusal reaches the client as a plain 403."""
|
||||
from fastapi import HTTPException
|
||||
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
patch_model,
|
||||
)
|
||||
from litellm.types.router import updateLiteLLMParams
|
||||
|
||||
model_id = "other-id"
|
||||
|
|
@ -4781,21 +4764,21 @@ class TestStrategyRouterWriteValidation:
|
|||
fake = self._FakeDb(["auto_router/complexity_router"])
|
||||
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", fake
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.llm_router", None
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: 1
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: the write must be refused before this DB step runs
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.get_db_model",
|
||||
new=AsyncMock(return_value=self._db_complexity_router(model_id)),
|
||||
|
|
@ -4821,7 +4804,6 @@ class TestStrategyRouterWriteValidation:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_model_refuses_switching_another_router_to_heuristic_v2(self) -> None:
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
update_model,
|
||||
)
|
||||
|
|
@ -4843,21 +4825,21 @@ class TestStrategyRouterWriteValidation:
|
|||
fake = self._FakeDb(["auto_router/complexity_router"], existing_row=existing_row)
|
||||
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.prisma_client", fake
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.llm_router", None
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
"litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: 1
|
||||
), # test-quality-ok: endpoint reads proxy server globals with no injection seam
|
||||
),
|
||||
patch( # test-quality-ok: prior auth check needs a live DB; only the license limit is under test
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call",
|
||||
new=AsyncMock(return_value=None),
|
||||
|
|
@ -4877,7 +4859,6 @@ class TestStrategyRouterWriteValidation:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_model_rejects_prefix_strip(self):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
update_model,
|
||||
)
|
||||
|
|
@ -4980,7 +4961,6 @@ class TestAutoRouterClassifierDefaultPrompt:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_negative_context_window_size_is_rejected(self):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
get_auto_router_classifier_default_prompt,
|
||||
)
|
||||
|
|
@ -5124,7 +5104,6 @@ class TestAutoRouterClassifierDefaultPrompt:
|
|||
],
|
||||
)
|
||||
def test_built_in_preview_rejects_the_same_invalid_labels_as_get(self, tier_labels):
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
AutoRouterClassifierPromptPreviewRequest,
|
||||
preview_auto_router_classifier_prompt,
|
||||
|
|
@ -5230,7 +5209,6 @@ class TestAutoRouterClassifierDefaultPrompt:
|
|||
async def test_malformed_tier_labels_are_rejected_rather_than_silently_ignored(self):
|
||||
"""An unparseable or invalid rename must not fall back to the canonical classification_rubric: that would
|
||||
prefill tier names the router does not accept while looking like it worked."""
|
||||
from litellm.proxy._types import ProxyException
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import (
|
||||
get_auto_router_classifier_default_prompt,
|
||||
)
|
||||
|
|
@ -5282,7 +5260,6 @@ class TestEnforceRpmTpmOnModelAdd:
|
|||
],
|
||||
)
|
||||
def test_raises_when_enabled_and_missing(self, params, expected_missing):
|
||||
from litellm.proxy._types import ProxyException
|
||||
|
||||
with pytest.raises(ProxyException) as exc_info:
|
||||
_raise_if_rate_limits_required_but_missing(litellm_params=params, enforced=True)
|
||||
|
|
@ -5322,19 +5299,19 @@ class TestBlockModelResponseSerialization:
|
|||
app.dependency_overrides[ps.user_api_key_auth] = lambda: admin
|
||||
try:
|
||||
with (
|
||||
patch(
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.prisma_client", mock_prisma
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
patch(
|
||||
),
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.store_model_in_db", True
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
),
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.llm_router",
|
||||
MagicMock(**{"get_model_ids.return_value": ["m-block-1"]}),
|
||||
),
|
||||
patch(
|
||||
patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
"litellm.proxy.proxy_server.redis_usage_cache", None
|
||||
), # test-quality-ok: proxy_server module global is the endpoint's only injection point
|
||||
),
|
||||
patch( # test-quality-ok: stubs the cache write so the test observes only response serialization
|
||||
"litellm.proxy.management_endpoints.model_management_endpoints.clear_cache",
|
||||
new=AsyncMock(return_value=ReconcileOutcome(still_desired=None, live_after=None)),
|
||||
|
|
@ -5424,7 +5401,6 @@ class TestAccessGroupModelSync:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_model_rename_rewrites_the_groups_that_named_the_model(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import patch_model
|
||||
|
||||
mock_prisma = self._prisma_with_row("m-rename", "gpt-5.6", deployment_count=0)
|
||||
router = MagicMock()
|
||||
|
|
@ -5446,7 +5422,6 @@ class TestAccessGroupModelSync:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_model_rename_appends_when_a_sibling_deployment_keeps_the_old_name(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import patch_model
|
||||
|
||||
mock_prisma = self._prisma_with_row("m-rename", "gpt-5.6", deployment_count=1)
|
||||
router = MagicMock()
|
||||
|
|
@ -5465,7 +5440,6 @@ class TestAccessGroupModelSync:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_model_without_a_rename_leaves_access_groups_alone(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import patch_model
|
||||
|
||||
mock_prisma = self._prisma_with_row("m-same", "gpt-5.6", deployment_count=0)
|
||||
router = MagicMock()
|
||||
|
|
@ -5511,7 +5485,6 @@ class TestAccessGroupModelSync:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_model_persists_a_new_model_name_and_rewrites_the_groups(self):
|
||||
from litellm.proxy.management_endpoints.model_management_endpoints import update_model
|
||||
from litellm.types.router import ModelInfo, updateLiteLLMParams
|
||||
|
||||
mock_prisma = self._prisma_with_row("m-terraform", "gpt-5.6", deployment_count=0)
|
||||
|
|
|
|||
|
|
@ -274,7 +274,9 @@ class TestVertexAIPassThroughHandler:
|
|||
test_token = vertex_credentials
|
||||
|
||||
with (
|
||||
mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth,
|
||||
mock.patch(
|
||||
"litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth"
|
||||
) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock.patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route"
|
||||
) as mock_create_route,
|
||||
|
|
@ -375,7 +377,9 @@ class TestVertexAIPassThroughHandler:
|
|||
test_token = vertex_credentials
|
||||
|
||||
with (
|
||||
mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth,
|
||||
mock.patch(
|
||||
"litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth"
|
||||
) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock.patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route"
|
||||
) as mock_create_route,
|
||||
|
|
@ -473,7 +477,9 @@ class TestVertexAIPassThroughHandler:
|
|||
mock_response = Response()
|
||||
|
||||
with (
|
||||
mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth,
|
||||
mock.patch(
|
||||
"litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth"
|
||||
) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock.patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route"
|
||||
) as mock_create_route,
|
||||
|
|
@ -615,7 +621,9 @@ class TestVertexAIPassThroughHandler:
|
|||
mock_request.method = "POST"
|
||||
mock_response = Mock()
|
||||
|
||||
with patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth") as mock_auth:
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth"
|
||||
) as mock_auth: # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock_auth.return_value = {"api_key": "test-key-123"}
|
||||
|
||||
with patch(
|
||||
|
|
@ -1196,7 +1204,9 @@ class TestVertexAIDiscoveryPassThroughHandler:
|
|||
test_token = "test-auth-token"
|
||||
|
||||
with (
|
||||
mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth,
|
||||
mock.patch(
|
||||
"litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth"
|
||||
) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock.patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route"
|
||||
) as mock_create_route,
|
||||
|
|
@ -1257,7 +1267,9 @@ class TestVertexAIDiscoveryPassThroughHandler:
|
|||
mock_request.method = "POST"
|
||||
mock_response = Mock()
|
||||
|
||||
with patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth") as mock_auth:
|
||||
with patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth"
|
||||
) as mock_auth: # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock_auth.return_value = {"api_key": "test-key-123"}
|
||||
|
||||
with patch(
|
||||
|
|
@ -1851,7 +1863,9 @@ class TestLLMPassthroughFactoryProxyRoute:
|
|||
mock_user_api_key_dict = MagicMock()
|
||||
|
||||
with (
|
||||
patch("litellm.utils.ProviderConfigManager.get_provider_model_info") as mock_get_provider,
|
||||
patch(
|
||||
"litellm.utils.ProviderConfigManager.get_provider_model_info"
|
||||
) as mock_get_provider, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials"
|
||||
) as mock_get_creds,
|
||||
|
|
@ -1898,9 +1912,9 @@ class TestVLLMProxyRoute:
|
|||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_passthrough_request_using_router_model",
|
||||
return_value=True,
|
||||
)
|
||||
@patch(
|
||||
@patch( # test-quality-ok: patching litellm internal for unit test isolation
|
||||
"litellm.proxy.proxy_server.llm_router"
|
||||
) # test-quality-ok: patching litellm internal for unit test isolation
|
||||
)
|
||||
async def test_vllm_proxy_route_with_router_model(self, mock_llm_router, mock_is_router, mock_get_body):
|
||||
mock_request = MagicMock(spec=Request)
|
||||
mock_request.method = "POST"
|
||||
|
|
@ -1961,9 +1975,9 @@ class TestGigachatProxyRoute:
|
|||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_passthrough_request_using_router_model",
|
||||
return_value=True,
|
||||
)
|
||||
@patch(
|
||||
@patch( # test-quality-ok: patching litellm internal for unit test isolation
|
||||
"litellm.proxy.proxy_server.llm_router"
|
||||
) # test-quality-ok: patching litellm internal for unit test isolation
|
||||
)
|
||||
async def test_gigachat_proxy_route_with_router_model(self, mock_llm_router, mock_is_router, mock_get_body):
|
||||
mock_request = MagicMock(spec=Request)
|
||||
mock_request.method = "POST"
|
||||
|
|
@ -2498,7 +2512,9 @@ class TestForwardHeaders:
|
|||
mock_httpx_response.aread = AsyncMock(return_value=b'{"result": "success"}')
|
||||
|
||||
with (
|
||||
patch("litellm.utils.ProviderConfigManager.get_provider_model_info") as mock_get_provider,
|
||||
patch(
|
||||
"litellm.utils.ProviderConfigManager.get_provider_model_info"
|
||||
) as mock_get_provider, # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials"
|
||||
) as mock_get_creds,
|
||||
|
|
@ -2821,7 +2837,9 @@ class TestMilvusProxyRoute:
|
|||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_allowed_to_call_vector_store_endpoint"
|
||||
),
|
||||
patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body"),
|
||||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body"
|
||||
), # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch.object(litellm, "vector_store_index_registry") as mock_index_registry,
|
||||
patch.object(litellm, "vector_store_registry") as mock_vector_registry,
|
||||
):
|
||||
|
|
@ -2876,7 +2894,9 @@ class TestMilvusProxyRoute:
|
|||
patch.object(litellm, "vector_store_registry") as mock_vector_registry,
|
||||
):
|
||||
mock_provider_config = MagicMock()
|
||||
mock_provider_config.get_auth_credentials.return_value = {"headers": {}}
|
||||
mock_provider_config.get_auth_credentials.return_value = {
|
||||
"headers": {}
|
||||
} # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
mock_provider_config.get_complete_url.return_value = None
|
||||
mock_get_config.return_value = mock_provider_config
|
||||
|
||||
|
|
@ -2926,7 +2946,9 @@ class TestMilvusProxyRoute:
|
|||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_allowed_to_call_vector_store_endpoint"
|
||||
),
|
||||
patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body"),
|
||||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body"
|
||||
), # test-quality-ok: the patched global is the collaborator's only injection seam
|
||||
patch(
|
||||
"litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route"
|
||||
) as mock_create_route,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import asyncio
|
|||
import logging
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
from typing import Dict, List
|
||||
from typing import Dict, Final, List
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
|
|
|||
|
|
@ -269,7 +269,6 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
const explicitlySupportedClassifierEfforts = effortOptionsByModel[classifierModel];
|
||||
|
||||
const handleClassifierTypeChange = (classifierType: ClassifierType) => {
|
||||
|
||||
onChange(transitionClassifierType(value, classifierType));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -140,8 +140,7 @@ export interface StoredComplexityRouterConfig {
|
|||
*/
|
||||
const asNumber = (v: unknown): number | undefined => (typeof v === "number" ? v : undefined);
|
||||
const asBoolean = (v: unknown): boolean | undefined => (typeof v === "boolean" ? v : undefined);
|
||||
const asNonEmptyString = (v: unknown): string | undefined =>
|
||||
typeof v === "string" && v.trim() !== "" ? v : undefined;
|
||||
const asNonEmptyString = (v: unknown): string | undefined => (typeof v === "string" && v.trim() !== "" ? v : undefined);
|
||||
|
||||
export const hydrateComplexityRouterConfig = (
|
||||
parsedConfig: StoredComplexityRouterConfig,
|
||||
|
|
@ -173,9 +172,7 @@ export const hydrateComplexityRouterConfig = (
|
|||
classifier_context_window_size: asNumber(parsedConfig.classifier_context_window_size),
|
||||
classifier_context_budget_chars: asNumber(parsedConfig.classifier_context_budget_chars),
|
||||
classifier_context_per_turn_chars: asNumber(parsedConfig.classifier_context_per_turn_chars),
|
||||
classifier_context_include_assistant_turns: asBoolean(
|
||||
parsedConfig.classifier_context_include_assistant_turns,
|
||||
),
|
||||
classifier_context_include_assistant_turns: asBoolean(parsedConfig.classifier_context_include_assistant_turns),
|
||||
classifier_fallback:
|
||||
parsedConfig.classifier_fallback === "default_model" || parsedConfig.classifier_fallback === "heuristic"
|
||||
? parsedConfig.classifier_fallback
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue