litellm/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/endpoints.py
Krish Dholakia a44bc9f613
Remove + Check for unsafe enterprise/ folder imports (#11107)
* refactor(litellm/): wrap enterprise package usage in safe try-excepts

prevents missing package error for OSS users

Fixes https://github.com/BerriAI/litellm/issues/10795

* ci(config.yml): add script to check for unsafe imports to ci/cd

Closes https://github.com/BerriAI/litellm/issues/10795

* fix: add missing docs

* test: test_openai_image_edit_litellm_sdk

* use n 4 for mapped tests (#11109)

* Fix/background health check (#10887)

* fix: improve health check logic by deep copying model list on each iteration

* test: add async test for background health check reflecting model list changes

* fix: validate health check interval before executing background health check

* fix: specify type for health check results dictionary

* fix(user_api_key_auth.py): handle user custom auth set with no custom settings

* bump: version 0.1.21 → 0.2.0

* ci(config.yml): run enterprise and litellm tests separately

* fix: fix linting error

* docs: add missing docs

* [Feat] Add content policy violation error mapping for image editd (#11113)

* feat: add image edit mapping for content policy violations

* test fix

* Expose `/list` and `/info` endpoints for Audit Log events (#11102)

* feat(audit_logging_endpoints.py): expose list endpoint to show all audit logs

make it easier for user to retrieve individual endpoints

* feat(enterprise/): add audit logging endpoint

* feat(audit_logging_endpoints.py): expose new GET `/audit/{id}` endpoint

make it easier to retrieve view individual audit logs

* feat(key_management_event_hooks.py): correctly show the key of the user who initiated the change

* fix(key_management_event_hooks.py): add key rotations as an audit log event

'

* test(test_audit_logging_endpoints.py): add simple unit testing for audit log endpoint

* fix: testing fixes

* fix: fix ruff check

* [Feat] Use aiohttp transport by default - 97% lower median latency  (#11097)

* fix: add flag for disabling use_aiohttp_transport

* feat: add _create_async_transport

* feat: fixes for transport

* add httpx-aiohttp

* feat: fixes for transport

* refactor: fixes for transport

* build: fix deps

* fixes: test fixes

* fix: ensure aiohttp does not auto set content type

* test: test fixes

* feat: add LiteLLMAiohttpTransport

* fix: fixes for responses API handling

* test: fixes for responses API handling

* test: fixes for responses API handling

* feat: fixes for transport

* fix: base embedding handler

* test: test_async_http_handler_force_ipv4

* test: fix failing deepeval test

* fix: add YARL for bedrock urls

* fix: issues with transport

* fix: comment out linting issues

* test fix

* test: XAI is unstable

* test: fixes for using respx

* test: XAI fixes

* test: XAI fixes

* test: infinity testing fixes

* docs(config_settings.md): document param

* test: test_openai_image_edit_litellm_sdk

* test: remove deprecated test

* bump respx==0.22.0

* test: test_xai_message_name_filtering

* test: fix anthropic test after bumping httpx

* use n 4 for mapped tests (#11109)

* fix: use 1 session per event loop

* test: test_client_session_helper

* fix: linting error

* fix: resolving GET requests on httpx 0.28.1

* test fixes proxy unit tests

* fix: add ssl verify settings

* fix: proxy unit tests

* fix: refactor

* tests: basic unit tests for aiohttp transports

* tests: fixes xai

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@gmail.com>

* test: cleanup redundant test

* fix: fix import

* fix: fix linting errors

---------

Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: JuHyun Bae <jhyun0408@nate.com>
2025-05-24 08:59:02 -07:00

202 lines
6.9 KiB
Python

"""
Endpoints for managing email alerts on litellm
"""
import json
from typing import Dict
from fastapi import APIRouter, Depends, HTTPException
from litellm_enterprise.types.enterprise_callbacks.send_emails import (
DefaultEmailSettings,
EmailEvent,
EmailEventSettings,
EmailEventSettingsResponse,
EmailEventSettingsUpdateRequest,
)
from litellm._logging import verbose_proxy_logger
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
router = APIRouter()
async def _get_email_settings(prisma_client) -> Dict[str, bool]:
"""Helper function to get email settings from general_settings in db"""
try:
# Get general settings from db
general_settings_entry = await prisma_client.db.litellm_config.find_unique(
where={"param_name": "general_settings"}
)
# Initialize with default email settings
settings_dict = DefaultEmailSettings.get_defaults()
if (
general_settings_entry is not None
and general_settings_entry.param_value is not None
):
# Get general settings value
if isinstance(general_settings_entry.param_value, str):
general_settings = json.loads(general_settings_entry.param_value)
else:
general_settings = general_settings_entry.param_value
# Extract email_settings from general settings if it exists
if general_settings and "email_settings" in general_settings:
email_settings = general_settings["email_settings"]
# Update settings_dict with values from general_settings
for event_name, enabled in email_settings.items():
settings_dict[event_name] = enabled
return settings_dict
except Exception as e:
verbose_proxy_logger.error(
f"Error getting email settings from general_settings: {str(e)}"
)
# Return default settings in case of error
return DefaultEmailSettings.get_defaults()
async def _save_email_settings(prisma_client, settings: Dict[str, bool]):
"""Helper function to save email settings to general_settings in db"""
try:
verbose_proxy_logger.debug(
f"Saving email settings to general_settings: {settings}"
)
# Get current general settings
general_settings_entry = await prisma_client.db.litellm_config.find_unique(
where={"param_name": "general_settings"}
)
# Initialize general settings dict
if (
general_settings_entry is not None
and general_settings_entry.param_value is not None
):
if isinstance(general_settings_entry.param_value, str):
general_settings = json.loads(general_settings_entry.param_value)
else:
general_settings = dict(general_settings_entry.param_value)
else:
general_settings = {}
# Update email_settings in general_settings
general_settings["email_settings"] = settings
# Convert to JSON for storage
json_settings = json.dumps(general_settings, default=str)
# Save updated general settings
await prisma_client.db.litellm_config.upsert(
where={"param_name": "general_settings"},
data={
"create": {
"param_name": "general_settings",
"param_value": json_settings,
},
"update": {"param_value": json_settings},
},
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Error saving email settings to general_settings: {str(e)}",
)
@router.get(
"/email/event_settings",
response_model=EmailEventSettingsResponse,
tags=["email management"],
dependencies=[Depends(user_api_key_auth)],
)
async def get_email_event_settings(
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
Get all email event settings
"""
from litellm.proxy.proxy_server import prisma_client
if prisma_client is None:
raise HTTPException(status_code=500, detail="Database not connected")
try:
# Get existing settings
settings_dict = await _get_email_settings(prisma_client)
# Create a response with all events (enabled or disabled)
response_settings = []
for event in EmailEvent:
enabled = settings_dict.get(event.value, False)
response_settings.append(EmailEventSettings(event=event, enabled=enabled))
return EmailEventSettingsResponse(settings=response_settings)
except Exception as e:
verbose_proxy_logger.exception(f"Error getting email settings: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@router.patch(
"/email/event_settings",
tags=["email management"],
dependencies=[Depends(user_api_key_auth)],
)
async def update_event_settings(
request: EmailEventSettingsUpdateRequest,
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
Update the settings for email events
"""
from litellm.proxy.proxy_server import prisma_client
if prisma_client is None:
raise HTTPException(status_code=500, detail="Database not connected")
try:
# Get existing settings
settings_dict = await _get_email_settings(prisma_client)
# Update with new settings
for setting in request.settings:
settings_dict[setting.event.value] = setting.enabled
# Save updated settings
await _save_email_settings(prisma_client, settings_dict)
return {"message": "Email event settings updated successfully"}
except Exception as e:
verbose_proxy_logger.exception(f"Error updating email settings: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@router.post(
"/email/event_settings/reset",
tags=["email management"],
dependencies=[Depends(user_api_key_auth)],
)
async def reset_event_settings(
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
Reset all email event settings to default (new user invitations on, virtual key creation off)
"""
from litellm.proxy.proxy_server import prisma_client
if prisma_client is None:
raise HTTPException(status_code=500, detail="Database not connected")
try:
# Reset to default settings using the Pydantic model
default_settings = DefaultEmailSettings.get_defaults()
# Save default settings
await _save_email_settings(prisma_client, default_settings)
return {"message": "Email event settings reset to defaults"}
except Exception as e:
verbose_proxy_logger.exception(f"Error resetting email settings: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))