diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index ef9436f2d8c..4fa24143c9d 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -438,6 +438,22 @@ async def health_services_endpoint( # noqa: PLR0915 }, ) if service == "email": + from litellm.proxy.proxy_server import proxy_config, store_model_in_db + + # TEST_EMAIL_ADDRESS is stored encrypted in the DB when the proxy + # is running in DB mode. Calling get_config() ensures the value is + # freshly decrypted and available both in the returned config dict + # and in os.environ. For YAML / env-var deployments the call is a + # cheap no-op and os.getenv() still works as the fallback. + if store_model_in_db and prisma_client is not None: + _fresh_config = await proxy_config.get_config() + _env_vars = _fresh_config.get("environment_variables", {}) + _test_email_address = _env_vars.get( + "TEST_EMAIL_ADDRESS" + ) or os.getenv("TEST_EMAIL_ADDRESS") + else: + _test_email_address = os.getenv("TEST_EMAIL_ADDRESS") + webhook_event = WebhookEvent( event="key_created", event_group=Litellm_EntityType.KEY, @@ -447,7 +463,7 @@ async def health_services_endpoint( # noqa: PLR0915 spend=0, max_budget=0, user_id=user_api_key_dict.user_id, - user_email=os.getenv("TEST_EMAIL_ADDRESS"), + user_email=_test_email_address, team_id=user_api_key_dict.team_id, ) diff --git a/ui/litellm-dashboard/src/components/email_settings.tsx b/ui/litellm-dashboard/src/components/email_settings.tsx index 53d08ad64f5..8fd3a37398c 100644 --- a/ui/litellm-dashboard/src/components/email_settings.tsx +++ b/ui/litellm-dashboard/src/components/email_settings.tsx @@ -14,7 +14,7 @@ interface EmailSettingsProps { } const EmailSettings: React.FC = ({ accessToken, premiumUser, alerts }) => { - const handleSaveEmailSettings = async () => { + const handleSaveEmailSettings = async ({ silent = false }: { silent?: boolean } = {}) => { if (!accessToken) { return; } @@ -43,9 +43,15 @@ const EmailSettings: React.FC = ({ accessToken, premiumUser, }; try { await setCallbacksCall(accessToken, payload); - NotificationManager.success("Email settings updated successfully"); + if (!silent) { + NotificationManager.success("Email settings updated successfully"); + } } catch (error) { - NotificationManager.fromBackend(error); + if (!silent) { + NotificationManager.fromBackend(error); + } + // In silent mode (called from test flow) swallow the error so that + // the test can still proceed using env-var / YAML config values. } }; @@ -163,6 +169,12 @@ const EmailSettings: React.FC = ({ accessToken, premiumUser, onClick={async () => { if (!accessToken) return; try { + // Silently attempt to persist the current form values so the + // backend can read TEST_EMAIL_ADDRESS from the DB (DB mode). + // If saving is not supported (e.g. STORE_MODEL_IN_DB=False / + // YAML mode), this is a no-op and the backend will fall back to + // the TEST_EMAIL_ADDRESS environment variable instead. + await handleSaveEmailSettings({ silent: true }); await serviceHealthCheck(accessToken, "email"); NotificationManager.success("Email test triggered. Check your configured email inbox/logs."); } catch (error) {