Remove Temperature parameter from Anthropic integration (#168504)

This commit is contained in:
Denis Shulyaka
2026-04-19 08:51:02 +02:00
committed by GitHub
parent 2ec1b12a94
commit 93b236ff94
9 changed files with 98 additions and 46 deletions
@@ -230,6 +230,19 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AnthropicConfigEntry)
)
hass.config_entries.async_update_entry(entry, minor_version=3)
if entry.version == 2 and entry.minor_version == 3:
# Remove Temperature parameter
CONF_TEMPERATURE = "temperature"
for subentry in entry.subentries.values():
data = subentry.data.copy()
if CONF_TEMPERATURE not in data:
continue
data.pop(CONF_TEMPERATURE, None)
hass.config_entries.async_update_subentry(entry, subentry, data=data)
hass.config_entries.async_update_entry(entry, minor_version=4)
LOGGER.debug(
"Migration to version %s:%s successful", entry.version, entry.minor_version
)
@@ -50,7 +50,6 @@ from .const import (
CONF_PROMPT,
CONF_PROMPT_CACHING,
CONF_RECOMMENDED,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
@@ -109,7 +108,7 @@ class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Anthropic."""
VERSION = 2
MINOR_VERSION = 3
MINOR_VERSION = 4
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -324,10 +323,6 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
): SelectSelector(
SelectSelectorConfig(options=self._get_model_list(), custom_value=True)
),
vol.Optional(
CONF_TEMPERATURE,
default=DEFAULT[CONF_TEMPERATURE],
): NumberSelector(NumberSelectorConfig(min=0, max=1, step=0.05)),
vol.Optional(
CONF_PROMPT_CACHING,
default=DEFAULT[CONF_PROMPT_CACHING],
@@ -15,7 +15,6 @@ CONF_CHAT_MODEL = "chat_model"
CONF_CODE_EXECUTION = "code_execution"
CONF_MAX_TOKENS = "max_tokens"
CONF_PROMPT_CACHING = "prompt_caching"
CONF_TEMPERATURE = "temperature"
CONF_THINKING_BUDGET = "thinking_budget"
CONF_THINKING_EFFORT = "thinking_effort"
CONF_TOOL_SEARCH = "tool_search"
@@ -43,7 +42,6 @@ DEFAULT = {
CONF_CODE_EXECUTION: False,
CONF_MAX_TOKENS: 3000,
CONF_PROMPT_CACHING: PromptCaching.PROMPT.value,
CONF_TEMPERATURE: 1.0,
CONF_THINKING_BUDGET: MIN_THINKING_BUDGET,
CONF_THINKING_EFFORT: "low",
CONF_TOOL_SEARCH: False,
@@ -98,7 +98,6 @@ from .const import (
CONF_CODE_EXECUTION,
CONF_MAX_TOKENS,
CONF_PROMPT_CACHING,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
@@ -768,9 +767,6 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
model_args["output_config"] = OutputConfigParam(effort=thinking_effort)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
)
else:
thinking_budget = options.get(
CONF_THINKING_BUDGET, DEFAULT[CONF_THINKING_BUDGET]
@@ -785,9 +781,6 @@ class AnthropicBaseLLMEntity(CoordinatorEntity[AnthropicCoordinator]):
)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
)
if (
self.model_info.capabilities
@@ -82,7 +82,6 @@
'type': 'text',
}),
]),
'temperature': 1.0,
'thinking': dict({
'type': 'disabled',
}),
@@ -288,7 +287,6 @@
'type': 'text',
}),
]),
'temperature': 1.0,
'thinking': dict({
'type': 'disabled',
}),
@@ -254,7 +254,6 @@
'type': 'text',
}),
]),
'temperature': 1.0,
'thinking': dict({
'type': 'disabled',
}),
@@ -319,7 +318,6 @@
'type': 'text',
}),
]),
'temperature': 1.0,
'thinking': dict({
'type': 'disabled',
}),
@@ -64,7 +64,7 @@
'translation_key': 'conversation',
}),
}),
'entry_version': '2.3',
'entry_version': '2.4',
'options': dict({
}),
'state': 'loaded',
+1 -17
View File
@@ -30,7 +30,6 @@ from homeassistant.components.anthropic.const import (
CONF_PROMPT,
CONF_PROMPT_CACHING,
CONF_RECOMMENDED,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
@@ -276,10 +275,7 @@ async def test_subentry_options_thinking_budget_more_than_max(
# Configure advanced step
options = await hass.config_entries.subentries.async_configure(
options["flow_id"],
{
"chat_model": "claude-sonnet-4-5",
"temperature": 1,
},
{"chat_model": "claude-sonnet-4-5"},
)
assert options["type"] is FlowResultType.FORM
assert options["step_id"] == "model"
@@ -390,7 +386,6 @@ async def test_subentry_web_search_user_location(
"prompt_caching": "prompt",
"recommended": False,
"region": "California",
"temperature": 1.0,
"thinking_budget": 1024,
"timezone": "America/Los_Angeles",
"tool_search": False,
@@ -574,7 +569,6 @@ async def test_invalid_model(
},
{
CONF_CHAT_MODEL: "claude-haiku-4-5",
CONF_TEMPERATURE: 1.0,
CONF_PROMPT_CACHING: "off",
},
{
@@ -588,7 +582,6 @@ async def test_invalid_model(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_PROMPT_CACHING: "off",
CONF_TEMPERATURE: 1.0,
CONF_CHAT_MODEL: "claude-haiku-4-5",
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
CONF_THINKING_BUDGET: DEFAULT[CONF_THINKING_BUDGET],
@@ -619,7 +612,6 @@ async def test_invalid_model(
},
{
CONF_CHAT_MODEL: "claude-sonnet-4-5",
CONF_TEMPERATURE: 1.0,
CONF_PROMPT_CACHING: "automatic",
},
{
@@ -635,7 +627,6 @@ async def test_invalid_model(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_PROMPT_CACHING: "automatic",
CONF_TEMPERATURE: 1.0,
CONF_CHAT_MODEL: "claude-sonnet-4-5",
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
CONF_THINKING_BUDGET: 2048,
@@ -667,7 +658,6 @@ async def test_invalid_model(
},
{
CONF_CHAT_MODEL: "claude-opus-4-7",
CONF_TEMPERATURE: 1.0,
CONF_PROMPT_CACHING: "prompt",
},
{
@@ -683,7 +673,6 @@ async def test_invalid_model(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_PROMPT_CACHING: "prompt",
CONF_TEMPERATURE: 1.0,
CONF_CHAT_MODEL: "claude-opus-4-7",
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
CONF_THINKING_EFFORT: "xhigh",
@@ -707,7 +696,6 @@ async def test_invalid_model(
},
{
CONF_CHAT_MODEL: "claude-3-haiku-20240307",
CONF_TEMPERATURE: 0.3,
CONF_PROMPT_CACHING: "automatic",
},
{},
@@ -716,7 +704,6 @@ async def test_invalid_model(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_PROMPT_CACHING: "automatic",
CONF_TEMPERATURE: 0.3,
CONF_CHAT_MODEL: "claude-3-haiku-20240307",
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
},
@@ -726,7 +713,6 @@ async def test_invalid_model(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_PROMPT_CACHING: "off",
CONF_TEMPERATURE: 0.3,
CONF_CHAT_MODEL: DEFAULT[CONF_CHAT_MODEL],
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
CONF_THINKING_BUDGET: DEFAULT[CONF_THINKING_BUDGET],
@@ -888,7 +874,6 @@ async def test_creating_ai_task_subentry_advanced(
result["flow_id"],
{
CONF_CHAT_MODEL: "claude-sonnet-4-5",
CONF_TEMPERATURE: 0.5,
},
)
@@ -910,7 +895,6 @@ async def test_creating_ai_task_subentry_advanced(
CONF_RECOMMENDED: False,
CONF_CHAT_MODEL: "claude-sonnet-4-5",
CONF_MAX_TOKENS: 1200,
CONF_TEMPERATURE: 0.5,
CONF_TOOL_SEARCH: False,
CONF_WEB_SEARCH: False,
CONF_WEB_SEARCH_MAX_USES: 5,
+82 -9
View File
@@ -13,6 +13,7 @@ import httpx
from httpx import URL, Request, Response
import pytest
from homeassistant.components.anthropic.config_flow import AnthropicConfigFlow
from homeassistant.components.anthropic.const import DOMAIN
from homeassistant.config_entries import (
ConfigEntryDisabler,
@@ -32,6 +33,8 @@ from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
MINOR_VERSION = AnthropicConfigFlow.MINOR_VERSION
@pytest.mark.parametrize(
("side_effect", "error"),
@@ -210,7 +213,7 @@ async def test_migration_from_v1_to_v2(
await hass.async_block_till_done()
assert mock_config_entry.version == 2
assert mock_config_entry.minor_version == 3
assert mock_config_entry.minor_version == MINOR_VERSION
assert mock_config_entry.data == {"api_key": "1234"}
assert mock_config_entry.options == {}
@@ -409,7 +412,11 @@ async def test_migration_from_v1_disabled(
entry = entries[0]
assert entry.disabled_by is merged_config_entry_disabled_by
assert entry.version == 2
assert entry.minor_version == 3
assert (
entry.minor_version == 3
if merged_config_entry_disabled_by is not None
else MINOR_VERSION
)
assert not entry.options
assert entry.title == "Claude conversation"
assert len(entry.subentries) == 2
@@ -529,7 +536,7 @@ async def test_migration_from_v1_to_v2_with_multiple_keys(
for idx, entry in enumerate(entries):
assert entry.version == 2
assert entry.minor_version == 3
assert entry.minor_version == MINOR_VERSION
assert not entry.options
assert len(entry.subentries) == 1
subentry = list(entry.subentries.values())[0]
@@ -620,7 +627,7 @@ async def test_migration_from_v1_to_v2_with_same_keys(
entry = entries[0]
assert entry.version == 2
assert entry.minor_version == 3
assert entry.minor_version == MINOR_VERSION
assert not entry.options
assert len(entry.subentries) == 2 # Two subentries from the two original entries
@@ -740,7 +747,7 @@ async def test_migration_from_v2_1_to_v2_2(
assert len(entries) == 1
entry = entries[0]
assert entry.version == 2
assert entry.minor_version == 3
assert entry.minor_version == MINOR_VERSION
assert not entry.options
assert entry.title == "Claude"
assert len(entry.subentries) == 2
@@ -817,7 +824,7 @@ async def test_migration_from_v2_1_to_v2_2(
DeviceEntryDisabler.CONFIG_ENTRY,
RegistryEntryDisabler.CONFIG_ENTRY,
True,
3,
MINOR_VERSION,
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
@@ -827,7 +834,7 @@ async def test_migration_from_v2_1_to_v2_2(
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
True,
3,
MINOR_VERSION,
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
@@ -837,7 +844,7 @@ async def test_migration_from_v2_1_to_v2_2(
DeviceEntryDisabler.USER,
RegistryEntryDisabler.USER,
True,
3,
MINOR_VERSION,
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.USER,
@@ -847,7 +854,7 @@ async def test_migration_from_v2_1_to_v2_2(
None,
None,
True,
3,
MINOR_VERSION,
None,
None,
None,
@@ -984,3 +991,69 @@ async def test_migrate_entry_to_v2_3(
assert mock_config_entry.disabled_by == config_entry_disabled_by_after_migration
assert conversation_device.disabled_by == device_disabled_by_after_migration
assert conversation_entity.disabled_by == entity_disabled_by_after_migration
@pytest.mark.usefixtures("mock_setup_entry")
async def test_migrate_entry_to_v2_4(
hass: HomeAssistant,
) -> None:
"""Test migration to version 2.4."""
# Create a v2.3 config entry
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_API_KEY: "test-api-key"},
version=2,
minor_version=3,
subentries_data=[
{
"data": {
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-haiku-4-5",
},
"subentry_id": "mock_id_1",
"subentry_type": "conversation",
"title": "Claude haiku default",
"unique_id": None,
},
{
"data": {
"recommended": False,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-haiku-4-5",
"temperature": 0.5,
},
"subentry_id": "mock_id_2",
"subentry_type": "conversation",
"title": "Claude haiku non-default",
"unique_id": None,
},
],
)
mock_config_entry.add_to_hass(hass)
# Run migration
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
# Check that minor version was updated
assert mock_config_entry.version == 2
assert mock_config_entry.minor_version == MINOR_VERSION
# Verify data was not changed for the first subentry
assert mock_config_entry.subentries["mock_id_1"].data == {
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-haiku-4-5",
}
# Verify that temperature was removed from the second subentry
assert mock_config_entry.subentries["mock_id_2"].data == {
"recommended": False,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-haiku-4-5",
}