greptile bug fixes

This commit is contained in:
Alejandro Tapia 2026-03-03 15:30:01 -08:00
parent 0d7de1c744
commit 80d96628b4
5 changed files with 125 additions and 6 deletions

View file

@ -587,7 +587,9 @@ def process_callback(
def normalize_callback_names(
callbacks: Iterable[Union[str, object]],
callbacks: Iterable[Union[str, object]] | None,
) -> List[Union[str, object]]:
"""Lowercase string callback names; pass through non-strings (e.g. from config) unchanged."""
if callbacks is None:
return []
return [c.lower() if isinstance(c, str) else c for c in callbacks]

View file

@ -3620,9 +3620,13 @@ class ProxyConfig:
merged_failure = list(
dict.fromkeys(failure_from_settings + (failure_callbacks or []))
)
merged_callbacks = list(
dict.fromkeys(callbacks_from_settings + (callbacks or []))
)
seen_both = {c if isinstance(c, str) else next(iter(c)) for c in callbacks_from_settings}
merged_callbacks: list = list(callbacks_from_settings)
for c in (callbacks or []):
name = c if isinstance(c, str) else next(iter(c.keys()))
if name not in seen_both:
merged_callbacks.append(c)
seen_both.add(name)
for success_callback in merged_success:
self._add_callback_from_db_to_in_memory_litellm_callbacks(
@ -11666,7 +11670,11 @@ async def get_config(): # noqa: PLR0915
(_callback_name, _callback_params), = _callback.items()
_callback = _callback_name
elif isinstance(_callback, str) and _callback in _callback_settings:
_callback_params = _callback_settings.get(_callback)
_raw = _callback_settings.get(_callback) or {}
_callback_params = (
{k: v for k, v in _raw.items() if k not in ("callback_type", "event_types")}
or None
)
_data_to_return.append(
process_callback(
_callback,

View file

@ -122,6 +122,12 @@ def test_normalize_callback_names_lowercases_strings():
assert normalize_callback_names(["SQS", "S3", "CUSTOM_CALLBACK"]) == ["sqs", "s3", "custom_callback"]
def test_normalize_callback_names_none_returns_empty_list():
"""None and empty list return [] to support callers that may pass unguarded None."""
assert normalize_callback_names(None) == []
assert normalize_callback_names([]) == []
def test_remaining_tokens_requests_returns_empty_when_metadata_empty_or_missing():
"""Empty or missing metadata returns {}."""
assert get_remaining_tokens_and_requests_from_request_data({}) == {}

View file

@ -2985,6 +2985,109 @@ def test_root_redirect_when_docs_url_not_root_and_redirect_url_set(monkeypatch):
assert response.headers["location"] == test_redirect_url
@pytest.mark.asyncio
async def test_get_image_non_root_uses_var_lib_assets_dir(monkeypatch):
"""
Test that get_image uses /var/lib/litellm/assets when LITELLM_NON_ROOT is true.
get_image only calls makedirs when the assets dir does not exist, so we must
have exists() return False for that path to trigger the create-dir branch.
"""
from unittest.mock import patch
from litellm.proxy.proxy_server import get_image
# Set LITELLM_NON_ROOT to true
monkeypatch.setenv("LITELLM_NON_ROOT", "true")
monkeypatch.delenv("UI_LOGO_PATH", raising=False)
def exists_side_effect(path):
# Return False for the assets dir so get_image calls makedirs()
if path == "/var/lib/litellm/assets":
return False
return True
with patch("litellm.proxy.proxy_server.os.makedirs") as mock_makedirs, \
patch("litellm.proxy.proxy_server.os.path.exists", side_effect=exists_side_effect), \
patch("litellm.proxy.proxy_server.os.getenv") as mock_getenv, \
patch("litellm.proxy.proxy_server.FileResponse") as mock_file_response:
# Setup mock_getenv to return empty string for UI_LOGO_PATH
def getenv_side_effect(key, default=""):
if key == "UI_LOGO_PATH":
return ""
elif key == "LITELLM_NON_ROOT":
return "true"
return default
mock_getenv.side_effect = getenv_side_effect
# Call the function
await get_image()
# Verify makedirs was called with /var/lib/litellm/assets
mock_makedirs.assert_called_once_with("/var/lib/litellm/assets", exist_ok=True)
@pytest.mark.asyncio
async def test_get_image_non_root_fallback_to_default_logo(monkeypatch):
"""
Test that get_image falls back to default_site_logo when logo doesn't exist
in /var/lib/litellm/assets for non-root case.
get_image only calls makedirs when the assets dir does not exist, so we must
have exists() return False for that path to trigger the create-dir branch.
"""
from unittest.mock import patch
from litellm.proxy.proxy_server import get_image
# Set LITELLM_NON_ROOT to true
monkeypatch.setenv("LITELLM_NON_ROOT", "true")
monkeypatch.delenv("UI_LOGO_PATH", raising=False)
# Track path.exists calls to verify it checks /var/lib/litellm/assets/logo.jpg
exists_calls = []
def exists_side_effect(path):
exists_calls.append(path)
# Return False for assets dir so get_image calls makedirs()
if path == "/var/lib/litellm/assets":
return False
# Return False for /var/lib/litellm/assets/logo.jpg to trigger fallback
if "/var/lib/litellm/assets/logo.jpg" in path or path == "/var/lib/litellm/assets/logo.jpg":
return False
return True
# Mock os.path operations
with patch("litellm.proxy.proxy_server.os.makedirs") as mock_makedirs, \
patch("litellm.proxy.proxy_server.os.path.exists", side_effect=exists_side_effect), \
patch("litellm.proxy.proxy_server.os.getenv") as mock_getenv, \
patch("litellm.proxy.proxy_server.FileResponse") as mock_file_response:
# Setup mock_getenv
def getenv_side_effect(key, default=""):
if key == "UI_LOGO_PATH":
return ""
elif key == "LITELLM_NON_ROOT":
return "true"
return default
mock_getenv.side_effect = getenv_side_effect
# Call the function
await get_image()
# Verify makedirs was called with /var/lib/litellm/assets
mock_makedirs.assert_called_once_with("/var/lib/litellm/assets", exist_ok=True)
# Verify that exists was called to check /var/lib/litellm/assets/logo.jpg
assets_logo_path = "/var/lib/litellm/assets/logo.jpg"
assert any(assets_logo_path in str(call) for call in exists_calls), \
f"Should check if {assets_logo_path} exists"
# Verify FileResponse was called (with fallback logo)
assert mock_file_response.called, "FileResponse should be called"
@pytest.mark.asyncio
async def test_get_image_root_case_uses_current_dir(monkeypatch):
"""

View file

@ -340,7 +340,7 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
setIsAddingCallback(true);
}
const payload = buildCallbackPayload(formValues, callbackName, callbacks, isEdit, undefined, callbackSettings);
const payload = buildCallbackPayload(formValues, callbackName, callbacks, isEdit, selectedEditCallback?.type as "success" | "failure" | "success_and_failure" | undefined, callbackSettings);
try {
await setCallbacksCall(accessToken, payload);