mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
* 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>
202 lines
6.9 KiB
Python
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))
|