Skip to content

Commit 771ac2c

Browse files
authored
Python: Azure OpenAI setting fix: Don't fetch AD token if API is present in environment variable. (#8985)
### Motivation and Context While working on #8951 I found that interacting with an Azure OpenAI instance I had an API key for, but my identity didn't have IAM access to the API for, that I was getting a permission error: ``` semantic_kernel.exceptions.service_exceptions.ServiceResponseException: ("<class 'semantic_kernel.connectors.ai.open_ai.services.azure_text_embedding.AzureTextEmbedding'> service failed to generate embeddings", AuthenticationError("Error code: 401 - {'error': {'code': 'PermissionDenied', 'message': 'Principal does not have access to API/Operation.'}}")) ``` I found that this was because, though my API key was set in the environment, the codepath only recognized an explicitly passed in API key when deciding whether to issue an AD token while initializing an AzureTextEmbedding instance. This PR fixes that by looking at the setting object explicitly, which may pull the API key from the environment. ### Contribution Checklist - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄
1 parent 679df70 commit 771ac2c

File tree

4 files changed

+53
-4
lines changed

4 files changed

+53
-4
lines changed

python/semantic_kernel/connectors/ai/open_ai/services/azure_text_completion.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,21 @@ def __init__(
7272

7373
# If the api_key is none, and the ad_token is none, and the ad_token_provider is none,
7474
# then we will attempt to get the ad_token using the default endpoint specified in the Azure OpenAI settings.
75-
if api_key is None and ad_token_provider is None and azure_openai_settings.token_endpoint and ad_token is None:
75+
if (
76+
azure_openai_settings.api_key is None
77+
and ad_token_provider is None
78+
and azure_openai_settings.token_endpoint
79+
and ad_token is None
80+
and async_client is None
81+
):
7682
ad_token = azure_openai_settings.get_azure_openai_auth_token(
7783
token_endpoint=azure_openai_settings.token_endpoint
7884
)
7985

80-
if not azure_openai_settings.api_key and not ad_token and not ad_token_provider:
81-
raise ServiceInitializationError("Please provide either api_key, ad_token or ad_token_provider")
86+
if not azure_openai_settings.api_key and not ad_token and not ad_token_provider and not async_client:
87+
raise ServiceInitializationError(
88+
"Please provide either api_key, ad_token, ad_token_provider, or a custom client."
89+
)
8290

8391
super().__init__(
8492
deployment_name=azure_openai_settings.text_deployment_name,

python/semantic_kernel/connectors/ai/open_ai/services/azure_text_embedding.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,22 @@ def __init__(
7474

7575
# If the api_key is none, and the ad_token is none, and the ad_token_provider is none,
7676
# then we will attempt to get the ad_token using the default endpoint specified in the Azure OpenAI settings.
77-
if api_key is None and ad_token_provider is None and azure_openai_settings.token_endpoint and ad_token is None:
77+
if (
78+
azure_openai_settings.api_key is None
79+
and ad_token_provider is None
80+
and azure_openai_settings.token_endpoint
81+
and ad_token is None
82+
and async_client is None
83+
):
7884
ad_token = azure_openai_settings.get_azure_openai_auth_token(
7985
token_endpoint=azure_openai_settings.token_endpoint
8086
)
8187

88+
if not azure_openai_settings.api_key and not ad_token and not ad_token_provider and not async_client:
89+
raise ServiceInitializationError(
90+
"Please provide either api_key, ad_token, ad_token_provider, or a custom client"
91+
)
92+
8293
super().__init__(
8394
deployment_name=azure_openai_settings.embedding_deployment_name,
8495
endpoint=azure_openai_settings.endpoint,

python/tests/unit/connectors/open_ai/services/test_azure_text_completion.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
OpenAITextPromptExecutionSettings,
1212
)
1313
from semantic_kernel.connectors.ai.open_ai.services.azure_text_completion import AzureTextCompletion
14+
from semantic_kernel.connectors.ai.open_ai.settings.azure_open_ai_settings import AzureOpenAISettings
1415
from semantic_kernel.connectors.ai.text_completion_client_base import TextCompletionClientBase
1516
from semantic_kernel.contents.text_content import TextContent
1617
from semantic_kernel.exceptions import ServiceInitializationError
@@ -54,6 +55,20 @@ def test_init_with_custom_header(azure_openai_unit_test_env) -> None:
5455
assert azure_text_completion.client.default_headers[key] == value
5556

5657

58+
def test_azure_text_embedding_generates_no_token_with_api_key_in_env(azure_openai_unit_test_env) -> None:
59+
with (
60+
patch(
61+
f"{AzureOpenAISettings.__module__}.{AzureOpenAISettings.__qualname__}.get_azure_openai_auth_token",
62+
) as mock_get_token,
63+
):
64+
mock_get_token.return_value = "test_token"
65+
azure_text_completion = AzureTextCompletion()
66+
67+
assert azure_text_completion.client is not None
68+
# API key is provided in env var, so the ad_token should be None
69+
assert mock_get_token.call_count == 0
70+
71+
5772
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_TEXT_DEPLOYMENT_NAME"]], indirect=True)
5873
def test_init_with_empty_deployment_name(monkeypatch, azure_openai_unit_test_env) -> None:
5974
monkeypatch.delenv("AZURE_OPENAI_TEXT_DEPLOYMENT_NAME", raising=False)

python/tests/unit/connectors/open_ai/services/test_azure_text_embedding.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import EmbeddingGeneratorBase
1010
from semantic_kernel.connectors.ai.open_ai.services.azure_text_embedding import AzureTextEmbedding
11+
from semantic_kernel.connectors.ai.open_ai.settings.azure_open_ai_settings import AzureOpenAISettings
1112
from semantic_kernel.exceptions.service_exceptions import ServiceInitializationError
1213

1314

@@ -74,6 +75,20 @@ def test_azure_text_embedding_init_with_from_dict(azure_openai_unit_test_env) ->
7475
assert azure_text_embedding.client.default_headers[key] == value
7576

7677

78+
def test_azure_text_embedding_generates_no_token_with_api_key_in_env(azure_openai_unit_test_env) -> None:
79+
with (
80+
patch(
81+
f"{AzureOpenAISettings.__module__}.{AzureOpenAISettings.__qualname__}.get_azure_openai_auth_token",
82+
) as mock_get_token,
83+
):
84+
mock_get_token.return_value = "test_token"
85+
azure_text_embedding = AzureTextEmbedding()
86+
87+
assert azure_text_embedding.client is not None
88+
# API key is provided in env var, so the ad_token should be None
89+
assert mock_get_token.call_count == 0
90+
91+
7792
@pytest.mark.asyncio
7893
@patch.object(AsyncEmbeddings, "create", new_callable=AsyncMock)
7994
async def test_azure_text_embedding_calls_with_parameters(mock_create, azure_openai_unit_test_env) -> None:

0 commit comments

Comments
 (0)