diff --git a/docs/my-website/docs/enterprise.md b/docs/my-website/docs/enterprise.md index 382ba8b28..793ce339c 100644 --- a/docs/my-website/docs/enterprise.md +++ b/docs/my-website/docs/enterprise.md @@ -15,6 +15,7 @@ This covers: - ✅ **Custom SLAs** - ✅ [**Secure UI access with Single Sign-On**](../docs/proxy/ui.md#setup-ssoauth-for-ui) - ✅ [**JWT-Auth**](../docs/proxy/token_auth.md) +- ✅ [**Invite Team Members to access `/spend` Routes**](../docs/proxy/cost_tracking#allowing-non-proxy-admins-to-access-spend-endpoints) ## [COMING SOON] AWS Marketplace Support diff --git a/docs/my-website/docs/proxy/cost_tracking.md b/docs/my-website/docs/proxy/cost_tracking.md index 2aaf8116e..7405fd123 100644 --- a/docs/my-website/docs/proxy/cost_tracking.md +++ b/docs/my-website/docs/proxy/cost_tracking.md @@ -125,6 +125,36 @@ Output from script +## Allowing Non-Proxy Admins to access `/spend` endpoints + +Use this when you want non-proxy admins to access `/spend` endpoints + +:::info + +Schedule a [meeting with us to get your Enterprise License](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat) + +::: + +### Create Key +Create Key with with `permissions={"get_spend_routes": true}` +```shell +curl --location 'http://0.0.0.0:4000/key/generate' \ + --header 'Authorization: Bearer sk-1234' \ + --header 'Content-Type: application/json' \ + --data '{ + "permissions": {"get_spend_routes": true} + }' +``` + +### Use generated key on `/spend` endpoints + +Access spend Routes with newly generate keys +```shell +curl -X GET 'http://localhost:4000/global/spend/report?start_date=2024-04-01&end_date=2024-06-30' \ + -H 'Authorization: Bearer sk-H16BKvrSNConSsBYLGc_7A' +``` + + ## Reset Team, API Key Spend - MASTER KEY ONLY diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 63d82e709..a6640e4d3 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -157,6 +157,7 @@ class LiteLLMRoutes(enum.Enum): "/global/spend/end_users", "/global/spend/models", "/global/predict/spend/logs", + "/global/spend/report", ] public_routes: List = [ diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 08c2fee07..cd9251942 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1211,6 +1211,13 @@ async def user_api_key_auth( _has_user_setup_sso() and route in LiteLLMRoutes.sso_only_routes.value ): + pass + elif ( + route in LiteLLMRoutes.global_spend_tracking_routes.value + and getattr(valid_token, "permissions", None) is not None + and "get_spend_routes" in getattr(valid_token, "permissions", None) + ): + pass else: user_role = "unknown" @@ -2967,7 +2974,7 @@ async def generate_key_helper_fn( organization_id: Optional[str] = None, table_name: Optional[Literal["key", "user"]] = None, ): - global prisma_client, custom_db_client, user_api_key_cache, litellm_proxy_admin_name + global prisma_client, custom_db_client, user_api_key_cache, litellm_proxy_admin_name, premium_user if prisma_client is None and custom_db_client is None: raise Exception( @@ -3062,6 +3069,14 @@ async def generate_key_helper_fn( if isinstance(saved_token["metadata"], str): saved_token["metadata"] = json.loads(saved_token["metadata"]) if isinstance(saved_token["permissions"], str): + if ( + "get_spend_routes" in saved_token["permissions"] + and premium_user != True + ): + raise Exception( + "get_spend_routes permission is only available for LiteLLM Enterprise users" + ) + saved_token["permissions"] = json.loads(saved_token["permissions"]) if isinstance(saved_token["model_max_budget"], str): saved_token["model_max_budget"] = json.loads( diff --git a/ui/litellm-dashboard/src/components/create_key_button.tsx b/ui/litellm-dashboard/src/components/create_key_button.tsx index 0e158778f..83e8d9264 100644 --- a/ui/litellm-dashboard/src/components/create_key_button.tsx +++ b/ui/litellm-dashboard/src/components/create_key_button.tsx @@ -319,7 +319,8 @@ const CreateKey: React.FC = ({ > - + + = ({ Token Name {selectedToken.key_alias ? selectedToken.key_alias : selectedToken.key_name} Token ID - {selectedToken.token} + {selectedToken.token} Metadata
{JSON.stringify(selectedToken.metadata)}