diff --git a/homeassistant/components/openai_conversation/config_flow.py b/homeassistant/components/openai_conversation/config_flow.py index a9fdf5fd771..24426d7543f 100644 --- a/homeassistant/components/openai_conversation/config_flow.py +++ b/homeassistant/components/openai_conversation/config_flow.py @@ -428,7 +428,9 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow): if user_input is not None: if user_input.get(CONF_WEB_SEARCH): - if user_input.get(CONF_WEB_SEARCH_USER_LOCATION): + if user_input.get(CONF_REASONING_EFFORT) == "minimal": + errors[CONF_WEB_SEARCH] = "web_search_minimal_reasoning" + if user_input.get(CONF_WEB_SEARCH_USER_LOCATION) and not errors: user_input.update(await self._get_location_data()) else: options.pop(CONF_WEB_SEARCH_CITY, None) @@ -437,16 +439,17 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow): options.pop(CONF_WEB_SEARCH_TIMEZONE, None) options.update(user_input) - if self._is_new: - return self.async_create_entry( - title=options.pop(CONF_NAME), + if not errors: + if self._is_new: + return self.async_create_entry( + title=options.pop(CONF_NAME), + data=options, + ) + return self.async_update_and_abort( + self._get_entry(), + self._get_reconfigure_subentry(), data=options, ) - return self.async_update_and_abort( - self._get_entry(), - self._get_reconfigure_subentry(), - data=options, - ) return self.async_show_form( step_id="model", diff --git a/homeassistant/components/openai_conversation/const.py b/homeassistant/components/openai_conversation/const.py index 9d936e03348..5118c5b8f98 100644 --- a/homeassistant/components/openai_conversation/const.py +++ b/homeassistant/components/openai_conversation/const.py @@ -55,6 +55,7 @@ UNSUPPORTED_MODELS: list[str] = [ ] UNSUPPORTED_WEB_SEARCH_MODELS: list[str] = [ + "gpt-5-nano", "gpt-3.5", "gpt-4-turbo", "gpt-4.1-nano", @@ -63,7 +64,7 @@ UNSUPPORTED_WEB_SEARCH_MODELS: list[str] = [ ] UNSUPPORTED_IMAGE_MODELS: list[str] = [ - "gpt-5", + "gpt-5-mini", "o3-mini", "o4", "o1", diff --git a/homeassistant/components/openai_conversation/strings.json b/homeassistant/components/openai_conversation/strings.json index e5b3cb30646..acfe1475f5d 100644 --- a/homeassistant/components/openai_conversation/strings.json +++ b/homeassistant/components/openai_conversation/strings.json @@ -70,7 +70,8 @@ "entry_not_loaded": "Cannot add things while the configuration is disabled." }, "error": { - "model_not_supported": "This model is not supported, please select a different model" + "model_not_supported": "This model is not supported, please select a different model", + "web_search_minimal_reasoning": "Web search is currently not supported with minimal reasoning effort" } }, "ai_task_data": { @@ -98,6 +99,7 @@ "model": { "title": "[%key:component::openai_conversation::config_subentries::conversation::step::model::title%]", "data": { + "code_interpreter": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data::code_interpreter%]", "reasoning_effort": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data::reasoning_effort%]", "image_model": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data::image_model%]", "web_search": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data::web_search%]", @@ -105,6 +107,7 @@ "user_location": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data::user_location%]" }, "data_description": { + "code_interpreter": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data_description::code_interpreter%]", "reasoning_effort": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data_description::reasoning_effort%]", "image_model": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data_description::image_model%]", "web_search": "[%key:component::openai_conversation::config_subentries::conversation::step::model::data_description::web_search%]", @@ -118,7 +121,8 @@ "entry_not_loaded": "[%key:component::openai_conversation::config_subentries::conversation::abort::entry_not_loaded%]" }, "error": { - "model_not_supported": "[%key:component::openai_conversation::config_subentries::conversation::error::model_not_supported%]" + "model_not_supported": "[%key:component::openai_conversation::config_subentries::conversation::error::model_not_supported%]", + "web_search_minimal_reasoning": "[%key:component::openai_conversation::config_subentries::conversation::error::web_search_minimal_reasoning%]" } } }, diff --git a/tests/components/openai_conversation/test_config_flow.py b/tests/components/openai_conversation/test_config_flow.py index ce0a64ea718..154a90ae210 100644 --- a/tests/components/openai_conversation/test_config_flow.py +++ b/tests/components/openai_conversation/test_config_flow.py @@ -234,6 +234,62 @@ async def test_subentry_unsupported_model( assert subentry_flow["errors"] == {"chat_model": "model_not_supported"} +async def test_subentry_websearch_unsupported_reasoning_effort( + hass: HomeAssistant, mock_config_entry, mock_init_component +) -> None: + """Test the subentry form giving error about unsupported minimal reasoning effort.""" + subentry = next(iter(mock_config_entry.subentries.values())) + subentry_flow = await mock_config_entry.start_subentry_reconfigure_flow( + hass, subentry.subentry_id + ) + assert subentry_flow["type"] is FlowResultType.FORM + assert subentry_flow["step_id"] == "init" + + # Configure initial step + subentry_flow = await hass.config_entries.subentries.async_configure( + subentry_flow["flow_id"], + { + CONF_RECOMMENDED: False, + CONF_PROMPT: "Speak like a pirate", + CONF_LLM_HASS_API: ["assist"], + }, + ) + assert subentry_flow["type"] is FlowResultType.FORM + assert subentry_flow["step_id"] == "advanced" + + # Configure advanced step + subentry_flow = await hass.config_entries.subentries.async_configure( + subentry_flow["flow_id"], + { + CONF_CHAT_MODEL: "gpt-5", + }, + ) + assert subentry_flow["type"] is FlowResultType.FORM + assert subentry_flow["step_id"] == "model" + + # Configure model step + subentry_flow = await hass.config_entries.subentries.async_configure( + subentry_flow["flow_id"], + { + CONF_REASONING_EFFORT: "minimal", + CONF_WEB_SEARCH: True, + }, + ) + assert subentry_flow["type"] is FlowResultType.FORM + assert subentry_flow["errors"] == {"web_search": "web_search_minimal_reasoning"} + + # Reconfigure model step + subentry_flow = await hass.config_entries.subentries.async_configure( + subentry_flow["flow_id"], + { + CONF_REASONING_EFFORT: "low", + CONF_WEB_SEARCH: True, + }, + ) + assert subentry_flow["type"] is FlowResultType.ABORT + assert subentry_flow["reason"] == "reconfigure_successful" + + @pytest.mark.parametrize( ("side_effect", "error"), [