test3 / litellm /proxy /ui_crud_endpoints /proxy_setting_endpoints.py
DesertWolf's picture
Upload folder using huggingface_hub
447ebeb verified
#### CRUD ENDPOINTS for UI Settings #####
from typing import Any, Dict, List, Union
from fastapi import APIRouter, Depends, HTTPException
import litellm
from litellm._logging import verbose_proxy_logger
from litellm.proxy._types import *
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
from litellm.types.proxy.management_endpoints.ui_sso import DefaultTeamSSOParams, SSOConfig
router = APIRouter()
class IPAddress(BaseModel):
ip: str
class SettingsResponse(BaseModel):
"""Base response model for settings with values and schema information"""
values: Dict[str, Any]
"""The current configuration values"""
field_schema: Dict[str, Any]
"""Schema information including descriptions and property types for UI display"""
class SSOSettingsResponse(SettingsResponse):
"""Response model for SSO settings"""
pass
class InternalUserSettingsResponse(SettingsResponse):
"""Response model for internal user settings"""
pass
class DefaultTeamSettingsResponse(SettingsResponse):
"""Response model for default team settings"""
pass
@router.get(
"/get/allowed_ips",
tags=["Budget & Spend Tracking"],
dependencies=[Depends(user_api_key_auth)],
include_in_schema=False,
)
async def get_allowed_ips():
from litellm.proxy.proxy_server import general_settings
_allowed_ip = general_settings.get("allowed_ips")
return {"data": _allowed_ip}
@router.post(
"/add/allowed_ip",
tags=["Budget & Spend Tracking"],
dependencies=[Depends(user_api_key_auth)],
)
async def add_allowed_ip(ip_address: IPAddress):
from litellm.proxy.proxy_server import (
general_settings,
prisma_client,
proxy_config,
store_model_in_db,
)
_allowed_ips: List = general_settings.get("allowed_ips", [])
if ip_address.ip not in _allowed_ips:
_allowed_ips.append(ip_address.ip)
general_settings["allowed_ips"] = _allowed_ips
else:
raise HTTPException(status_code=400, detail="IP address already exists")
if prisma_client is None:
raise Exception("No DB Connected")
if store_model_in_db is not True:
raise HTTPException(
status_code=500,
detail={
"error": "Set `'STORE_MODEL_IN_DB='True'` in your env to enable this feature."
},
)
# Load existing config
config = await proxy_config.get_config()
verbose_proxy_logger.debug("Loaded config: %s", config)
if "general_settings" not in config:
config["general_settings"] = {}
if "allowed_ips" not in config["general_settings"]:
config["general_settings"]["allowed_ips"] = []
if ip_address.ip not in config["general_settings"]["allowed_ips"]:
config["general_settings"]["allowed_ips"].append(ip_address.ip)
await proxy_config.save_config(new_config=config)
return {
"message": f"IP {ip_address.ip} address added successfully",
"status": "success",
}
@router.post(
"/delete/allowed_ip",
tags=["Budget & Spend Tracking"],
dependencies=[Depends(user_api_key_auth)],
)
async def delete_allowed_ip(ip_address: IPAddress):
from litellm.proxy.proxy_server import general_settings, proxy_config
_allowed_ips: List = general_settings.get("allowed_ips", [])
if ip_address.ip in _allowed_ips:
_allowed_ips.remove(ip_address.ip)
general_settings["allowed_ips"] = _allowed_ips
else:
raise HTTPException(status_code=404, detail="IP address not found")
# Load existing config
config = await proxy_config.get_config()
verbose_proxy_logger.debug("Loaded config: %s", config)
if "general_settings" not in config:
config["general_settings"] = {}
if "allowed_ips" not in config["general_settings"]:
config["general_settings"]["allowed_ips"] = []
if ip_address.ip in config["general_settings"]["allowed_ips"]:
config["general_settings"]["allowed_ips"].remove(ip_address.ip)
await proxy_config.save_config(new_config=config)
return {"message": f"IP {ip_address.ip} deleted successfully", "status": "success"}
async def _get_settings_with_schema(
settings_key: str,
settings_class: Any,
config: dict,
) -> dict:
"""
Common utility function to get settings with schema information.
Args:
settings_key: The key in litellm_settings to get
settings_class: The Pydantic class to use for schema
config: The config dictionary
"""
from pydantic import TypeAdapter
litellm_settings = config.get("litellm_settings", {}) or {}
settings_data = litellm_settings.get(settings_key, {}) or {}
# Create the settings object
settings = settings_class(**(settings_data))
# Get the schema
schema = TypeAdapter(settings_class).json_schema(by_alias=True)
# Convert to dict for response
settings_dict = settings.model_dump()
# Add descriptions to the response
result = {
"values": settings_dict,
"field_schema": {"description": schema.get("description", ""), "properties": {}},
}
# Add property descriptions
for field_name, field_info in schema["properties"].items():
result["field_schema"]["properties"][field_name] = {
"description": field_info.get("description", ""),
"type": field_info.get("type", "string"),
}
# Add nested object descriptions
for def_name, def_schema in schema.get("definitions", {}).items():
result["field_schema"][def_name] = {
"description": def_schema.get("description", ""),
"properties": {
prop_name: {"description": prop_info.get("description", "")}
for prop_name, prop_info in def_schema.get("properties", {}).items()
},
}
return result
@router.get(
"/get/internal_user_settings",
tags=["SSO Settings"],
dependencies=[Depends(user_api_key_auth)],
response_model=InternalUserSettingsResponse,
)
async def get_internal_user_settings():
"""
Get all SSO settings from the litellm_settings configuration.
Returns a structured object with values and descriptions for UI display.
"""
from litellm.proxy.proxy_server import proxy_config
# Load existing config
config = await proxy_config.get_config()
return await _get_settings_with_schema(
settings_key="default_internal_user_params",
settings_class=DefaultInternalUserParams,
config=config,
)
@router.get(
"/get/default_team_settings",
tags=["SSO Settings"],
dependencies=[Depends(user_api_key_auth)],
response_model=DefaultTeamSettingsResponse,
)
async def get_default_team_settings():
"""
Get all SSO settings from the litellm_settings configuration.
Returns a structured object with values and descriptions for UI display.
"""
from litellm.proxy.proxy_server import proxy_config
# Load existing config
config = await proxy_config.get_config()
return await _get_settings_with_schema(
settings_key="default_team_params",
settings_class=DefaultTeamSSOParams,
config=config,
)
async def _update_litellm_setting(
settings: Union[DefaultInternalUserParams, DefaultTeamSSOParams],
settings_key: str,
in_memory_var: Any,
success_message: str,
):
"""
Common utility function to update `litellm_settings` in both memory and config.
Args:
settings: The settings object to update
settings_key: The key in litellm_settings to update
in_memory_var: The in-memory variable to update
success_message: Message to return on success
"""
from litellm.proxy.proxy_server import proxy_config
# Update the in-memory settings
in_memory_var = settings.model_dump(exclude_none=True)
# Load existing config
config = await proxy_config.get_config()
# Update config with new settings
if "litellm_settings" not in config:
config["litellm_settings"] = {}
config["litellm_settings"][settings_key] = settings.model_dump(exclude_none=True)
# Save the updated config
await proxy_config.save_config(new_config=config)
return {
"message": success_message,
"status": "success",
"settings": in_memory_var,
}
@router.patch(
"/update/internal_user_settings",
tags=["SSO Settings"],
dependencies=[Depends(user_api_key_auth)],
)
async def update_internal_user_settings(settings: DefaultInternalUserParams):
"""
Update the default internal user parameters for SSO users.
These settings will be applied to new users who sign in via SSO.
"""
return await _update_litellm_setting(
settings=settings,
settings_key="default_internal_user_params",
in_memory_var=litellm.default_internal_user_params,
success_message="Internal user settings updated successfully",
)
@router.patch(
"/update/default_team_settings",
tags=["SSO Settings"],
dependencies=[Depends(user_api_key_auth)],
)
async def update_default_team_settings(settings: DefaultTeamSSOParams):
"""
Update the default team parameters for SSO users.
These settings will be applied to new teams created from SSO.
"""
return await _update_litellm_setting(
settings=settings,
settings_key="default_team_params",
in_memory_var=litellm.default_team_params,
success_message="Default team settings updated successfully",
)
@router.get(
"/get/sso_settings",
tags=["SSO Settings"],
dependencies=[Depends(user_api_key_auth)],
response_model=SSOSettingsResponse,
)
async def get_sso_settings():
"""
Get all SSO configuration settings from the environment variables.
Returns a structured object with values and descriptions for UI display.
"""
import os
from litellm.proxy.proxy_server import proxy_config
# Load existing config to get both environment variables and general settings
config = await proxy_config.get_config()
general_settings = config.get("general_settings", {}) or {}
environment_variables = config.get("environment_variables", {}) or {}
# Get user_email from general_settings
proxy_admin_email = general_settings.get("proxy_admin_email", None)
# Helper function to get env var value (first from config, then from environment)
def get_env_value(env_var_name: str):
return environment_variables.get(env_var_name) or os.getenv(env_var_name)
# Get current environment variables for SSO
sso_config = SSOConfig(
google_client_id=get_env_value("GOOGLE_CLIENT_ID"),
google_client_secret=get_env_value("GOOGLE_CLIENT_SECRET"),
microsoft_client_id=get_env_value("MICROSOFT_CLIENT_ID"),
microsoft_client_secret=get_env_value("MICROSOFT_CLIENT_SECRET"),
microsoft_tenant=get_env_value("MICROSOFT_TENANT"),
generic_client_id=get_env_value("GENERIC_CLIENT_ID"),
generic_client_secret=get_env_value("GENERIC_CLIENT_SECRET"),
generic_authorization_endpoint=get_env_value("GENERIC_AUTHORIZATION_ENDPOINT"),
generic_token_endpoint=get_env_value("GENERIC_TOKEN_ENDPOINT"),
generic_userinfo_endpoint=get_env_value("GENERIC_USERINFO_ENDPOINT"),
proxy_base_url=get_env_value("PROXY_BASE_URL"),
user_email=proxy_admin_email, # Get from config instead of environment
)
# Get the schema for UI display
from pydantic import TypeAdapter
schema = TypeAdapter(SSOConfig).json_schema(by_alias=True)
# Convert to dict for response
sso_dict = sso_config.model_dump()
# Add descriptions to the response
result = {
"values": sso_dict,
"field_schema": {"description": schema.get("description", ""), "properties": {}},
}
# Add property descriptions
for field_name, field_info in schema["properties"].items():
result["field_schema"]["properties"][field_name] = {
"description": field_info.get("description", ""),
"type": field_info.get("type", "string"),
}
return result
@router.patch(
"/update/sso_settings",
tags=["SSO Settings"],
dependencies=[Depends(user_api_key_auth)],
)
async def update_sso_settings(sso_config: SSOConfig):
"""
Update SSO configuration by saving to both environment variables and config file.
"""
from litellm.proxy.proxy_server import proxy_config
import os
# Update environment variables
env_var_mapping = {
'google_client_id': 'GOOGLE_CLIENT_ID',
'google_client_secret': 'GOOGLE_CLIENT_SECRET',
'microsoft_client_id': 'MICROSOFT_CLIENT_ID',
'microsoft_client_secret': 'MICROSOFT_CLIENT_SECRET',
'microsoft_tenant': 'MICROSOFT_TENANT',
'generic_client_id': 'GENERIC_CLIENT_ID',
'generic_client_secret': 'GENERIC_CLIENT_SECRET',
'generic_authorization_endpoint': 'GENERIC_AUTHORIZATION_ENDPOINT',
'generic_token_endpoint': 'GENERIC_TOKEN_ENDPOINT',
'generic_userinfo_endpoint': 'GENERIC_USERINFO_ENDPOINT',
'proxy_base_url': 'PROXY_BASE_URL',
}
# Load existing config
config = await proxy_config.get_config()
# Update config with new environment variables
if "environment_variables" not in config:
config["environment_variables"] = {}
# Update general_settings for user_email (admin email)
if "general_settings" not in config:
config["general_settings"] = {}
# Update environment variables in config and in memory
sso_data = sso_config.model_dump(exclude_none=True)
for field_name, value in sso_data.items():
if field_name == 'user_email' and value is not None:
# Store user_email in general_settings instead of environment variables
config["general_settings"]["proxy_admin_email"] = value
elif field_name in env_var_mapping and value is not None:
env_var_name = env_var_mapping[field_name]
# Update in config
config["environment_variables"][env_var_name] = value
# Update in runtime environment
os.environ[env_var_name] = value
# Save the updated config
await proxy_config.save_config(new_config=config)
return {
"message": "SSO settings updated successfully",
"status": "success",
"settings": sso_data,
}