diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index a21f09bf38ea..11e61ca17e7e 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -19,6 +19,6 @@ "learn_more_url": "https://support.nabucasa.com/hc/en-us/articles/29718084245149-Speech-to-text-STT" } }, - "requirements": ["hass-nabucasa==2.7.0", "openai==2.45.0"], + "requirements": ["hass-nabucasa==2.7.0", "openai==3.10.0"], "single_config_entry": true } diff --git a/homeassistant/components/litellm/config_flow.py b/homeassistant/components/litellm/config_flow.py index e22fbcce329d..177bf7e24ed7 100644 --- a/homeassistant/components/litellm/config_flow.py +++ b/homeassistant/components/litellm/config_flow.py @@ -1,7 +1,7 @@ """Config flow for LiteLLM integration.""" import logging -from typing import Any, override +from typing import Any, cast, override from openai import AsyncOpenAI, AuthenticationError, OpenAIError, PermissionDeniedError import voluptuous as vol @@ -65,7 +65,8 @@ async def _get_models(hass: HomeAssistant, url: str, api_key: str | None) -> lis client = AsyncOpenAI( base_url=url, api_key=api_key or PLACEHOLDER_API_KEY, - http_client=get_async_client(hass), + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) try: return [ diff --git a/homeassistant/components/litellm/coordinator.py b/homeassistant/components/litellm/coordinator.py index ecd856bf6fd8..47cba3f72539 100644 --- a/homeassistant/components/litellm/coordinator.py +++ b/homeassistant/components/litellm/coordinator.py @@ -1,7 +1,7 @@ """Coordinator for the LiteLLM integration.""" from datetime import timedelta -from typing import override +from typing import Any, cast, override from openai import AsyncOpenAI, AuthenticationError, OpenAIError, PermissionDeniedError @@ -40,7 +40,8 @@ class LiteLLMDataUpdateCoordinator(DataUpdateCoordinator[None]): self.client = AsyncOpenAI( base_url=config_entry.data[CONF_URL], api_key=config_entry.data.get(CONF_API_KEY) or PLACEHOLDER_API_KEY, - http_client=get_async_client(hass), + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) @override diff --git a/homeassistant/components/litellm/manifest.json b/homeassistant/components/litellm/manifest.json index 595ec0710b38..5342871ba983 100644 --- a/homeassistant/components/litellm/manifest.json +++ b/homeassistant/components/litellm/manifest.json @@ -9,5 +9,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "quality_scale": "bronze", - "requirements": ["openai==2.45.0"] + "requirements": ["openai==3.10.0"] } diff --git a/homeassistant/components/llama_cpp/api.py b/homeassistant/components/llama_cpp/api.py index bf2c7bfa3959..5ee7f95014b6 100644 --- a/homeassistant/components/llama_cpp/api.py +++ b/homeassistant/components/llama_cpp/api.py @@ -62,7 +62,8 @@ async def async_create_client( client = openai.AsyncOpenAI( api_key=api_key, base_url=config_entry_data[CONF_BASE_URL], - http_client=get_async_client(hass), + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) # Cache current platform data which gets added to each request # (caching done by library) diff --git a/homeassistant/components/llama_cpp/manifest.json b/homeassistant/components/llama_cpp/manifest.json index a610d9027208..b3599d7eeaf0 100644 --- a/homeassistant/components/llama_cpp/manifest.json +++ b/homeassistant/components/llama_cpp/manifest.json @@ -9,5 +9,5 @@ "integration_type": "service", "iot_class": "local_polling", "quality_scale": "bronze", - "requirements": ["openai==2.45.0"] + "requirements": ["openai==3.10.0"] } diff --git a/homeassistant/components/open_router/__init__.py b/homeassistant/components/open_router/__init__.py index e25ab075383d..e09406740745 100644 --- a/homeassistant/components/open_router/__init__.py +++ b/homeassistant/components/open_router/__init__.py @@ -1,5 +1,7 @@ """The OpenRouter integration.""" +from typing import Any, cast + from openai import AsyncOpenAI, AuthenticationError, OpenAIError from homeassistant.config_entries import ConfigEntry @@ -20,7 +22,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: OpenRouterConfigEntry) - client = AsyncOpenAI( base_url="https://openrouter.ai/api/v1", api_key=entry.data[CONF_API_KEY], - http_client=get_async_client(hass), + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) # Cache current platform data which gets added to each request diff --git a/homeassistant/components/open_router/manifest.json b/homeassistant/components/open_router/manifest.json index 8b835969cbe0..fa80c1fa7a19 100644 --- a/homeassistant/components/open_router/manifest.json +++ b/homeassistant/components/open_router/manifest.json @@ -9,5 +9,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "quality_scale": "bronze", - "requirements": ["openai==2.45.0", "python-open-router==0.4.0"] + "requirements": ["openai==3.10.0", "python-open-router==0.4.0"] } diff --git a/homeassistant/components/openai_conversation/__init__.py b/homeassistant/components/openai_conversation/__init__.py index 2ff1088ce1c9..67799727ed54 100644 --- a/homeassistant/components/openai_conversation/__init__.py +++ b/homeassistant/components/openai_conversation/__init__.py @@ -2,6 +2,7 @@ from pathlib import Path from types import MappingProxyType +from typing import Any, cast import openai from openai.types.images_response import ImagesResponse @@ -282,7 +283,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: OpenAIConfigEntry) -> bo """Set up OpenAI Conversation from a config entry.""" client = openai.AsyncOpenAI( api_key=entry.data[CONF_API_KEY], - http_client=get_async_client(hass), + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) # Cache current platform data which gets added to each request @@ -290,7 +292,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: OpenAIConfigEntry) -> bo _ = await hass.async_add_executor_job(client.platform_headers) try: - await hass.async_add_executor_job(client.with_options(timeout=10.0).models.list) + await client.models.list(timeout=10.0) except openai.AuthenticationError as err: raise ConfigEntryAuthFailed(err) from err except openai.OpenAIError as err: diff --git a/homeassistant/components/openai_conversation/ai_task.py b/homeassistant/components/openai_conversation/ai_task.py index e5ffa7516229..eb3caff7cec9 100644 --- a/homeassistant/components/openai_conversation/ai_task.py +++ b/homeassistant/components/openai_conversation/ai_task.py @@ -140,7 +140,7 @@ class OpenAITaskEntity( else: mime_type = "image/png" - if hasattr(image_call, "size") and (size := image_call.size): + if size := image_call.size: width, height = tuple(size.split("x")) else: width, height = None, None diff --git a/homeassistant/components/openai_conversation/config_flow.py b/homeassistant/components/openai_conversation/config_flow.py index 711d8e49db86..477769b96461 100644 --- a/homeassistant/components/openai_conversation/config_flow.py +++ b/homeassistant/components/openai_conversation/config_flow.py @@ -3,7 +3,7 @@ from collections.abc import Mapping import json import logging -from typing import Any, override +from typing import Any, cast, override import openai from probatio import to_openapi @@ -117,7 +117,9 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None: Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. """ client = openai.AsyncOpenAI( - api_key=data[CONF_API_KEY], http_client=get_async_client(hass) + api_key=data[CONF_API_KEY], + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) await client.models.list(timeout=10.0) @@ -651,7 +653,7 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow): if zone_home is not None: client = openai.AsyncOpenAI( api_key=self._get_entry().data[CONF_API_KEY], - http_client=get_async_client(self.hass), + http_client=cast(Any, get_async_client(self.hass)), ) location_schema = vol.Schema( { diff --git a/homeassistant/components/openai_conversation/manifest.json b/homeassistant/components/openai_conversation/manifest.json index 95fb8fc7d211..1af6e41921a0 100644 --- a/homeassistant/components/openai_conversation/manifest.json +++ b/homeassistant/components/openai_conversation/manifest.json @@ -9,5 +9,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "quality_scale": "bronze", - "requirements": ["openai==2.45.0"] + "requirements": ["openai==3.10.0"] } diff --git a/homeassistant/components/ovhcloud_ai_endpoints/__init__.py b/homeassistant/components/ovhcloud_ai_endpoints/__init__.py index 54f225060787..83667c6c367d 100644 --- a/homeassistant/components/ovhcloud_ai_endpoints/__init__.py +++ b/homeassistant/components/ovhcloud_ai_endpoints/__init__.py @@ -1,5 +1,7 @@ """The OVHcloud AI Endpoints integration.""" +from typing import Any, cast + from openai import ( AsyncOpenAI, AuthenticationError, @@ -27,7 +29,8 @@ def _create_client(hass: HomeAssistant, api_key: str) -> AsyncOpenAI: return AsyncOpenAI( base_url=BASE_URL, api_key=api_key, - http_client=get_async_client(hass), + # Legacy HTTPX clients are supported at runtime only. + http_client=cast(Any, get_async_client(hass)), ) diff --git a/homeassistant/components/ovhcloud_ai_endpoints/manifest.json b/homeassistant/components/ovhcloud_ai_endpoints/manifest.json index 93feba804811..a1c56ee0592d 100644 --- a/homeassistant/components/ovhcloud_ai_endpoints/manifest.json +++ b/homeassistant/components/ovhcloud_ai_endpoints/manifest.json @@ -9,5 +9,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "quality_scale": "silver", - "requirements": ["openai==2.45.0"] + "requirements": ["openai==3.10.0"] } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index edcb7114de95..17f13279796b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -47,7 +47,7 @@ ifaddr==0.2.0 Jinja2==3.1.6 lru-dict==1.4.1 mutagen==1.48.1 -openai==2.45.0 +openai==3.10.0 orjson==3.11.9 packaging>=23.1 paho-mqtt==2.1.0 diff --git a/requirements_all.txt b/requirements_all.txt index 57b5c23dc548..1b1321161254 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1821,7 +1821,7 @@ open-meteo==0.3.2 # homeassistant.components.open_router # homeassistant.components.openai_conversation # homeassistant.components.ovhcloud_ai_endpoints -openai==2.45.0 +openai==3.10.0 # homeassistant.components.openerz openerz-api==0.3.0 diff --git a/tests/components/openai_conversation/conftest.py b/tests/components/openai_conversation/conftest.py index 22a18743394b..06a6b316765d 100644 --- a/tests/components/openai_conversation/conftest.py +++ b/tests/components/openai_conversation/conftest.py @@ -1,6 +1,6 @@ """Tests helpers.""" -from collections.abc import Generator +from collections.abc import AsyncGenerator, Generator from typing import Any from unittest.mock import AsyncMock, MagicMock, patch @@ -122,13 +122,15 @@ async def mock_config_entry_with_reasoning_model( @pytest.fixture async def mock_init_component( hass: HomeAssistant, mock_config_entry: MockConfigEntry -) -> None: +) -> AsyncGenerator[None]: """Initialize integration.""" with patch( "openai.resources.models.AsyncModels.list", + new_callable=AsyncMock, ): assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() + yield @pytest.fixture(autouse=True) diff --git a/tests/components/openai_conversation/test_init.py b/tests/components/openai_conversation/test_init.py index 9a09697af7a9..06651c6d2d5c 100644 --- a/tests/components/openai_conversation/test_init.py +++ b/tests/components/openai_conversation/test_init.py @@ -277,12 +277,13 @@ async def test_init_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, caplog: pytest.LogCaptureFixture, - side_effect, - error, + side_effect: APIConnectionError | BadRequestError, + error: str, ) -> None: """Test initialization errors.""" with patch( "openai.resources.models.AsyncModels.list", + new_callable=AsyncMock, side_effect=side_effect, ): assert await async_setup_component(hass, DOMAIN, {}) @@ -298,6 +299,7 @@ async def test_init_auth_error( """Test auth error during init errors.""" with patch( "openai.resources.models.AsyncModels.list", + new_callable=AsyncMock, side_effect=AuthenticationError( response=httpx.Response( status_code=500, request=httpx.Request(method="GET", url="test")