diff --git a/litellm/llms/azure_ai/chat/transformation.py b/litellm/llms/azure_ai/chat/transformation.py index 4c60c93f04..0523a7e5ef 100644 --- a/litellm/llms/azure_ai/chat/transformation.py +++ b/litellm/llms/azure_ai/chat/transformation.py @@ -51,7 +51,7 @@ class AzureAIStudioConfig(OpenAIConfig): message["content"] = texts return messages - def _is_azure_openai_model(self, model: str) -> bool: + def _is_azure_openai_model(self, model: str, api_base: Optional[str]) -> bool: try: if "/" in model: model = model.split("/", 1)[1] @@ -61,6 +61,9 @@ class AzureAIStudioConfig(OpenAIConfig): or model in litellm.open_ai_embedding_models ): return True + + if api_base and "services.ai.azure" in api_base: + return True except Exception: return False return False @@ -75,7 +78,7 @@ class AzureAIStudioConfig(OpenAIConfig): api_base = api_base or get_secret_str("AZURE_AI_API_BASE") dynamic_api_key = api_key or get_secret_str("AZURE_AI_API_KEY") - if self._is_azure_openai_model(model=model): + if self._is_azure_openai_model(model=model, api_base=api_base): verbose_logger.debug( "Model={} is Azure OpenAI model. Setting custom_llm_provider='azure'.".format( model diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 526cc05c9a..5550309069 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -94,29 +94,46 @@ def _is_allowed_to_create_key( def _team_key_generation_team_member_check( + assigned_user_id: Optional[str], team_table: LiteLLM_TeamTableCachedObj, user_api_key_dict: UserAPIKeyAuth, - team_key_generation: Optional[TeamUIKeyGenerationConfig], + team_key_generation: TeamUIKeyGenerationConfig, ): - if ( - team_key_generation is None - or "allowed_team_member_roles" not in team_key_generation - ): - return True + if assigned_user_id is not None: + key_assigned_user_in_team = _get_user_in_team( + team_table=team_table, user_id=assigned_user_id + ) - user_in_team = _get_user_in_team( + if key_assigned_user_in_team is None: + raise HTTPException( + status_code=400, + detail=f"User={assigned_user_id} not assigned to team={team_table.team_id}", + ) + + key_creating_user_in_team = _get_user_in_team( team_table=team_table, user_id=user_api_key_dict.user_id ) - if user_in_team is None: + + is_admin = ( + user_api_key_dict.user_role is not None + and user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value + ) + + if is_admin: + return True + elif key_creating_user_in_team is None: raise HTTPException( status_code=400, detail=f"User={user_api_key_dict.user_id} not assigned to team={team_table.team_id}", ) - - if user_in_team.role not in team_key_generation["allowed_team_member_roles"]: + elif ( + "allowed_team_member_roles" in team_key_generation + and key_creating_user_in_team.role + not in team_key_generation["allowed_team_member_roles"] + ): raise HTTPException( status_code=400, - detail=f"Team member role {user_in_team.role} not in allowed_team_member_roles={team_key_generation['allowed_team_member_roles']}", + detail=f"Team member role {key_creating_user_in_team.role} not in allowed_team_member_roles={team_key_generation['allowed_team_member_roles']}", ) return True @@ -143,14 +160,17 @@ def _team_key_generation_check( data: GenerateKeyRequest, ): if ( - litellm.key_generation_settings is None - or litellm.key_generation_settings.get("team_key_generation") is None + litellm.key_generation_settings is not None + and "team_key_generation" in litellm.key_generation_settings ): - return True - - _team_key_generation = litellm.key_generation_settings["team_key_generation"] # type: ignore + _team_key_generation = litellm.key_generation_settings["team_key_generation"] + else: + _team_key_generation = TeamUIKeyGenerationConfig( + allowed_team_member_roles=["admin", "member"], + ) _team_key_generation_team_member_check( + assigned_user_id=data.user_id, team_table=team_table, user_api_key_dict=user_api_key_dict, team_key_generation=_team_key_generation, @@ -215,21 +235,17 @@ def key_generation_check( """ Check if admin has restricted key creation to certain roles for teams or individuals """ - if ( - litellm.key_generation_settings is None - or user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value - ): - return True ## check if key is for team or individual is_team_key = _is_team_key(data=data) - if is_team_key: - if team_table is None: + if team_table is None and litellm.key_generation_settings is not None: raise HTTPException( status_code=400, detail=f"Unable to find team object in database. Team ID: {data.team_id}", ) + elif team_table is None: + return True # assume user is assigning team_id without using the team table return _team_key_generation_check( team_table=team_table, user_api_key_dict=user_api_key_dict, @@ -332,21 +348,26 @@ async def generate_key_fn( # noqa: PLR0915 raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=message ) - elif litellm.key_generation_settings is not None: - if data.team_id is None: - team_table: Optional[LiteLLM_TeamTableCachedObj] = None - else: + team_table: Optional[LiteLLM_TeamTableCachedObj] = None + if data.team_id is not None: + try: team_table = await get_team_object( team_id=data.team_id, prisma_client=prisma_client, user_api_key_cache=user_api_key_cache, parent_otel_span=user_api_key_dict.parent_otel_span, ) - key_generation_check( - team_table=team_table, - user_api_key_dict=user_api_key_dict, - data=data, - ) + except Exception as e: + verbose_proxy_logger.debug( + f"Error getting team object in `/key/generate`: {e}" + ) + team_table = None + + key_generation_check( + team_table=team_table, + user_api_key_dict=user_api_key_dict, + data=data, + ) try: _is_allowed_to_create_key( diff --git a/tests/local_testing/test_get_llm_provider.py b/tests/local_testing/test_get_llm_provider.py index 4a79d7b1c0..c713eaa3c3 100644 --- a/tests/local_testing/test_get_llm_provider.py +++ b/tests/local_testing/test_get_llm_provider.py @@ -191,3 +191,12 @@ def test_get_llm_provider_watson_text(): ) assert custom_llm_provider == "watsonx_text" assert model == "watson-text-to-speech" + + +def test_azure_global_standard_get_llm_provider(): + model, custom_llm_provider, dynamic_api_key, api_base = litellm.get_llm_provider( + model="azure_ai/gpt-4o-global-standard", + api_base="https://my-deployment-francecentral.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview", + api_key="fake-api-key", + ) + assert custom_llm_provider == "azure" diff --git a/ui/litellm-dashboard/src/components/create_key_button.tsx b/ui/litellm-dashboard/src/components/create_key_button.tsx index 4f771b1117..7060128dc9 100644 --- a/ui/litellm-dashboard/src/components/create_key_button.tsx +++ b/ui/litellm-dashboard/src/components/create_key_button.tsx @@ -125,6 +125,7 @@ const CreateKey: React.FC = ({ try { const newKeyAlias = formValues?.key_alias ?? ""; const newKeyTeamId = formValues?.team_id ?? null; + const existingKeyAliases = data ?.filter((k) => k.team_id === newKeyTeamId) @@ -163,8 +164,8 @@ const CreateKey: React.FC = ({ form.resetFields(); localStorage.removeItem("userData" + userID); } catch (error) { - console.error("Error creating the key:", error); - message.error(`Error creating the key: ${error}`, 20); + console.log("error in create key:", error); + message.error(`Error creating the key: ${error}`); } }; @@ -224,11 +225,27 @@ const CreateKey: React.FC = ({ > You Service Account + {userRole === "Admin" && Another User} + + = ({ = ({ visible, possibleUIRoles, label="Spend (USD)" name="spend" tooltip="(float) - Spend of all LLM calls completed by this user" + help="Across all keys (including keys with team_id)." > @@ -118,6 +119,7 @@ const EditUserModal: React.FC = ({ visible, possibleUIRoles, label="User Budget (USD)" name="max_budget" tooltip="(float) - Maximum budget of this user" + help="Ignored if the key has a team_id; team budget applies there." > diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index d91b5fe683..3605d87f09 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -31,8 +31,6 @@ const handleError = async (errorData: string) => { await sleep(3000); // 5 second sleep document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; window.location.href = baseUrl; - } else { - message.error(errorData); } lastErrorTime = currentTime; } else { @@ -450,7 +448,7 @@ export const keyCreateCall = async ( const errorData = await response.text(); handleError(errorData); console.error("Error response from the server:", errorData); - throw new Error("Network response was not ok"); + throw new Error(errorData); } const data = await response.json();