Add GPT-6 Astra support (#181693)

This commit is contained in:
Denis Shulyaka
2026-09-09 07:02:52 +02:00
committed by GitHub
parent 4300dc4e40
commit 107da6e03f
5 changed files with 210 additions and 16 deletions
@@ -423,7 +423,7 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow):
elif CONF_REASONING_EFFORT in options:
options.pop(CONF_REASONING_EFFORT)
if model.startswith("gpt-5.6"):
if model.startswith(("gpt-5.6", "gpt-6")):
step_schema.update(
{
vol.Optional(
@@ -435,7 +435,7 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow):
elif CONF_PRO_MODE in options:
options.pop(CONF_PRO_MODE)
if model.startswith("gpt-5"):
if model.startswith(("gpt-5", "gpt-6")):
step_schema.update(
{
vol.Optional(
@@ -453,7 +453,7 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow):
elif CONF_VERBOSITY in options:
options.pop(CONF_VERBOSITY)
if model.startswith(("o", "gpt-5")):
if model.startswith(("o", "gpt-5", "gpt-6")):
reasoning_summary_options = ["off", "auto", "concise", "detailed"]
if model.startswith("o"):
reasoning_summary_options.remove("concise")
@@ -602,10 +602,13 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow):
def _get_reasoning_options(self, model: str) -> list[str]:
"""Get reasoning effort options based on model."""
if not model.startswith(("o", "gpt-5")) or model.startswith("gpt-5-pro"):
if not model.startswith(("o", "gpt-5", "gpt-6")) or model.startswith(
"gpt-5-pro"
):
return []
models_reasoning_map: dict[str | tuple[str, ...], list[str]] = {
"gpt-6": ["low", "medium", "high", "xhigh", "max"],
"gpt-5.6": ["none", "low", "medium", "high", "xhigh", "max"],
("gpt-5.2-pro", "gpt-5.4-pro", "gpt-5.5-pro"): ["medium", "high", "xhigh"],
("gpt-5.2", "gpt-5.3", "gpt-5.4", "gpt-5.5"): [
@@ -523,7 +523,7 @@ class OpenAIBaseLLMEntity(Entity):
stream=True,
)
if model_args["model"].startswith(("o", "gpt-5")):
if model_args["model"].startswith(("o", "gpt-5", "gpt-6")):
reasoning: Reasoning = {
"effort": options.get(
CONF_REASONING_EFFORT, RECOMMENDED_REASONING_EFFORT
@@ -545,7 +545,7 @@ class OpenAIBaseLLMEntity(Entity):
model_args["include"] = ["reasoning.encrypted_content"]
if (
not model_args["model"].startswith("gpt-5")
not model_args["model"].startswith(("gpt-5", "gpt-6"))
or model_args["reasoning"]["effort"] == "none" # type: ignore[index]
):
model_args["top_p"] = options.get(CONF_TOP_P, RECOMMENDED_TOP_P)
@@ -553,7 +553,7 @@ class OpenAIBaseLLMEntity(Entity):
CONF_TEMPERATURE, RECOMMENDED_TEMPERATURE
)
if model_args["model"].startswith("gpt-5"):
if model_args["model"].startswith(("gpt-5", "gpt-6")):
model_args["text"] = {
"verbosity": options.get(CONF_VERBOSITY, RECOMMENDED_VERBOSITY)
}
@@ -561,7 +561,10 @@ class OpenAIBaseLLMEntity(Entity):
if not model_args["model"].startswith(
tuple(UNSUPPORTED_EXTENDED_CACHE_RETENTION_MODELS)
):
model_args["prompt_cache_retention"] = "24h"
if model_args["model"].startswith(("gpt-5.6", "gpt-6")):
model_args["prompt_cache_options"] = {"ttl": "30m"}
else:
model_args["prompt_cache_retention"] = "24h"
tools: list[ToolParam] = []
if chat_log.llm_api:
@@ -298,13 +298,46 @@
])
# ---
# name: test_model_args[subentry_options0]
dict({
'max_output_tokens': 3000,
'model': 'gpt-4o-mini',
'service_tier': 'auto',
'store': False,
'stream': True,
'temperature': 1.0,
'top_p': 1.0,
})
# ---
# name: test_model_args[subentry_options1]
dict({
'include': list([
'reasoning.encrypted_content',
]),
'max_output_tokens': 3000,
'model': 'gpt-5.5',
'prompt_cache_retention': '24h',
'reasoning': dict({
'effort': 'low',
'summary': 'auto',
}),
'service_tier': 'auto',
'store': False,
'stream': True,
'text': dict({
'verbosity': 'medium',
}),
})
# ---
# name: test_model_args[subentry_options2]
dict({
'include': list([
'reasoning.encrypted_content',
]),
'max_output_tokens': 3000,
'model': 'gpt-5.6-sol',
'prompt_cache_retention': '24h',
'prompt_cache_options': dict({
'ttl': '30m',
}),
'reasoning': dict({
'effort': 'low',
'mode': 'pro',
@@ -318,6 +351,96 @@
}),
})
# ---
# name: test_model_args[subentry_options3]
dict({
'include': list([
'reasoning.encrypted_content',
]),
'max_output_tokens': 3000,
'model': 'gpt-5.6-sol',
'prompt_cache_options': dict({
'ttl': '30m',
}),
'reasoning': dict({
'effort': 'none',
'summary': 'auto',
}),
'service_tier': 'auto',
'store': False,
'stream': True,
'temperature': 0.5,
'text': dict({
'verbosity': 'medium',
}),
'top_p': 0.9,
})
# ---
# name: test_model_args[subentry_options4]
dict({
'include': list([
'reasoning.encrypted_content',
]),
'max_output_tokens': 3000,
'model': 'gpt-6-astra',
'prompt_cache_options': dict({
'ttl': '30m',
}),
'reasoning': dict({
'effort': 'low',
'summary': 'auto',
}),
'service_tier': 'auto',
'store': False,
'stream': True,
'text': dict({
'verbosity': 'medium',
}),
})
# ---
# name: test_model_args[subentry_options5]
dict({
'include': list([
'reasoning.encrypted_content',
]),
'max_output_tokens': 3000,
'model': 'gpt-6-astra',
'prompt_cache_options': dict({
'ttl': '30m',
}),
'reasoning': dict({
'effort': 'max',
'mode': 'pro',
'summary': 'detailed',
}),
'service_tier': 'auto',
'store': False,
'stream': True,
'text': dict({
'verbosity': 'low',
}),
})
# ---
# name: test_model_args[subentry_options6]
dict({
'include': list([
'reasoning.encrypted_content',
]),
'max_output_tokens': 3000,
'model': 'gpt-6-astra',
'prompt_cache_options': dict({
'ttl': '30m',
}),
'reasoning': dict({
'effort': 'high',
}),
'service_tier': 'auto',
'store': False,
'stream': True,
'text': dict({
'verbosity': 'high',
}),
})
# ---
# name: test_web_search[False]
list([
dict({
@@ -275,6 +275,7 @@ async def test_subentry_unsupported_model(
("gpt-5.5", ["none", "low", "medium", "high", "xhigh"]),
("gpt-5.5-pro", ["medium", "high", "xhigh"]),
("gpt-5.6", ["none", "low", "medium", "high", "xhigh", "max"]),
("gpt-6-astra", ["low", "medium", "high", "xhigh", "max"]),
],
)
async def test_subentry_reasoning_effort_list(
@@ -327,6 +328,7 @@ async def test_subentry_reasoning_effort_list(
("gpt-5", True),
("gpt-5-mini", True),
("gpt-5-pro", True),
("gpt-6-astra", True),
("gpt-4o", False),
("gpt-4.1", False),
],
@@ -379,6 +381,7 @@ async def test_subentry_reasoning_summary_visibility(
("o4-mini", ["off", "auto", "detailed"]),
("gpt-5", ["off", "auto", "concise", "detailed"]),
("gpt-5-mini", ["off", "auto", "concise", "detailed"]),
("gpt-6-astra", ["off", "auto", "concise", "detailed"]),
],
)
async def test_subentry_reasoning_summary_options(
@@ -1090,15 +1093,46 @@ async def test_form_invalid_auth(hass: HomeAssistant, side_effect, error) -> Non
CONF_WEB_SEARCH_INLINE_CITATIONS: True,
},
),
(
{},
(
{CONF_RECOMMENDED: False},
{CONF_CHAT_MODEL: "gpt-6-astra"},
{
CONF_REASONING_EFFORT: "max",
CONF_REASONING_SUMMARY: "detailed",
CONF_PRO_MODE: True,
CONF_VERBOSITY: "low",
},
),
{
CONF_RECOMMENDED: False,
CONF_CHAT_MODEL: "gpt-6-astra",
CONF_MAX_TOKENS: RECOMMENDED_MAX_TOKENS,
CONF_TOP_P: RECOMMENDED_TOP_P,
CONF_TEMPERATURE: 1.0,
CONF_STORE_RESPONSES: False,
CONF_CODE_INTERPRETER: False,
CONF_REASONING_EFFORT: "max",
CONF_REASONING_SUMMARY: "detailed",
CONF_PRO_MODE: True,
CONF_VERBOSITY: "low",
CONF_SERVICE_TIER: "auto",
CONF_WEB_SEARCH: False,
CONF_WEB_SEARCH_CONTEXT_SIZE: "medium",
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_WEB_SEARCH_INLINE_CITATIONS: False,
},
),
],
)
@pytest.mark.usefixtures("mock_init_component")
async def test_subentry_switching(
hass: HomeAssistant,
mock_config_entry,
mock_init_component,
current_options,
new_options,
expected_options,
mock_config_entry: MockConfigEntry,
current_options: dict[str, str | float | bool | list[str]],
new_options: tuple[dict[str, str | float | bool | list[str]], ...],
expected_options: dict[str, str | float | bool | list[str]],
) -> None:
"""Test the subentry form."""
subentry = next(
@@ -22,9 +22,13 @@ from homeassistant.components.openai_conversation.const import (
CONF_CHAT_MODEL,
CONF_CODE_INTERPRETER,
CONF_PRO_MODE,
CONF_REASONING_EFFORT,
CONF_REASONING_SUMMARY,
CONF_SERVICE_TIER,
CONF_STORE_RESPONSES,
CONF_TEMPERATURE,
CONF_TOP_P,
CONF_VERBOSITY,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_CONTEXT_SIZE,
@@ -821,7 +825,34 @@ async def test_flex_tier_retry(
@pytest.mark.parametrize(
"subentry_options", [{CONF_CHAT_MODEL: "gpt-5.6-sol", CONF_PRO_MODE: True}]
"subentry_options",
[
{CONF_CHAT_MODEL: "gpt-4o-mini"},
{CONF_CHAT_MODEL: "gpt-5.5"},
{CONF_CHAT_MODEL: "gpt-5.6-sol", CONF_PRO_MODE: True},
{
CONF_CHAT_MODEL: "gpt-5.6-sol",
CONF_REASONING_EFFORT: "none",
CONF_TEMPERATURE: 0.5,
CONF_TOP_P: 0.9,
},
{CONF_CHAT_MODEL: "gpt-6-astra"},
{
CONF_CHAT_MODEL: "gpt-6-astra",
CONF_REASONING_EFFORT: "max",
CONF_PRO_MODE: True,
CONF_REASONING_SUMMARY: "detailed",
CONF_VERBOSITY: "low",
CONF_TEMPERATURE: 0.5,
CONF_TOP_P: 0.9,
},
{
CONF_CHAT_MODEL: "gpt-6-astra",
CONF_REASONING_EFFORT: "high",
CONF_REASONING_SUMMARY: "off",
CONF_VERBOSITY: "high",
},
],
)
@pytest.mark.usefixtures("mock_init_component")
async def test_model_args(
@@ -829,7 +860,7 @@ async def test_model_args(
mock_config_entry: MockConfigEntry,
mock_create_stream: AsyncMock,
snapshot: SnapshotAssertion,
subentry_options: dict[str, str | bool],
subentry_options: dict[str, str | bool | float],
) -> None:
"""Test model arguments for various configuration."""