[Docs] Tutorial using MSFT auto team assignment with LiteLLM (#9898)

* add default_team_params as a config.yaml setting

* create_litellm_team_from_sso_group

* test_default_team_params

* test_create_team_without_default_params

* docs default team settings

* docs msft entra id tutorial

* commit litellm docs msft group assignment

* litellm MSFT sso

* member, team assignment on litellm

* docs msft auto assignment

* bug fix default team setting

* docs litellm default team settings

* test_default_team_params
This commit is contained in:
Ishaan Jaff 2025-04-10 20:07:55 -07:00 committed by GitHub
parent 72a12e91c4
commit 98e34cbf5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 191 additions and 59 deletions

View file

@ -0,0 +1,142 @@
import Image from '@theme/IdealImage';
# Microsoft SSO: Sync Groups, Members with LiteLLM
Sync Microsoft SSO Groups, Members with LiteLLM Teams.
<Image img={require('../../img/litellm_entra_id.png')} style={{ width: '800px', height: 'auto' }} />
<br />
<br />
## Overview of this tutorial
1. Auto-Create Entra ID Groups on LiteLLM Teams
2. Sync Entra ID Team Memberships
3. Set default params for new teams and users auto-created on LiteLLM
## 1. Auto-Create Entra ID Groups on LiteLLM Teams
In this step, our goal is to have LiteLLM automatically create a new team on the LiteLLM DB when there is a new Group Added to the LiteLLM Enterprise App on Azure Entra ID.
### 1.1 Create a new group in Entra ID
Navigate to [your Azure Portal](https://portal.azure.com/) > Groups > New Group. Create a new group.
<Image img={require('../../img/entra_create_team.png')} style={{ width: '800px', height: 'auto' }} />
### 1.2 Assign the group to your LiteLLM Enterprise App
On your Azure Portal, navigate to `Enterprise Applications` > Select your litellm app
<Image img={require('../../img/msft_enterprise_app.png')} style={{ width: '800px', height: 'auto' }} />
<br />
<br />
Once you've selected your litellm app, click on `Users and Groups` > `Add user/group`
<Image img={require('../../img/msft_enterprise_assign_group.png')} style={{ width: '800px', height: 'auto' }} />
<br />
Now select the group you created in step 1.1. And add it to the LiteLLM Enterprise App. At this point we have added `Production LLM Evals Group` to the LiteLLM Enterprise App. The next steps is having LiteLLM automatically create the `Production LLM Evals Group` on the LiteLLM DB when a new user signs in.
<Image img={require('../../img/msft_enterprise_select_group.png')} style={{ width: '800px', height: 'auto' }} />
### 1.3 Sign in to LiteLLM UI via SSO
Sign into the LiteLLM UI via SSO. You should be redirected to the Entra ID SSO page. This SSO sign in flow will trigger LiteLLM to fetch the latest Groups and Members from Azure Entra ID.
<Image img={require('../../img/msft_sso_sign_in.png')} style={{ width: '800px', height: 'auto' }} />
### 1.4 Check the new team on LiteLLM UI
On the LiteLLM UI, Navigate to `Teams`, You should see the new team `Production LLM Evals Group` auto-created on LiteLLM.
<Image img={require('../../img/msft_auto_team.png')} style={{ width: '800px', height: 'auto' }} />
#### How this works
When a SSO user signs in to LiteLLM, LiteLLM automatically fetches the Groups under the LiteLLM Enterprise App, and found that the `Production LLM Evals Group` was assigned to the LiteLLM Enterprise App. Since the id associated with `Production LLM Evals Group` did not exist in the LiteLLM Teams Table, LiteLLM automatically created a new team with the name `Production LLM Evals Group` and id == id of `Production LLM Evals Group` in Entra ID.
## 2. Sync Entra ID Team Memberships
In this step, we will have LiteLLM automatically add a user to the `Production LLM Evals` Team on the LiteLLM DB when a new user is added to the `Production LLM Evals` Group in Entra ID.
### 2.1 Navigate to the `Production LLM Evals` Group in Entra ID
Navigate to the `Production LLM Evals` Group in Entra ID.
<Image img={require('../../img/msft_member_1.png')} style={{ width: '800px', height: 'auto' }} />
### 2.2 Add a member to the group in Entra ID
Select `Members` > `Add members`
In this stage you should add the user you want to add to the `Production LLM Evals` Team.
<Image img={require('../../img/msft_member_2.png')} style={{ width: '800px', height: 'auto' }} />
### 2.3 Sign in as the new user on LiteLLM UI
Sign in as the new user on LiteLLM UI. You should be redirected to the Entra ID SSO page. This SSO sign in flow will trigger LiteLLM to fetch the latest Groups and Members from Azure Entra ID. During this step LiteLLM sync it's teams, team members with what is available from Entra ID
<Image img={require('../../img/msft_sso_sign_in.png')} style={{ width: '800px', height: 'auto' }} />
### 2.4 Check the team membership on LiteLLM UI
On the LiteLLM UI, Navigate to `Teams`, You should see the new team `Production LLM Evals Group`. Since your are now a member of the `Production LLM Evals Group` in Entra ID, you should see the new team `Production LLM Evals Group` on the LiteLLM UI.
<Image img={require('../../img/msft_member_3.png')} style={{ width: '800px', height: 'auto' }} />
## 3. Set default params for new teams auto-created on LiteLLM
Since litellm auto creates a new team on the LiteLLM DB when there is a new Group Added to the LiteLLM Enterprise App on Azure Entra ID, we can set default params for new teams created.
This allows you to set a default budget, models, etc for new teams created.
### 3.1 Set `default_team_params` on litellm
Navigate to your litellm config file and set the following params
```yaml showLineNumbers title="litellm config with default_team_params"
litellm_settings:
default_team_params: # Default Params to apply when litellm auto creates a team from SSO IDP provider
max_budget: 100 # Optional[float], optional): $100 budget for the team
budget_duration: 30d # Optional[str], optional): 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]], optional): models to be used by the team
```
### 3.2 Auto-create a new team on LiteLLM
- In this step you should add a new group to the LiteLLM Enterprise App on Azure Entra ID (like we did in step 1.1). We will call this group `Default LiteLLM Prod Team` on Azure Entra ID.
- Start litellm proxy server with your config
- Sign into LiteLLM UI via SSO
- Navigate to `Teams` and you should see the new team `Default LiteLLM Prod Team` auto-created on LiteLLM
- Note LiteLLM will set the default params for this new team.
<Image img={require('../../img/msft_default_settings.png')} style={{ width: '800px', height: 'auto' }} />

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 KiB

View file

@ -435,6 +435,7 @@ const sidebars = {
label: "Tutorials",
items: [
"tutorials/openweb_ui",
"tutorials/msft_sso",
'tutorials/litellm_proxy_aporia',
{
type: "category",

View file

@ -277,7 +277,7 @@ default_key_generate_params: Optional[Dict] = None
upperbound_key_generate_params: Optional[LiteLLM_UpperboundKeyGenerateParams] = None
key_generation_settings: Optional[StandardKeyGenerationConfig] = None
default_internal_user_params: Optional[Dict] = None
default_team_params: Optional[NewTeamRequest] = None
default_team_params: Optional[Union[NewTeamRequest, Dict]] = None
default_team_settings: Optional[List] = None
max_user_budget: Optional[float] = None
default_max_internal_user_budget: Optional[float] = None

View file

@ -11,6 +11,7 @@ Has all /sso/* routes
import asyncio
import os
import uuid
from copy import deepcopy
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast
from fastapi import APIRouter, Depends, HTTPException, Request, status
@ -919,13 +920,13 @@ class SSOAuthenticationHandler:
team_alias=litellm_team_name,
)
if litellm.default_team_params:
team_request = litellm.default_team_params.model_copy(
deep=True,
update={
"team_id": litellm_team_id,
"team_alias": litellm_team_name,
},
team_request = SSOAuthenticationHandler._cast_and_deepcopy_litellm_default_team_params(
default_team_params=litellm.default_team_params,
litellm_team_id=litellm_team_id,
litellm_team_name=litellm_team_name,
team_request=team_request,
)
await new_team(
data=team_request,
# params used for Audit Logging
@ -938,6 +939,35 @@ class SSOAuthenticationHandler:
except Exception as e:
verbose_proxy_logger.exception(f"Error creating Litellm Team: {e}")
@staticmethod
def _cast_and_deepcopy_litellm_default_team_params(
default_team_params: Union[NewTeamRequest, Dict],
team_request: NewTeamRequest,
litellm_team_id: str,
litellm_team_name: Optional[str] = None,
) -> NewTeamRequest:
"""
Casts and deepcopies the litellm.default_team_params to a NewTeamRequest object
- Ensures we create a new NewTeamRequest object
- Handle the case where litellm.default_team_params is a dict or a NewTeamRequest object
- Adds the litellm_team_id and litellm_team_name to the NewTeamRequest object
"""
if isinstance(default_team_params, dict):
_team_request = deepcopy(default_team_params)
_team_request["team_id"] = litellm_team_id
_team_request["team_alias"] = litellm_team_name
team_request = NewTeamRequest(**_team_request)
elif isinstance(litellm.default_team_params, NewTeamRequest):
team_request = litellm.default_team_params.model_copy(
deep=True,
update={
"team_id": litellm_team_id,
"team_alias": litellm_team_name,
},
)
return team_request
class MicrosoftSSOHandler:
"""

View file

@ -418,62 +418,21 @@ def test_get_group_ids_from_graph_api_response():
@pytest.mark.asyncio
async def test_upsert_sso_user_existing_user():
"""
If a user_id is already in the LiteLLM DB and the user signed in with SSO. Ensure that the user_id is updated with the SSO user_email
SSO Test
"""
# Arrange
mock_prisma = MagicMock()
mock_prisma.db = MagicMock()
mock_prisma.db.litellm_usertable = MagicMock()
mock_prisma.db.litellm_usertable.update_many = AsyncMock()
# Create a mock existing user
mock_user = MagicMock()
mock_user.user_id = "existing_user_123"
mock_user.user_email = "old_email@example.com"
# Create mock SSO response
mock_sso_response = CustomOpenID(
email="new_email@example.com",
display_name="Test User",
provider="microsoft",
id="existing_user_123",
first_name="Test",
last_name="User",
team_ids=[],
)
# Create mock user defined values
mock_user_defined_values = MagicMock()
# Act
result = await SSOAuthenticationHandler.upsert_sso_user(
result=mock_sso_response,
user_info=mock_user,
user_email="new_email@example.com",
user_defined_values=mock_user_defined_values,
prisma_client=mock_prisma,
)
# Assert
mock_prisma.db.litellm_usertable.update_many.assert_called_once_with(
where={"user_id": "existing_user_123"},
data={"user_email": "new_email@example.com"},
)
assert result == mock_user
async def test_default_team_params():
@pytest.mark.parametrize(
"team_params",
[
# Test case 1: Using NewTeamRequest
NewTeamRequest(max_budget=10, budget_duration="1d", models=["special-gpt-5"]),
# Test case 2: Using Dict
{"max_budget": 10, "budget_duration": "1d", "models": ["special-gpt-5"]},
],
)
async def test_default_team_params(team_params):
"""
When litellm.default_team_params is set, it should be used to create a new team
"""
# Arrange
litellm.default_team_params = NewTeamRequest(
max_budget=10, budget_duration="1d", models=["special-gpt-5"]
)
litellm.default_team_params = team_params
def mock_jsonify_team_object(db_data):
return db_data