1
0
mirror of https://github.com/home-assistant/core.git synced 2026-03-01 14:25:31 +00:00

Update Anthropic models (#163897)

This commit is contained in:
Denis Shulyaka
2026-02-24 00:13:31 +03:00
committed by GitHub
parent 5611b4564f
commit 7e162cfda2
10 changed files with 52 additions and 76 deletions

View File

@@ -112,19 +112,12 @@ async def get_model_list(client: anthropic.AsyncAnthropic) -> list[SelectOptionD
# Resolve alias from versioned model name:
model_alias = (
model_info.id[:-9]
if model_info.id
not in (
"claude-3-haiku-20240307",
"claude-3-5-haiku-20241022",
"claude-3-opus-20240229",
)
if model_info.id != "claude-3-haiku-20240307"
and model_info.id[-2:-1] != "-"
else model_info.id
)
if short_form.search(model_alias):
model_alias += "-0"
if model_alias.endswith(("haiku", "opus", "sonnet")):
model_alias += "-latest"
model_options.append(
SelectOptionDict(
label=model_info.display_name,

View File

@@ -37,8 +37,6 @@ DEFAULT = {
MIN_THINKING_BUDGET = 1024
NON_THINKING_MODELS = [
"claude-3-5", # Both sonnet and haiku
"claude-3-opus",
"claude-3-haiku",
]
@@ -51,7 +49,7 @@ NON_ADAPTIVE_THINKING_MODELS = [
"claude-opus-4-20250514",
"claude-sonnet-4-0",
"claude-sonnet-4-20250514",
"claude-3",
"claude-3-haiku",
]
UNSUPPORTED_STRUCTURED_OUTPUT_MODELS = [
@@ -60,19 +58,13 @@ UNSUPPORTED_STRUCTURED_OUTPUT_MODELS = [
"claude-opus-4-20250514",
"claude-sonnet-4-0",
"claude-sonnet-4-20250514",
"claude-3",
"claude-3-haiku",
]
WEB_SEARCH_UNSUPPORTED_MODELS = [
"claude-3-haiku",
"claude-3-opus",
"claude-3-5-sonnet-20240620",
"claude-3-5-sonnet-20241022",
]
DEPRECATED_MODELS = [
"claude-3-5-haiku",
"claude-3-7-sonnet",
"claude-3-5-sonnet",
"claude-3-opus",
"claude-3",
]

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from collections.abc import Iterator
from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING
import voluptuous as vol
@@ -19,7 +19,7 @@ from homeassistant.helpers.selector import (
)
from .config_flow import get_model_list
from .const import CONF_CHAT_MODEL, DEFAULT, DEPRECATED_MODELS, DOMAIN
from .const import CONF_CHAT_MODEL, DEPRECATED_MODELS, DOMAIN
if TYPE_CHECKING:
from . import AnthropicConfigEntry
@@ -67,13 +67,23 @@ class ModelDeprecatedRepairFlow(RepairsFlow):
self._model_list_cache[entry.entry_id] = model_list
if "opus" in model:
suggested_model = "claude-opus-4-5"
elif "haiku" in model:
suggested_model = "claude-haiku-4-5"
family = "claude-opus"
elif "sonnet" in model:
suggested_model = "claude-sonnet-4-5"
family = "claude-sonnet"
else:
suggested_model = cast(str, DEFAULT[CONF_CHAT_MODEL])
family = "claude-haiku"
suggested_model = next(
(
model_option["value"]
for model_option in sorted(
(m for m in model_list if family in m["value"]),
key=lambda x: x["value"],
reverse=True,
)
),
vol.UNDEFINED,
)
schema = vol.Schema(
{

View File

@@ -81,6 +81,12 @@ async def mock_init_component(
"""Initialize integration."""
model_list = AsyncPage(
data=[
ModelInfo(
id="claude-sonnet-4-6",
created_at=datetime.datetime(2026, 2, 17, 0, 0, tzinfo=datetime.UTC),
display_name="Claude Sonnet 4.6",
type="model",
),
ModelInfo(
id="claude-opus-4-6",
created_at=datetime.datetime(2026, 2, 4, 0, 0, tzinfo=datetime.UTC),
@@ -123,30 +129,12 @@ async def mock_init_component(
display_name="Claude Sonnet 4",
type="model",
),
ModelInfo(
id="claude-3-7-sonnet-20250219",
created_at=datetime.datetime(2025, 2, 24, 0, 0, tzinfo=datetime.UTC),
display_name="Claude Sonnet 3.7",
type="model",
),
ModelInfo(
id="claude-3-5-haiku-20241022",
created_at=datetime.datetime(2024, 10, 22, 0, 0, tzinfo=datetime.UTC),
display_name="Claude Haiku 3.5",
type="model",
),
ModelInfo(
id="claude-3-haiku-20240307",
created_at=datetime.datetime(2024, 3, 7, 0, 0, tzinfo=datetime.UTC),
display_name="Claude Haiku 3",
type="model",
),
ModelInfo(
id="claude-3-opus-20240229",
created_at=datetime.datetime(2024, 2, 29, 0, 0, tzinfo=datetime.UTC),
display_name="Claude Opus 3",
type="model",
),
]
)
with patch(
@@ -204,7 +192,7 @@ def mock_create_stream() -> Generator[AsyncMock]:
id="msg_1234567890ABCDEFGHIJKLMN",
content=[],
role="assistant",
model="claude-3-5-sonnet-20240620",
model=kwargs["model"],
usage=Usage(input_tokens=0, output_tokens=0),
),
type="message_start",

View File

@@ -1,6 +1,10 @@
# serializer version: 1
# name: test_model_list
list([
dict({
'label': 'Claude Sonnet 4.6',
'value': 'claude-sonnet-4-6',
}),
dict({
'label': 'Claude Opus 4.6',
'value': 'claude-opus-4-6',
@@ -29,21 +33,9 @@
'label': 'Claude Sonnet 4',
'value': 'claude-sonnet-4-0',
}),
dict({
'label': 'Claude Sonnet 3.7',
'value': 'claude-3-7-sonnet-latest',
}),
dict({
'label': 'Claude Haiku 3.5',
'value': 'claude-3-5-haiku-20241022',
}),
dict({
'label': 'Claude Haiku 3',
'value': 'claude-3-haiku-20240307',
}),
dict({
'label': 'Claude Opus 3',
'value': 'claude-3-opus-20240229',
}),
])
# ---

View File

@@ -86,7 +86,7 @@
'role': 'assistant',
}),
]),
'model': 'claude-3-7-sonnet-latest',
'model': 'claude-sonnet-4-5',
'stream': True,
'system': list([
dict({

View File

@@ -427,7 +427,7 @@ async def test_model_list_error(
CONF_PROMPT: "Speak like a pirate",
},
{
CONF_CHAT_MODEL: "claude-3-opus",
CONF_CHAT_MODEL: "claude-3-haiku-20240307",
CONF_TEMPERATURE: 1.0,
},
),
@@ -435,7 +435,7 @@ async def test_model_list_error(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_TEMPERATURE: 1.0,
CONF_CHAT_MODEL: "claude-3-opus",
CONF_CHAT_MODEL: "claude-3-haiku-20240307",
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
},
),
@@ -459,7 +459,7 @@ async def test_model_list_error(
CONF_LLM_HASS_API: [],
},
{
CONF_CHAT_MODEL: "claude-3-5-haiku-20241022",
CONF_CHAT_MODEL: "claude-haiku-4-5",
CONF_TEMPERATURE: 1.0,
},
{
@@ -472,8 +472,9 @@ async def test_model_list_error(
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
CONF_TEMPERATURE: 1.0,
CONF_CHAT_MODEL: "claude-3-5-haiku-20241022",
CONF_CHAT_MODEL: "claude-haiku-4-5",
CONF_MAX_TOKENS: DEFAULT[CONF_MAX_TOKENS],
CONF_THINKING_BUDGET: 0,
CONF_WEB_SEARCH: False,
CONF_WEB_SEARCH_MAX_USES: 10,
CONF_WEB_SEARCH_USER_LOCATION: False,

View File

@@ -539,7 +539,7 @@ async def test_extended_thinking(
next(iter(mock_config_entry.subentries.values())),
data={
CONF_LLM_HASS_API: llm.LLM_API_ASSIST,
CONF_CHAT_MODEL: "claude-3-7-sonnet-latest",
CONF_CHAT_MODEL: "claude-sonnet-4-5",
CONF_THINKING_BUDGET: 1500,
},
)

View File

@@ -103,7 +103,7 @@ async def test_downgrade_from_v3_to_v2(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
},
"subentry_id": "mock_id",
"subentry_type": "conversation",
@@ -154,7 +154,7 @@ async def test_migration_from_v1_to_v2(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
}
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
@@ -315,7 +315,7 @@ async def test_migration_from_v1_disabled(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
}
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
@@ -444,7 +444,7 @@ async def test_migration_from_v1_to_v2_with_multiple_keys(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
}
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
@@ -534,7 +534,7 @@ async def test_migration_from_v1_to_v2_with_same_keys(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
}
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
@@ -639,7 +639,7 @@ async def test_migration_from_v2_1_to_v2_2(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
}
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
@@ -901,7 +901,7 @@ async def test_migrate_entry_to_v2_3(
"recommended": True,
"llm_hass_api": ["assist"],
"prompt": "You are a helpful assistant",
"chat_model": "claude-3-haiku-20240307",
"chat_model": "claude-haiku-4-5",
},
"subentry_id": conversation_subentry_id,
"subentry_type": "conversation",

View File

@@ -114,8 +114,8 @@ async def test_repair_flow_iterates_subentries(
model_options: list[dict[str, str]] = [
{"label": "Claude Haiku 4.5", "value": "claude-haiku-4-5"},
{"label": "Claude Sonnet 4.5", "value": "claude-sonnet-4-5"},
{"label": "Claude Opus 4.5", "value": "claude-opus-4-5"},
{"label": "Claude Sonnet 4.6", "value": "claude-sonnet-4-6"},
{"label": "Claude Opus 4.6", "value": "claude-opus-4-6"},
]
with patch(
@@ -152,12 +152,12 @@ async def test_repair_flow_iterates_subentries(
result = await process_repair_fix_flow(
client,
flow_id,
json={CONF_CHAT_MODEL: "claude-sonnet-4-5"},
json={CONF_CHAT_MODEL: "claude-sonnet-4-6"},
)
assert result["type"] == FlowResultType.FORM
assert (
_get_subentry(entry_one, "ai_task_data").data[CONF_CHAT_MODEL]
== "claude-sonnet-4-5"
== "claude-sonnet-4-6"
)
assert (
_get_subentry(entry_one, "conversation").data[CONF_CHAT_MODEL]
@@ -172,12 +172,12 @@ async def test_repair_flow_iterates_subentries(
result = await process_repair_fix_flow(
client,
flow_id,
json={CONF_CHAT_MODEL: "claude-opus-4-5"},
json={CONF_CHAT_MODEL: "claude-opus-4-6"},
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert (
_get_subentry(entry_two, "conversation").data[CONF_CHAT_MODEL]
== "claude-opus-4-5"
== "claude-opus-4-6"
)
assert issue_registry.async_get_issue(DOMAIN, "model_deprecated") is None