diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index e1d5a90dc79..fd576ee7c02 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -9477,7 +9477,19 @@ async def get_config(): # noqa: PLR0915 elif _callback == "traceloop": env_vars = ["TRACELOOP_API_KEY"] elif _callback == "custom_callback_api": - env_vars = ["GENERIC_LOGGER_ENDPOINT"] + custom_callback_url = environment_variables.get( + "custom_callback_api_url" + ) + custom_callback_headers = environment_variables.get( + "custom_callback_api_headers" + ) + if custom_callback_url is not None and custom_callback_headers is not None: + env_vars = [ + "custom_callback_api_url", + "custom_callback_api_headers", + ] + else: + env_vars = ["GENERIC_LOGGER_ENDPOINT", "GENERIC_LOGGER_HEADER"] elif _callback == "otel": env_vars = ["OTEL_EXPORTER", "OTEL_ENDPOINT", "OTEL_HEADERS"] elif _callback == "langsmith": @@ -9495,11 +9507,7 @@ async def get_config(): # noqa: PLR0915 if env_variable is None: env_vars_dict[_var] = None else: - # decode + decrypt the value - decrypted_value = decrypt_value_helper( - value=env_variable, key=_var - ) - env_vars_dict[_var] = decrypted_value + env_vars_dict[_var] = env_variable _data_to_return.append({"name": _callback, "variables": env_vars_dict}) elif _callback == "langfuse": diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py index 7b3157f8ee1..e75b87e1c87 100644 --- a/tests/test_litellm/proxy/test_proxy_server.py +++ b/tests/test_litellm/proxy/test_proxy_server.py @@ -246,6 +246,54 @@ def test_update_config_fields_deep_merge_db_wins(): assert rs["routing_mode"] == "cost_optimized" +def test_get_config_custom_callback_api_env_vars(monkeypatch): + """ + Ensure /get/config/callbacks returns custom callback env vars when both custom values are provided. + """ + from litellm.proxy.proxy_server import app, proxy_config, user_api_key_auth + + # Mock config with custom_callback_api enabled and custom env vars present + config_data = { + "litellm_settings": {"success_callback": ["custom_callback_api"]}, + "general_settings": {}, + "environment_variables": { + "custom_callback_api_url": "https://callback.example.com", + "custom_callback_api_headers": "Auth: token", + }, + } + + # Mock proxy_config.get_config and router settings + mock_router = MagicMock() + mock_router.get_settings.return_value = {} + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", mock_router) + monkeypatch.setattr( + proxy_config, "get_config", AsyncMock(return_value=config_data) + ) + + # Bypass auth dependency + original_overrides = app.dependency_overrides.copy() + app.dependency_overrides[user_api_key_auth] = lambda: MagicMock() + + client = TestClient(app) + try: + response = client.get("/get/config/callbacks") + finally: + app.dependency_overrides = original_overrides + + assert response.status_code == 200 + callbacks = response.json()["callbacks"] + custom_cb = next( + (cb for cb in callbacks if cb["name"] == "custom_callback_api"), None + ) + + assert custom_cb is not None + assert custom_cb["variables"] == { + "custom_callback_api_url": "https://callback.example.com", + "custom_callback_api_headers": "Auth: token", + } + assert "GENERIC_LOGGER_ENDPOINT" not in custom_cb["variables"] + + # Mock Prisma class MockPrisma: def __init__(self, database_url=None, proxy_logging_obj=None, http_client=None):