mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(proxy): log the internal model access denial reason for MCP sampling denials
MCP sampling catches the denial itself and returns ErrorData, so the central ProxyException handler never sees it. Log the sanitized internal reason there and share the CR/LF stripping through ModelAccessDeniedProxyException.sanitized_internal_message Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
b7af51cc4a
commit
241b177f05
4 changed files with 26 additions and 2 deletions
|
|
@ -771,6 +771,7 @@ async def _check_model_access(model: str, user_api_key_auth: "UserAPIKeyAuth | N
|
|||
|
||||
try:
|
||||
import litellm
|
||||
from litellm.proxy._types import ModelAccessDeniedProxyException
|
||||
from litellm.proxy.auth.auth_checks import (
|
||||
_check_team_member_model_access,
|
||||
can_key_call_model,
|
||||
|
|
@ -887,7 +888,9 @@ async def _check_model_access(model: str, user_api_key_auth: "UserAPIKeyAuth | N
|
|||
verbose_logger.warning(
|
||||
"MCP sampling: model access denied for model=%s: %s",
|
||||
model,
|
||||
access_err,
|
||||
access_err.sanitized_internal_message()
|
||||
if isinstance(access_err, ModelAccessDeniedProxyException)
|
||||
else access_err,
|
||||
)
|
||||
return ErrorData(
|
||||
code=-1,
|
||||
|
|
|
|||
|
|
@ -4042,6 +4042,9 @@ class ModelAccessDeniedProxyException(ProxyException):
|
|||
super().__init__(message=message, type=type, param=param, code=code)
|
||||
self.internal_message: Final = internal_message
|
||||
|
||||
def sanitized_internal_message(self) -> str:
|
||||
return self.internal_message.replace("\r", "").replace("\n", "")
|
||||
|
||||
|
||||
class CommonProxyErrors(str, enum.Enum):
|
||||
db_not_connected_error = (
|
||||
|
|
|
|||
|
|
@ -1684,7 +1684,7 @@ async def openai_exception_handler(request: Request, exc: ProxyException):
|
|||
def _log_model_access_denial(exc: ProxyException) -> None:
|
||||
if not litellm.model_access_denied_message or not isinstance(exc, ModelAccessDeniedProxyException):
|
||||
return
|
||||
verbose_proxy_logger.warning(exc.internal_message.replace("\r", "").replace("\n", ""))
|
||||
verbose_proxy_logger.warning(exc.sanitized_internal_message())
|
||||
|
||||
|
||||
def _close_dangling_otel_server_span(request: Request, status_code: int, exc: Exception | None = None) -> None:
|
||||
|
|
|
|||
|
|
@ -137,6 +137,24 @@ class TestCheckModelAccess:
|
|||
assert result.code == -1
|
||||
assert "claude-3-opus-20240229" in result.message
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_should_log_internal_denial_reason_when_client_message_is_configured(self, monkeypatch, caplog):
|
||||
import litellm
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
|
||||
monkeypatch.setattr(litellm, "model_access_denied_message", "The model `{model}` is unavailable for this API key.")
|
||||
auth = UserAPIKeyAuth(api_key="sk-test-key", models=["gpt-3.5-turbo"])
|
||||
|
||||
with caplog.at_level("WARNING", logger="LiteLLM"):
|
||||
result = await _check_model_access("gpt-4o\r\nforged", user_api_key_auth=auth)
|
||||
|
||||
assert result is not None
|
||||
assert "gpt-4o\r\nforged" in result.message
|
||||
assert "gpt-3.5-turbo" not in result.message
|
||||
denial_records = [r for r in caplog.records if "gpt-3.5-turbo" in r.getMessage()]
|
||||
assert len(denial_records) == 1
|
||||
assert "Tried to access gpt-4oforged" in denial_records[0].getMessage()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_should_deny_empty_oauth_passthrough_placeholder(self):
|
||||
"""Regression: process_mcp_request() returns an empty UserAPIKeyAuth()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue