From 2089f53b3825207638444eb7bdcfd469b1bf5543 Mon Sep 17 00:00:00 2001 From: Denis Shulyaka Date: Wed, 9 Sep 2026 14:21:47 +0300 Subject: [PATCH] Add support for GPT-Image-2.5 (#181735) --- .../openai_conversation/config_flow.py | 2 + .../components/openai_conversation/const.py | 2 +- .../components/openai_conversation/entity.py | 2 +- .../openai_conversation/test_ai_task.py | 46 +++++++++++++++---- .../openai_conversation/test_config_flow.py | 22 ++++++++- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/openai_conversation/config_flow.py b/homeassistant/components/openai_conversation/config_flow.py index 953b983629d9..711d8e49db86 100644 --- a/homeassistant/components/openai_conversation/config_flow.py +++ b/homeassistant/components/openai_conversation/config_flow.py @@ -553,6 +553,8 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow): ] = SelectSelector( SelectSelectorConfig( options=[ + "gpt-image-2.5-sunburst", + "gpt-image-2.5-flare", "gpt-image-2", "gpt-image-1.5", "gpt-image-1", diff --git a/homeassistant/components/openai_conversation/const.py b/homeassistant/components/openai_conversation/const.py index 6f76455b0c02..78e7bf948582 100644 --- a/homeassistant/components/openai_conversation/const.py +++ b/homeassistant/components/openai_conversation/const.py @@ -40,7 +40,7 @@ CONF_WEB_SEARCH_TIMEZONE = "timezone" CONF_WEB_SEARCH_INLINE_CITATIONS = "inline_citations" RECOMMENDED_CODE_INTERPRETER = False RECOMMENDED_CHAT_MODEL = "gpt-4o-mini" -RECOMMENDED_IMAGE_MODEL = "gpt-image-2" +RECOMMENDED_IMAGE_MODEL = "gpt-image-2.5-flare" RECOMMENDED_MAX_TOKENS = 3000 RECOMMENDED_PRO_MODE = False RECOMMENDED_REASONING_EFFORT = "low" diff --git a/homeassistant/components/openai_conversation/entity.py b/homeassistant/components/openai_conversation/entity.py index 27bb5eb2936d..21c8e772e355 100644 --- a/homeassistant/components/openai_conversation/entity.py +++ b/homeassistant/components/openai_conversation/entity.py @@ -630,7 +630,7 @@ class OpenAIBaseLLMEntity(Entity): model=image_model, output_format="png", ) - if image_model not in ("gpt-image-1-mini", "gpt-image-2"): + if image_model in ("gpt-image-1", "gpt-image-1.5"): image_tool["input_fidelity"] = "high" tools.append(image_tool) # Keep image state on OpenAI so follow-up prompts can continue by diff --git a/tests/components/openai_conversation/test_ai_task.py b/tests/components/openai_conversation/test_ai_task.py index b123dc497d4a..ae08c3e17570 100644 --- a/tests/components/openai_conversation/test_ai_task.py +++ b/tests/components/openai_conversation/test_ai_task.py @@ -10,7 +10,10 @@ import voluptuous as vol from homeassistant.components import ai_task, media_source from homeassistant.components.openai_conversation import DOMAIN -from homeassistant.components.openai_conversation.const import CONF_STORE_RESPONSES +from homeassistant.components.openai_conversation.const import ( + CONF_IMAGE_MODEL, + CONF_STORE_RESPONSES, +) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er, issue_registry as ir, selector @@ -229,12 +232,31 @@ async def test_generate_data_with_attachments( @pytest.mark.freeze_time("2025-06-14 22:59:00") @pytest.mark.parametrize("configured_store", [False, True]) @pytest.mark.parametrize( - ("image_model", "input_fidelity_present"), + ("image_options", "image_model", "input_fidelity_options"), [ - ("gpt-image-2", False), - ("gpt-image-1.5", True), - ("gpt-image-1", True), - ("gpt-image-1-mini", False), + ({}, "gpt-image-2.5-flare", {}), + ( + {CONF_IMAGE_MODEL: "gpt-image-2.5-sunburst"}, + "gpt-image-2.5-sunburst", + {}, + ), + ( + {CONF_IMAGE_MODEL: "gpt-image-2.5-flare"}, + "gpt-image-2.5-flare", + {}, + ), + ({CONF_IMAGE_MODEL: "gpt-image-2"}, "gpt-image-2", {}), + ( + {CONF_IMAGE_MODEL: "gpt-image-1.5"}, + "gpt-image-1.5", + {"input_fidelity": "high"}, + ), + ( + {CONF_IMAGE_MODEL: "gpt-image-1"}, + "gpt-image-1", + {"input_fidelity": "high"}, + ), + ({CONF_IMAGE_MODEL: "gpt-image-1-mini"}, "gpt-image-1-mini", {}), ], ) async def test_generate_image( @@ -243,8 +265,9 @@ async def test_generate_image( mock_create_stream: AsyncMock, entity_registry: er.EntityRegistry, issue_registry: ir.IssueRegistry, + image_options: dict[str, str], image_model: str, - input_fidelity_present: bool, + input_fidelity_options: dict[str, str], configured_store: bool, ) -> None: """Test AI Task image generation.""" @@ -264,7 +287,7 @@ async def test_generate_image( ai_task_entry, data={ **ai_task_entry.data, - "image_model": image_model, + **image_options, CONF_STORE_RESPONSES: configured_store, }, ) @@ -314,7 +337,12 @@ async def test_generate_image( if tool["type"] == "image_generation" ), ) - assert ("input_fidelity" in image_tool) == input_fidelity_present + assert image_tool == { + "type": "image_generation", + "model": image_model, + "output_format": "png", + **input_fidelity_options, + } image_data = mock_upload_media.call_args[0][1] assert image_data.file.getvalue() == b"A" assert image_data.content_type == "image/png" diff --git a/tests/components/openai_conversation/test_config_flow.py b/tests/components/openai_conversation/test_config_flow.py index 6b95171c92a1..58913f8141d7 100644 --- a/tests/components/openai_conversation/test_config_flow.py +++ b/tests/components/openai_conversation/test_config_flow.py @@ -1352,10 +1352,27 @@ async def test_ai_task_subentry_not_loaded( assert result.get("reason") == "entry_not_loaded" +@pytest.mark.usefixtures("mock_init_component") +@pytest.mark.parametrize( + ("image_options", "image_model"), + [ + ({}, "gpt-image-2.5-flare"), + ( + {CONF_IMAGE_MODEL: "gpt-image-2.5-sunburst"}, + "gpt-image-2.5-sunburst", + ), + ( + {CONF_IMAGE_MODEL: "gpt-image-2.5-flare"}, + "gpt-image-2.5-flare", + ), + ({CONF_IMAGE_MODEL: "gpt-image-2"}, "gpt-image-2"), + ], +) async def test_creating_ai_task_subentry_additional( hass: HomeAssistant, mock_config_entry: MockConfigEntry, - mock_init_component, + image_options: dict[str, str], + image_model: str, ) -> None: """Test creating an AI task subentry with additional settings.""" result = await hass.config_entries.subentries.async_init( @@ -1398,6 +1415,7 @@ async def test_creating_ai_task_subentry_additional( result["flow_id"], { CONF_CODE_INTERPRETER: False, + **image_options, }, ) @@ -1406,7 +1424,7 @@ async def test_creating_ai_task_subentry_additional( assert result4.get("data") == { CONF_RECOMMENDED: False, CONF_CHAT_MODEL: "gpt-4o", - CONF_IMAGE_MODEL: "gpt-image-2", + CONF_IMAGE_MODEL: image_model, CONF_MAX_TOKENS: 200, CONF_STORE_RESPONSES: True, CONF_TEMPERATURE: 0.5,