mirror of
https://github.com/home-assistant/core.git
synced 2026-08-03 20:24:55 +02:00
Lower reasoning effort for recommended conversations, raise it for AI tasks
Recommended settings are resolved at runtime, so subentries without a stored reasoning effort now use minimal for conversation (voice commands stay snappy) and medium for AI tasks (quality over latency). The efforts a model accepts differ per model, so the entity default only applies to the recommended model, other models keep using low. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SNEGNPyUtLLDerutyVvE3F
This commit is contained in:
@@ -55,6 +55,7 @@ from .const import (
|
||||
LOGGER,
|
||||
RECOMMENDED_AI_TASK_OPTIONS,
|
||||
RECOMMENDED_CHAT_MODEL,
|
||||
RECOMMENDED_CONVERSATION_REASONING_EFFORT,
|
||||
RECOMMENDED_MAX_TOKENS,
|
||||
RECOMMENDED_REASONING_EFFORT,
|
||||
RECOMMENDED_REASONING_SUMMARY,
|
||||
@@ -217,7 +218,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
if model.startswith(("o", "gpt-5")):
|
||||
model_args["reasoning"] = {
|
||||
"effort": conversation_subentry.data.get(
|
||||
CONF_REASONING_EFFORT, RECOMMENDED_REASONING_EFFORT
|
||||
CONF_REASONING_EFFORT,
|
||||
RECOMMENDED_CONVERSATION_REASONING_EFFORT
|
||||
if model == RECOMMENDED_CHAT_MODEL
|
||||
else RECOMMENDED_REASONING_EFFORT,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from homeassistant.util.json import json_loads
|
||||
from .const import (
|
||||
CONF_CHAT_MODEL,
|
||||
CONF_IMAGE_MODEL,
|
||||
RECOMMENDED_AI_TASK_REASONING_EFFORT,
|
||||
RECOMMENDED_CHAT_MODEL,
|
||||
RECOMMENDED_IMAGE_MODEL,
|
||||
UNSUPPORTED_IMAGE_MODELS,
|
||||
@@ -52,6 +53,8 @@ class OpenAITaskEntity(
|
||||
):
|
||||
"""OpenAI AI Task entity."""
|
||||
|
||||
_recommended_reasoning_effort = RECOMMENDED_AI_TASK_REASONING_EFFORT
|
||||
|
||||
def __init__(self, entry: OpenAIConfigEntry, subentry: ConfigSubentry) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(entry, subentry)
|
||||
|
||||
@@ -44,6 +44,8 @@ RECOMMENDED_IMAGE_MODEL = "gpt-image-2"
|
||||
RECOMMENDED_MAX_TOKENS = 3000
|
||||
RECOMMENDED_PRO_MODE = False
|
||||
RECOMMENDED_REASONING_EFFORT = "low"
|
||||
RECOMMENDED_CONVERSATION_REASONING_EFFORT = "minimal"
|
||||
RECOMMENDED_AI_TASK_REASONING_EFFORT = "medium"
|
||||
RECOMMENDED_STORE_RESPONSES = False
|
||||
RECOMMENDED_REASONING_SUMMARY = "auto"
|
||||
RECOMMENDED_SERVICE_TIER = "auto"
|
||||
|
||||
@@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import OpenAIConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, RECOMMENDED_CONVERSATION_REASONING_EFFORT
|
||||
from .entity import OpenAIBaseLLMEntity
|
||||
|
||||
# Max number of back and forth with the LLM to generate a response
|
||||
@@ -39,6 +39,7 @@ class OpenAIConversationEntity(
|
||||
"""OpenAI conversation agent."""
|
||||
|
||||
_attr_supports_streaming = True
|
||||
_recommended_reasoning_effort = RECOMMENDED_CONVERSATION_REASONING_EFFORT
|
||||
|
||||
def __init__(self, entry: OpenAIConfigEntry, subentry: ConfigSubentry) -> None:
|
||||
"""Initialize the agent."""
|
||||
|
||||
@@ -480,6 +480,8 @@ class OpenAIBaseLLMEntity(Entity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name: str | None = None
|
||||
# Effort used in recommended mode, must be supported by RECOMMENDED_CHAT_MODEL
|
||||
_recommended_reasoning_effort = RECOMMENDED_REASONING_EFFORT
|
||||
|
||||
def __init__(self, entry: OpenAIConfigEntry, subentry: ConfigSubentry) -> None:
|
||||
"""Initialize the entity."""
|
||||
@@ -523,10 +525,14 @@ class OpenAIBaseLLMEntity(Entity):
|
||||
)
|
||||
|
||||
if model_args["model"].startswith(("o", "gpt-5")):
|
||||
# Other models support a different set of efforts
|
||||
recommended_effort = (
|
||||
self._recommended_reasoning_effort
|
||||
if model_args["model"] == RECOMMENDED_CHAT_MODEL
|
||||
else RECOMMENDED_REASONING_EFFORT
|
||||
)
|
||||
reasoning: Reasoning = {
|
||||
"effort": options.get(
|
||||
CONF_REASONING_EFFORT, RECOMMENDED_REASONING_EFFORT
|
||||
)
|
||||
"effort": options.get(CONF_REASONING_EFFORT, recommended_effort)
|
||||
if not model_args["model"].startswith("gpt-5-pro")
|
||||
else "high", # GPT-5 pro only supports reasoning.effort: high
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
}),
|
||||
])
|
||||
# ---
|
||||
# name: test_model_args[subentry_options0]
|
||||
# name: test_model_args[pro_mode]
|
||||
dict({
|
||||
'include': list([
|
||||
'reasoning.encrypted_content',
|
||||
@@ -318,6 +318,25 @@
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_model_args[recommended]
|
||||
dict({
|
||||
'include': list([
|
||||
'reasoning.encrypted_content',
|
||||
]),
|
||||
'max_output_tokens': 3000,
|
||||
'model': 'gpt-5-nano',
|
||||
'reasoning': dict({
|
||||
'effort': 'minimal',
|
||||
'summary': 'auto',
|
||||
}),
|
||||
'service_tier': 'auto',
|
||||
'store': False,
|
||||
'stream': True,
|
||||
'text': dict({
|
||||
'verbosity': 'medium',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_web_search[False]
|
||||
list([
|
||||
dict({
|
||||
|
||||
@@ -68,6 +68,26 @@ async def test_generate_data(
|
||||
assert mock_create_stream.call_args.kwargs["store"] is expected_store
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_init_component", "mock_config_entry")
|
||||
async def test_recommended_reasoning_effort(
|
||||
hass: HomeAssistant,
|
||||
mock_create_stream: AsyncMock,
|
||||
) -> None:
|
||||
"""Test AI Task reasons more than conversation with recommended settings."""
|
||||
mock_create_stream.return_value = [
|
||||
create_message_item(id="msg_A", text="The test data", output_index=0)
|
||||
]
|
||||
|
||||
await ai_task.async_generate_data(
|
||||
hass,
|
||||
task_name="Test Task",
|
||||
entity_id="ai_task.openai_ai_task",
|
||||
instructions="Generate test data",
|
||||
)
|
||||
|
||||
assert mock_create_stream.call_args.kwargs["reasoning"]["effort"] == "medium"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_init_component")
|
||||
async def test_generate_structured_data(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -821,7 +821,13 @@ async def test_flex_tier_retry(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"subentry_options", [{CONF_CHAT_MODEL: "gpt-5.6-sol", CONF_PRO_MODE: True}]
|
||||
"subentry_options",
|
||||
[
|
||||
pytest.param(
|
||||
{CONF_CHAT_MODEL: "gpt-5.6-sol", CONF_PRO_MODE: True}, id="pro_mode"
|
||||
),
|
||||
pytest.param({}, id="recommended"),
|
||||
],
|
||||
)
|
||||
async def test_model_args(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -429,7 +429,7 @@ async def test_generate_content_service(
|
||||
service_data["config_entry"] = mock_config_entry.entry_id
|
||||
expected_args["model"] = "gpt-5-nano"
|
||||
expected_args["max_output_tokens"] = 3000
|
||||
expected_args["reasoning"] = {"effort": "low"}
|
||||
expected_args["reasoning"] = {"effort": "minimal"}
|
||||
expected_args["user"] = None
|
||||
expected_args["store"] = store_responses
|
||||
expected_args["input"][0]["type"] = "message"
|
||||
|
||||
Reference in New Issue
Block a user