feat(code): add Grok 4.5 model (#4596)

Add Grok 4.5 to Deep Agents Code model selection with xAI reasoning
efforts.

Made by [Open
SWE](https://openswe.vercel.app/agents/d182a499-a539-da64-2d66-6b487d6519cf)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Johannes du Plessis
2026-07-09 09:47:47 -07:00
committed by GitHub
parent c6c72139b3
commit b0a209da3a
4 changed files with 116 additions and 6 deletions
+66 -3
View File
@@ -27,7 +27,7 @@ type-checked against this alias, so update them in lockstep when it changes.
"""
ReasoningProvider: TypeAlias = Literal[
"anthropic", "fireworks", "google_genai", "openai", "openai_codex"
"anthropic", "fireworks", "google_genai", "openai", "openai_codex", "xai"
]
"""Provider identifiers that support model-specific reasoning effort controls.
@@ -112,9 +112,16 @@ FIREWORKS_GLM_EFFORTS: tuple[EffortLabel, ...] = ("none", "high", "max")
See https://docs.fireworks.ai/guides/reasoning.
"""
XAI_EFFORTS: tuple[EffortLabel, ...] = ("low", "medium", "high")
"""xAI `reasoning_effort` labels for Grok 4.5.
See https://docs.x.ai/developers/model-capabilities/text/reasoning.
"""
_REASONING_KEYS: frozenset[str] = frozenset(
{
"effort",
"extra_body",
"output_config",
"reasoning",
"reasoning_effort",
@@ -341,6 +348,44 @@ def _fireworks_current_effort(model_params: dict[str, Any]) -> str | None:
return None
def _is_xai_grok_45(model: str) -> bool:
"""Return whether `model` is Grok 4.5 or a documented alias."""
return model in {"grok-4.5", "grok-4.5-latest", "grok-build-latest"}
def _xai_supported_efforts(model: str) -> tuple[EffortLabel, ...]:
"""Return xAI reasoning effort levels for a model."""
return XAI_EFFORTS if _is_xai_grok_45(model) else ()
def _xai_default_effort(model: str) -> EffortLabel | None:
"""Return the xAI default reasoning effort when known."""
return "high" if _is_xai_grok_45(model) else None
def _xai_model_params(effort: str) -> dict[str, Any]:
"""Return xAI reasoning params for an effort label."""
return {"extra_body": {"reasoning_effort": effort}}
def _xai_current_effort(model_params: dict[str, Any]) -> str | None:
"""Read the xAI reasoning effort from model params.
Returns:
The configured effort label, or `None` when unset.
"""
extra = model_params.get("extra_body")
if isinstance(extra, dict):
value = extra.get("reasoning_effort")
if value is not None and not isinstance(value, str):
logger.warning(
"Ignoring non-str xAI extra_body.reasoning_effort of type %s",
type(value).__name__,
)
return value if isinstance(value, str) else None
return None
_OPENAI_CONFIG = ReasoningProviderConfig(
supported_efforts=_openai_supported_efforts,
default_effort=_openai_default_effort,
@@ -374,6 +419,12 @@ _PROVIDER_CONFIGS: dict[ReasoningProvider, ReasoningProviderConfig] = {
model_params=_fireworks_model_params,
current_effort=_fireworks_current_effort,
),
"xai": ReasoningProviderConfig(
supported_efforts=_xai_supported_efforts,
default_effort=_xai_default_effort,
model_params=_xai_model_params,
current_effort=_xai_current_effort,
),
}
"""Provider-specific reasoning effort behavior keyed by `ModelSpec` provider."""
@@ -405,6 +456,8 @@ def _classify_reasoning_provider(provider: str, model: str) -> ReasoningProvider
return "google_genai"
if provider == "fireworks" and model_lower.startswith("accounts/fireworks/models/"):
return "fireworks"
if provider == "xai" and _is_xai_grok_45(model_lower):
return "xai"
return None
@@ -526,7 +579,9 @@ def merge_effort_model_params(
"""
merged = dict(existing) if existing else {}
for key, value in effort_params.items():
if key in {"model_kwargs", "output_config"} and isinstance(value, dict):
if key in {"extra_body", "model_kwargs", "output_config"} and isinstance(
value, dict
):
current = merged.get(key)
base = dict(current) if isinstance(current, dict) else {}
base.update(value)
@@ -556,7 +611,8 @@ def without_effort_model_params(
cleaned = {
key: (dict(value) if isinstance(value, dict) else value)
for key, value in existing.items()
if key not in _REASONING_KEYS and key not in {"model_kwargs", "output_config"}
if key not in _REASONING_KEYS
and key not in {"extra_body", "model_kwargs", "output_config"}
}
kwargs = existing.get("model_kwargs")
if isinstance(kwargs, dict):
@@ -572,4 +628,11 @@ def without_effort_model_params(
cleaned["output_config"] = output_config_params
elif output_config is not None:
cleaned["output_config"] = output_config
extra = existing.get("extra_body")
if isinstance(extra, dict):
extra_params = {k: v for k, v in extra.items() if k != "reasoning_effort"}
if extra_params:
cleaned["extra_body"] = extra_params
elif extra is not None:
cleaned["extra_body"] = extra
return cleaned or None
@@ -114,6 +114,7 @@ _RECOMMENDED_MODELS: dict[str, str] = {
"openrouter:openrouter/fusion": "OpenRouter Fusion",
"openrouter:qwen/qwen3.7-plus": "Qwen 3.7 Plus",
"openrouter:z-ai/glm-5.2": "GLM 5.2",
"xai:grok-4.5": "Grok 4.5",
}
"""Hand-curated frontier-tier models promoted across the UI, mapped to a
human-readable display name.
@@ -65,6 +65,9 @@ def _restore_settings() -> Iterator[None]:
("anthropic:claude-opus-4-16", ("low", "medium", "high", "xhigh", "max")),
("google_genai:gemini-3.5-flash", ("low", "medium", "high")),
("google_genai:gemini-3.1-pro-preview", ("low", "medium", "high")),
("xai:grok-4.5", ("low", "medium", "high")),
("xai:grok-4.5-latest", ("low", "medium", "high")),
("xai:grok-build-latest", ("low", "medium", "high")),
(
"fireworks:accounts/fireworks/models/deepseek-v4-pro",
("none", "low", "medium", "high", "xhigh", "max"),
@@ -81,6 +84,7 @@ def _restore_settings() -> Iterator[None]:
("openai_codex:gpt-4o", ()),
("anthropic:claude-3-5-haiku-latest", ()),
("google_genai:gemini-2.5-flash", ()),
("xai:grok-4", ()),
("fireworks:accounts/fireworks/models/llama-v3p1-70b-instruct", ()),
],
)
@@ -104,6 +108,7 @@ def test_supported_efforts_for_model(model_spec: str, efforts: tuple[str, ...])
("google_genai:gemini-3.1-pro-preview", "high"),
("google_genai:gemini-3-pro", "high"),
("google_genai:gemini-3-flash", "high"),
("xai:grok-4.5", "high"),
("fireworks:accounts/fireworks/models/deepseek-v4-pro", "high"),
("fireworks:accounts/fireworks/models/glm-5p2", "max"),
("fireworks:accounts/fireworks/models/kimi-k2p7-code", None),
@@ -128,6 +133,9 @@ def test_model_params_for_effort_maps_provider_kwargs() -> None:
assert model_params_for_effort(
"fireworks:accounts/fireworks/models/deepseek-v4-pro", "max"
) == {"model_kwargs": {"reasoning_effort": "max"}}
assert model_params_for_effort("xai:grok-4.5", "medium") == {
"extra_body": {"reasoning_effort": "medium"}
}
def test_current_effort_reads_anthropic_output_config() -> None:
@@ -139,6 +147,15 @@ def test_current_effort_reads_anthropic_output_config() -> None:
)
def test_current_effort_reads_xai_extra_body() -> None:
assert (
current_effort_from_model_params(
"xai:grok-4.5", {"extra_body": {"reasoning_effort": "high"}}
)
== "high"
)
def test_model_params_for_effort_rejects_unsupported_effort() -> None:
assert (
model_params_for_effort(
@@ -171,6 +188,21 @@ def test_merge_and_clear_effort_model_params_preserves_unrelated_params() -> Non
}
def test_merge_and_clear_xai_effort_preserves_extra_body_params() -> None:
merged = merge_effort_model_params(
{"extra_body": {"prompt_cache_key": "thread-1"}},
{"extra_body": {"reasoning_effort": "high"}},
)
assert merged == {
"extra_body": {"prompt_cache_key": "thread-1", "reasoning_effort": "high"}
}
assert current_effort_from_model_params("xai:grok-4.5", merged) == "high"
assert without_effort_model_params(merged) == {
"extra_body": {"prompt_cache_key": "thread-1"}
}
@pytest.mark.parametrize(
("model_spec", "model_params"),
[
@@ -185,6 +217,7 @@ def test_merge_and_clear_effort_model_params_preserves_unrelated_params() -> Non
"fireworks:accounts/fireworks/models/deepseek-v4-pro",
{"model_kwargs": {"reasoning_effort": 5}},
),
("xai:grok-4.5", {"extra_body": {"reasoning_effort": 5}}),
],
)
def test_current_effort_warns_on_malformed_params(
@@ -215,6 +248,13 @@ def test_current_effort_non_dict_model_kwargs_is_silent() -> None:
)
def test_current_effort_non_dict_extra_body_is_silent() -> None:
"""A non-dict `extra_body` is a legit shape and must not warn."""
assert (
current_effort_from_model_params("xai:grok-4.5", {"extra_body": "raw"}) is None
)
def test_effort_argument_hint_covers_effort_vocabulary() -> None:
"""The `/effort` argument hint must list every `EffortLabel` plus a reset.
@@ -2251,15 +2251,21 @@ class TestGetModelDisplayName:
== "claude-sonnet-4-5"
)
def test_uses_recommended_name_when_no_profile(self) -> None:
@pytest.mark.parametrize(
("spec", "name"),
[
("fireworks:accounts/fireworks/models/kimi-k2p7-code", "Kimi K2.7 Code"),
("xai:grok-4.5", "Grok 4.5"),
],
)
def test_uses_recommended_name_when_no_profile(self, spec: str, name: str) -> None:
"""Uninstalled recommendations use the hardcoded name, not the raw id."""
from deepagents_code.tui.widgets import model_selector
screen = ModelSelectorScreen.__new__(ModelSelectorScreen)
screen._profiles = {}
spec = "fireworks:accounts/fireworks/models/kimi-k2p7-code"
assert spec in model_selector._RECOMMENDED_MODELS
assert screen._get_model_display_name(spec) == "Kimi K2.7 Code"
assert screen._get_model_display_name(spec) == name
def test_profile_name_wins_over_recommended_name(self) -> None:
"""A loaded profile's `name` takes precedence over the hardcoded one."""