Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions litellm/integrations/azure_sentinel/azure_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def __init__(
If not provided, will use AZURE_SENTINEL_AUDIT_STREAM_NAME env var or the standard stream name.
authority_host (str, optional): Microsoft Entra authority host that issues the OAuth2 token,
e.g. "https://login.microsoftonline.us" for Azure Government. If not provided, will use
AZURE_AUTHORITY_HOST env var or default to the Azure Public Cloud authority. The Azure
Monitor audience is derived from it.
AZURE_SENTINEL_AUTHORITY_HOST or AZURE_AUTHORITY_HOST env vars, or default to the Azure
Public Cloud authority. The Azure Monitor audience is derived from it.
"""
self.async_httpx_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback)

Expand All @@ -95,7 +95,10 @@ def __init__(
client_secret or os.getenv("AZURE_SENTINEL_CLIENT_SECRET") or os.getenv("AZURE_CLIENT_SECRET")
)
resolved_authority_host: Final = self._normalize_authority_host(
authority_host or os.getenv("AZURE_AUTHORITY_HOST") or DEFAULT_AZURE_AUTHORITY_HOST
authority_host
or os.getenv("AZURE_SENTINEL_AUTHORITY_HOST")
or os.getenv("AZURE_AUTHORITY_HOST")
or DEFAULT_AZURE_AUTHORITY_HOST
)

if not resolved_dcr_immutable_id:
Expand Down
36 changes: 36 additions & 0 deletions tests/test_litellm/integrations/test_azure_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ def _build_logger(**overrides):

@pytest.fixture
def _no_authority_host_env(monkeypatch):
monkeypatch.delenv("AZURE_SENTINEL_AUTHORITY_HOST", raising=False)
monkeypatch.delenv("AZURE_AUTHORITY_HOST", raising=False)


Expand Down Expand Up @@ -389,3 +390,38 @@ async def mock_post(*args, **kwargs):
assert len(token_calls) == 1
assert token_calls[0].kwargs["url"] == "https://login.microsoftonline.us/test-tenant-id/oauth2/v2.0/token"
assert token_calls[0].kwargs["data"]["scope"] == "https://monitor.azure.us/.default"


def test_azure_sentinel_authority_host_prefers_the_sentinel_scoped_env_var(_no_authority_host_env, monkeypatch):
"""AZURE_AUTHORITY_HOST is shared with Azure OpenAI and the azure_storage callback, so a deployment
whose Sentinel workspace lives in a different cloud than the rest of its Azure resources needs a
Sentinel-scoped override. This mirrors how tenant, client id and secret already resolve."""
monkeypatch.setenv("AZURE_AUTHORITY_HOST", "https://login.microsoftonline.com")
monkeypatch.setenv("AZURE_SENTINEL_AUTHORITY_HOST", "https://login.microsoftonline.us")

logger = _build_logger()

assert logger.authority_host == "https://login.microsoftonline.us"
assert logger.oauth_scope == "https://monitor.azure.us/.default"


def test_azure_sentinel_falls_back_to_the_shared_authority_host(_no_authority_host_env, monkeypatch):
"""With no Sentinel-scoped override the shared variable still applies, which is the behavior
shipped in the original fix."""
monkeypatch.setenv("AZURE_AUTHORITY_HOST", "https://login.microsoftonline.us")

logger = _build_logger()

assert logger.authority_host == "https://login.microsoftonline.us"
assert logger.oauth_scope == "https://monitor.azure.us/.default"


def test_azure_sentinel_authority_host_argument_outranks_the_scoped_env_var(_no_authority_host_env, monkeypatch):
"""An explicit constructor argument is the most specific source and has to win, otherwise a
deployment that exports the scoped variable silently overrides an SDK caller."""
monkeypatch.setenv("AZURE_SENTINEL_AUTHORITY_HOST", "https://login.microsoftonline.us")

logger = _build_logger(authority_host="https://login.microsoftonline.com")

assert logger.authority_host == "https://login.microsoftonline.com"
assert logger.oauth_scope == "https://monitor.azure.com/.default"
Loading