From 60e8a4a3644c894bc5e9c8d31bc9f9040b57d14f Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Sun, 12 Apr 2026 04:59:18 +0800 Subject: [PATCH] fix: recompute is_azure_openai_endpoint after lazy endpoint resolve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_prepare_completion_params` uses `is_azure_openai_endpoint` to decide whether to include the `model` parameter in requests — Azure OpenAI endpoints embed the deployment name in the URL and reject a `model` field. When the endpoint was resolved lazily from env vars, the flag stayed at its pre-resolve `False` value, causing every lazily-inited Azure OpenAI request to include `model` and fail. Factor the classification into `_is_azure_openai_endpoint` and call it from both `_normalize_azure_fields` and `_make_client_kwargs`. Extend the lazy-build regression test to assert the flag flips to `True` once the endpoint is resolved. --- .../crewai/llms/providers/azure/completion.py | 26 ++++++++++++++----- lib/crewai/tests/llms/azure/test_azure.py | 6 ++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/crewai/src/crewai/llms/providers/azure/completion.py b/lib/crewai/src/crewai/llms/providers/azure/completion.py index fc68b42be..4b8d842a5 100644 --- a/lib/crewai/src/crewai/llms/providers/azure/completion.py +++ b/lib/crewai/src/crewai/llms/providers/azure/completion.py @@ -123,18 +123,23 @@ class AzureCompletion(BaseLLM): data["endpoint"] = AzureCompletion._validate_and_fix_endpoint( data["endpoint"], model ) - parsed = urlparse(data["endpoint"]) - hostname = parsed.hostname or "" - data["is_azure_openai_endpoint"] = ( - hostname == "openai.azure.com" or hostname.endswith(".openai.azure.com") - ) and "/openai/deployments/" in data["endpoint"] - else: - data["is_azure_openai_endpoint"] = False + data["is_azure_openai_endpoint"] = AzureCompletion._is_azure_openai_endpoint( + data["endpoint"] + ) data["is_openai_model"] = any( prefix in model.lower() for prefix in ["gpt-", "o1-", "text-"] ) return data + @staticmethod + def _is_azure_openai_endpoint(endpoint: str | None) -> bool: + if not endpoint: + return False + hostname = urlparse(endpoint).hostname or "" + return ( + hostname == "openai.azure.com" or hostname.endswith(".openai.azure.com") + ) and "/openai/deployments/" in endpoint + @model_validator(mode="after") def _init_clients(self) -> AzureCompletion: """Eagerly build clients when credentials are available, otherwise @@ -170,6 +175,13 @@ class AzureCompletion(BaseLLM): self.endpoint = AzureCompletion._validate_and_fix_endpoint( endpoint, self.model ) + # Recompute the routing flag now that the endpoint is known — + # _prepare_completion_params uses it to decide whether to + # include `model` in the request body (Azure OpenAI endpoints + # embed the deployment name in the URL and reject it). + self.is_azure_openai_endpoint = ( + AzureCompletion._is_azure_openai_endpoint(self.endpoint) + ) if not self.api_key: raise ValueError( diff --git a/lib/crewai/tests/llms/azure/test_azure.py b/lib/crewai/tests/llms/azure/test_azure.py index d5efcfc69..f113bba29 100644 --- a/lib/crewai/tests/llms/azure/test_azure.py +++ b/lib/crewai/tests/llms/azure/test_azure.py @@ -419,13 +419,16 @@ async def test_azure_aclose_is_noop_when_uninitialized(): def test_azure_lazy_build_reads_env_vars_set_after_construction(): """When `LLM(model="azure/...")` is constructed before env vars are set, the lazy client builder must re-read `AZURE_API_KEY` / `AZURE_ENDPOINT` - so the LLM actually works once credentials become available.""" + so the LLM actually works once credentials become available, and the + `is_azure_openai_endpoint` routing flag must be recomputed off the + newly-resolved endpoint.""" from crewai.llms.providers.azure.completion import AzureCompletion with patch.dict(os.environ, {}, clear=True): llm = AzureCompletion(model="gpt-4") assert llm.api_key is None assert llm.endpoint is None + assert llm.is_azure_openai_endpoint is False with patch.dict( os.environ, @@ -440,6 +443,7 @@ def test_azure_lazy_build_reads_env_vars_set_after_construction(): assert llm.api_key == "late-key" assert llm.endpoint is not None assert "test.openai.azure.com" in llm.endpoint + assert llm.is_azure_openai_endpoint is True def test_azure_endpoint_configuration():