This is an automated email from the ASF dual-hosted git repository.
kaxil pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new dcd360f7454 Fix common ai Azure OpenAI docs (#73024)
dcd360f7454 is described below
commit dcd360f7454780ec4602c6cba3a8bff9d7b10d00
Author: Jeff(Wei-Hao) Lu <[email protected]>
AuthorDate: Wed Sep 16 05:24:39 2026 +0800
Fix common ai Azure OpenAI docs (#73024)
---
.../common/ai/docs/connections/pydantic_ai.rst | 4 ++-
.../ai/docs/connections/pydantic_ai_azure.rst | 32 +++++++++++-------
providers/common/ai/provider.yaml | 8 +++--
.../providers/common/ai/get_provider_info.py | 6 ++--
.../providers/common/ai/hooks/pydantic_ai.py | 12 ++++---
.../tests/unit/common/ai/hooks/test_pydantic_ai.py | 38 +++++++++++++++++++++-
6 files changed, 76 insertions(+), 24 deletions(-)
diff --git a/providers/common/ai/docs/connections/pydantic_ai.rst
b/providers/common/ai/docs/connections/pydantic_ai.rst
index e71b703da48..19a48988da9 100644
--- a/providers/common/ai/docs/connections/pydantic_ai.rst
+++ b/providers/common/ai/docs/connections/pydantic_ai.rst
@@ -60,7 +60,9 @@ Host (optional)
- Ollama: ``http://localhost:11434/v1``
- vLLM: ``http://localhost:8000/v1``
- - Azure OpenAI:
``https://<resource>.openai.azure.com/openai/deployments/<deployment>``
+ - Azure OpenAI with an ``openai:`` model:
+ ``https://<resource>.openai.azure.com/openai/v1``. For an ``azure:``
model,
+ use the dedicated :doc:`pydantic_ai_azure` connection instead.
- Any OpenAI-compatible API: the base URL of that service
Extra (JSON, optional)
diff --git a/providers/common/ai/docs/connections/pydantic_ai_azure.rst
b/providers/common/ai/docs/connections/pydantic_ai_azure.rst
index 4c8ee7daf65..8f7b9167060 100644
--- a/providers/common/ai/docs/connections/pydantic_ai_azure.rst
+++ b/providers/common/ai/docs/connections/pydantic_ai_azure.rst
@@ -23,9 +23,11 @@ Pydantic AI (Azure OpenAI) Connection
The ``pydanticai_azure`` connection type configures access to
`Azure OpenAI
<https://azure.microsoft.com/en-us/products/ai-services/openai-service>`__
via the pydantic-ai framework. It backs ``PydanticAIAzureHook``, the dedicated
-subclass of ``PydanticAIHook`` for Azure's non-standard auth (an endpoint URL
-plus an API version, rather than the plain ``api_key`` + optional ``base_url``
-that the generic :doc:`pydantic_ai` connection assumes).
+subclass of ``PydanticAIHook`` that maps Airflow connection fields to
+Azure-specific parameters: ``api_key``, ``azure_endpoint``, and, for endpoints
+that do not use the OpenAI-compatible v1 API, ``api_version``. The generic
+:doc:`pydantic_ai` connection instead supplies ``api_key`` and an optional
+``base_url``.
.. note::
@@ -71,11 +73,14 @@ API Key (Password field)
Azure Endpoint (Host field)
The Azure OpenAI resource endpoint, e.g.
- ``https://<resource>.openai.azure.com/openai/deployments/<deployment>``.
+ ``https://<resource>.openai.azure.com/openai/v1``.
API Version (Extra field)
- Azure OpenAI API version (e.g. ``2024-07-01-preview``). Falls back to the
- ``OPENAI_API_VERSION`` environment variable when omitted.
+ Azure OpenAI API version (e.g. ``2024-07-01-preview``). Set it when the
+ endpoint path does not end in ``/v1`` and the host is not
+ ``*.models.ai.azure.com``. When required, it falls back to the
+ ``OPENAI_API_VERSION`` environment variable if omitted. Endpoints matching
+ either OpenAI-compatible v1 form reject this field.
Examples
--------
@@ -85,15 +90,18 @@ Examples
{
"conn_type": "pydanticai_azure",
"password": "<azure-api-key>",
- "host": "https://<resource>.openai.azure.com",
- "extra": "{\"model\": \"azure:gpt-4o\", \"api_version\":
\"2024-07-01-preview\"}"
+ "host": "https://<resource>.openai.azure.com/openai/v1",
+ "extra": "{\"model\": \"azure:gpt-4o\"}"
}
Relationship to the hook
-------------------------
``PydanticAIAzureHook`` maps the connection's ``password`` to the provider's
-``api_key``, ``host`` to ``azure_endpoint``, and ``extra["api_version"]`` to
-``api_version``, then constructs pydantic-ai's Azure provider with those
values.
-If none of them are set, the hook falls back to pydantic-ai's own
environment-variable
-resolution (``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_ENDPOINT``,
``OPENAI_API_VERSION``).
+``api_key`` and ``host`` to ``azure_endpoint``, and maps
+``extra["api_version"]`` to ``api_version`` when provided. The API version is
+valid only when the endpoint path does not end in ``/v1`` and the host is not
+``*.models.ai.azure.com``. The hook then constructs pydantic-ai's Azure
provider
+with those values. If none of them are set, it falls back to pydantic-ai's own
+environment-variable resolution (``AZURE_OPENAI_API_KEY``,
+``AZURE_OPENAI_ENDPOINT``, ``OPENAI_API_VERSION``).
diff --git a/providers/common/ai/provider.yaml
b/providers/common/ai/provider.yaml
index bf4f9d0dbe2..c23468202df 100644
--- a/providers/common/ai/provider.yaml
+++ b/providers/common/ai/provider.yaml
@@ -194,8 +194,8 @@ connection-types:
password: API Key
host: Azure Endpoint
placeholders:
- host: "https://<resource>.openai.azure.com"
- extra: '{"model": "azure:gpt-4o", "api_version": "2024-07-01-preview"}'
+ host: "https://<resource>.openai.azure.com/openai/v1"
+ extra: '{"model": "azure:gpt-4o"}'
conn-fields:
model:
label: Model
@@ -206,7 +206,9 @@ connection-types:
- 'null'
api_version:
label: API Version
- description: "Azure OpenAI API version (e.g. 2024-07-01-preview).
Falls back to OPENAI_API_VERSION."
+ description: >-
+ Azure OpenAI API version (e.g. 2024-07-01-preview). Set when the
endpoint path does not end
+ in /v1 and the host is not *.models.ai.azure.com. Falls back to
OPENAI_API_VERSION.
schema:
type:
- string
diff --git
a/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
b/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
index 70fe47e607c..a964ee6e823 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
@@ -161,8 +161,8 @@ def get_provider_info():
"hidden-fields": ["schema", "port", "login"],
"relabeling": {"password": "API Key", "host": "Azure
Endpoint"},
"placeholders": {
- "host": "https://<resource>.openai.azure.com",
- "extra": '{"model": "azure:gpt-4o", "api_version":
"2024-07-01-preview"}',
+ "host":
"https://<resource>.openai.azure.com/openai/v1",
+ "extra": '{"model": "azure:gpt-4o"}',
},
},
"conn-fields": {
@@ -173,7 +173,7 @@ def get_provider_info():
},
"api_version": {
"label": "API Version",
- "description": "Azure OpenAI API version (e.g.
2024-07-01-preview). Falls back to OPENAI_API_VERSION.",
+ "description": "Azure OpenAI API version (e.g.
2024-07-01-preview). Set when the endpoint path does not end in /v1 and the
host is not *.models.ai.azure.com. Falls back to OPENAI_API_VERSION.",
"schema": {"type": ["string", "null"]},
},
},
diff --git
a/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
b/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
index 3ffd5da1c9d..c24672cc913 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
@@ -308,10 +308,14 @@ class PydanticAIAzureHook(PydanticAIHook):
Connection fields:
- **password**: Azure API key
- - **host**: Azure endpoint (e.g.
``https://<resource>.openai.azure.com``)
+ - **host**: Azure endpoint (e.g.
``https://<resource>.openai.azure.com/openai/v1``)
- **extra** JSON::
- {"model": "azure:gpt-4o", "api_version": "2024-07-01-preview"}
+ {"model": "azure:gpt-4o"}
+
+ ``api_version`` must be omitted when the endpoint path ends in
``/v1``
+ or the host is ``*.models.ai.azure.com``. For other endpoints, set it
+ here or with ``OPENAI_API_VERSION``.
:param llm_conn_id: Airflow connection ID.
:param model_id: Model identifier, e.g. ``"azure:gpt-4o"``.
@@ -328,8 +332,8 @@ class PydanticAIAzureHook(PydanticAIHook):
"hidden_fields": ["schema", "port", "login"],
"relabeling": {"password": "API Key", "host": "Azure Endpoint"},
"placeholders": {
- "host": "https://<resource>.openai.azure.com",
- "extra": '{"model": "azure:gpt-4o", "api_version":
"2024-07-01-preview"}',
+ "host": "https://<resource>.openai.azure.com/openai/v1",
+ "extra": '{"model": "azure:gpt-4o"}',
},
}
diff --git a/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
b/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
index 76cf0e0dd03..d4ef9a46de6 100644
--- a/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
+++ b/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
@@ -525,9 +525,29 @@ class TestPydanticAIAzureHook:
def test_hook_name(self):
assert "Azure" in PydanticAIAzureHook.hook_name
- def test_ui_field_behaviour_relabels_host(self):
+ def test_ui_metadata(self):
+ expected_placeholders = {
+ "host": "https://<resource>.openai.azure.com/openai/v1",
+ "extra": '{"model": "azure:gpt-4o"}',
+ }
+ expected_api_version_description = (
+ "Azure OpenAI API version (e.g. 2024-07-01-preview). Set when the
endpoint path does not "
+ "end in /v1 and the host is not *.models.ai.azure.com. Falls back
to OPENAI_API_VERSION."
+ )
+
behaviour = PydanticAIAzureHook.get_ui_field_behaviour()
assert behaviour["relabeling"].get("host") == "Azure Endpoint"
+ assert behaviour["placeholders"] == expected_placeholders
+
+ connection_types = get_provider_info()["connection-types"]
+ azure_connection_type = next(
+ c for c in connection_types if c["connection-type"] ==
"pydanticai_azure"
+ )
+ assert azure_connection_type["ui-field-behaviour"]["placeholders"] ==
expected_placeholders
+ assert (
+ azure_connection_type["conn-fields"]["api_version"]["description"]
+ == expected_api_version_description
+ )
def test_get_provider_kwargs_maps_azure_endpoint(self):
hook = PydanticAIAzureHook.__new__(PydanticAIAzureHook)
@@ -597,6 +617,22 @@ class TestPydanticAIAzureHook:
api_version="2024-07-01-preview",
)
+ def test_get_conn_accepts_v1_endpoint_without_api_version(self):
+ conn = Connection(
+ conn_id="azure_test",
+ conn_type="pydanticai_azure",
+ password="azure-key",
+ host="https://myresource.openai.azure.com/openai/v1",
+ extra=json.dumps({"model": "azure:gpt-4o"}),
+ )
+ hook = PydanticAIAzureHook(llm_conn_id="azure_test")
+
+ with patch.object(hook, "get_connection", return_value=conn):
+ model = hook.get_conn()
+
+ assert model.system == "azure"
+ assert model.base_url.rstrip("/") ==
"https://myresource.openai.azure.com/openai/v1"
+
@patch("airflow.providers.common.ai.hooks.pydantic_ai.infer_model",
autospec=True)
def test_get_conn_falls_back_to_env_auth_when_no_kwargs(self,
mock_infer_model):
"""No host + no password → env-var auth path (empty
_get_provider_kwargs)."""