diff --git a/docs/my-website/docs/proxy/enterprise.md b/docs/my-website/docs/proxy/enterprise.md index 3e97780213..a4f3ea7b17 100644 --- a/docs/my-website/docs/proxy/enterprise.md +++ b/docs/my-website/docs/proxy/enterprise.md @@ -1,7 +1,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# ✨ Enterprise Features - Content Moderation, Blocked Users/Keywords +# ✨ Enterprise Features - End-user Opt-out, Content Mod Features here are behind a commercial license in our `/enterprise` folder. [**See Code**](https://github.com/BerriAI/litellm/tree/main/enterprise) @@ -167,6 +167,29 @@ curl --location 'http://0.0.0.0:8000/chat/completions' \ ::: +### Using via API + + +**Block all calls for a user id** + +``` +curl -X POST "http://0.0.0.0:8000/user/block" \ +-H "Authorization: Bearer sk-1234" \ +-D '{ +"user_ids": [, ...] +}' +``` + +**Unblock calls for a user id** + +``` +curl -X POST "http://0.0.0.0:8000/user/unblock" \ +-H "Authorization: Bearer sk-1234" \ +-D '{ +"user_ids": [, ...] +}' +``` + ## Enable Banned Keywords List ```yaml diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 7f453980fb..029f35867e 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -116,6 +116,10 @@ class ModelInfo(LiteLLMBase): return values +class BlockUsers(LiteLLMBase): + user_ids: List[str] # required + + class ModelParams(LiteLLMBase): model_name: str litellm_params: dict diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 14f8bc96ca..939dd244a6 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -4307,6 +4307,93 @@ async def user_get_requests(): ) +@router.post( + "/user/block", + tags=["user management"], + dependencies=[Depends(user_api_key_auth)], +) +async def block_user(data: BlockUsers): + """ + [BETA] Reject calls with this user id + + ``` + curl -X POST "http://0.0.0.0:8000/user/block" + -H "Authorization: Bearer sk-1234" + -D '{ + "user_ids": [, ...] + }' + ``` + """ + from litellm.proxy.enterprise.enterprise_hooks.blocked_user_list import ( + _ENTERPRISE_BlockedUserList, + ) + + if not any(isinstance(x, _ENTERPRISE_BlockedUserList) for x in litellm.callbacks): + blocked_user_list = _ENTERPRISE_BlockedUserList() + litellm.callbacks.append(blocked_user_list) # type: ignore + + if litellm.blocked_user_list is None: + litellm.blocked_user_list = data.user_ids + elif isinstance(litellm.blocked_user_list, list): + litellm.blocked_user_list = litellm.blocked_user_list + data.user_ids + else: + raise HTTPException( + status_code=500, + detail={ + "error": "`blocked_user_list` must be a list or not set. Filepaths can't be updated." + }, + ) + + return {"blocked_users": litellm.blocked_user_list} + + +@router.post( + "/user/unblock", + tags=["user management"], + dependencies=[Depends(user_api_key_auth)], +) +async def unblock_user(data: BlockUsers): + """ + [BETA] Unblock calls with this user id + + Example + ``` + curl -X POST "http://0.0.0.0:8000/user/unblock" + -H "Authorization: Bearer sk-1234" + -D '{ + "user_ids": [, ...] + }' + ``` + """ + from litellm.proxy.enterprise.enterprise_hooks.blocked_user_list import ( + _ENTERPRISE_BlockedUserList, + ) + + if ( + not any(isinstance(x, _ENTERPRISE_BlockedUserList) for x in litellm.callbacks) + or litellm.blocked_user_list is None + ): + raise HTTPException( + status_code=400, + detail={ + "error": "Blocked user check was never set. This call has no effect." + }, + ) + + if isinstance(litellm.blocked_user_list, list): + for id in data.user_ids: + litellm.blocked_user_list.remove(id) + else: + raise HTTPException( + status_code=500, + detail={ + "error": "`blocked_user_list` must be set as a list. Filepaths can't be updated." + }, + ) + + return {"blocked_users": litellm.blocked_user_list} + + #### TEAM MANAGEMENT #### diff --git a/litellm/utils.py b/litellm/utils.py index 0f54a6ba58..38ca669fc7 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4306,6 +4306,7 @@ def get_optional_params( or model in litellm.vertex_language_models or model in litellm.vertex_embedding_models ): + print_verbose(f"(start) INSIDE THE VERTEX AI OPTIONAL PARAM BLOCK") ## check if unsupported param passed in supported_params = [ "temperature", @@ -4339,6 +4340,9 @@ def get_optional_params( optional_params["tools"] = [ generative_models.Tool(function_declarations=gtool_func_declarations) ] + print_verbose( + f"(end) INSIDE THE VERTEX AI OPTIONAL PARAM BLOCK - optional_params: {optional_params}" + ) elif custom_llm_provider == "sagemaker": ## check if unsupported param passed in supported_params = ["stream", "temperature", "max_tokens", "top_p", "stop", "n"] @@ -4754,6 +4758,7 @@ def get_optional_params( extra_body # openai client supports `extra_body` param ) else: # assume passing in params for openai/azure openai + print_verbose(f"UNMAPPED PROVIDER, ASSUMING IT'S OPENAI/AZUREs") supported_params = [ "functions", "function_call", @@ -4829,6 +4834,7 @@ def get_optional_params( for k in passed_params.keys(): if k not in default_params.keys(): optional_params[k] = passed_params[k] + print_verbose(f"Final returned optional params: {optional_params}") return optional_params