From 18e8b31706f5f7b3961d65ebfe61ee546dcb9f6f Mon Sep 17 00:00:00 2001 From: acrogenesis Date: Wed, 13 May 2026 20:35:37 -0600 Subject: [PATCH] feat(xai): allow reasoning_effort on Grok-4 family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xAI now exposes the reasoning_effort parameter on all reasoning-capable Grok-4 models (none / low / medium / high — default "low"): https://docs.x.ai/developers/model-capabilities/text/reasoning The translate_options/3 guard that stripped the parameter for any model whose id contained "grok-4" was written before that landed. Forward reasoning_effort to the API for Grok-4 the same way it already does for Grok-3 mini variants, and update the model-compatibility note + test accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) --- guides/xai.md | 1 + lib/req_llm/providers/xai.ex | 19 ++++++------------- test/providers/xai_test.exs | 8 +++++--- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/guides/xai.md b/guides/xai.md index cd27522be..ff7bfd0a7 100644 --- a/guides/xai.md +++ b/guides/xai.md @@ -88,6 +88,7 @@ Passed via `:provider_options` keyword: - Do NOT support `stop`, `presence_penalty`, or `frequency_penalty` - Use `max_completion_tokens` instead of `max_tokens` - Support native structured outputs +- Support the `reasoning_effort` parameter (`"none"`, `"low"`, `"medium"`, `"high"`) — see [xAI's reasoning docs](https://docs.x.ai/developers/model-capabilities/text/reasoning) ### Grok-3-mini Models - Support `reasoning_effort` parameter (`"low"`, `"medium"`, `"high"`) diff --git a/lib/req_llm/providers/xai.ex b/lib/req_llm/providers/xai.ex index e887867e7..4d480f6fa 100644 --- a/lib/req_llm/providers/xai.ex +++ b/lib/req_llm/providers/xai.ex @@ -37,7 +37,7 @@ defmodule ReqLLM.Providers.XAI do Beyond standard OpenAI parameters, xAI supports: - `max_completion_tokens` - Preferred over max_tokens for Grok-4 models - - `reasoning_effort` - Reasoning level (low, medium, high) for Grok-3 mini models only + - `reasoning_effort` - Reasoning level (low, medium, high, none) for Grok-3 mini and Grok-4 family models - `xai_tools` - Agent tools configuration (e.g., web_search, x_search) - `parallel_tool_calls` - Allow parallel function calls (default: true) - `stream_options` - Streaming configuration (include_usage) @@ -46,7 +46,7 @@ defmodule ReqLLM.Providers.XAI do ## Model Compatibility Notes - Native structured outputs supported on models >= `grok-2-1212` and `grok-2-vision-1212` - - `reasoning_effort` is only supported for grok-3-mini and grok-3-mini-fast models + - `reasoning_effort` is supported for grok-3-mini, grok-3-mini-fast, and Grok-4 family models (see https://docs.x.ai/developers/model-capabilities/text/reasoning) - Grok-4 models do not support `stop`, `presence_penalty`, or `frequency_penalty` - Agent tools (e.g., web_search) incur additional costs per source @@ -713,7 +713,7 @@ defmodule ReqLLM.Providers.XAI do {opts, Enum.reverse(warnings)} end - def translate_options(_operation, model, opts) do + def translate_options(_operation, _model, opts) do warnings = [] {stream_value, opts} = Keyword.pop(opts, :stream?) @@ -775,18 +775,11 @@ defmodule ReqLLM.Providers.XAI do {reasoning_effort, opts} = Keyword.pop(opts, :reasoning_effort) - {opts, warnings} = + opts = if reasoning_effort do - model_name = model.id - - if String.contains?(model_name, "grok-4") do - warning = "reasoning_effort is not supported for Grok-4 models and will be ignored" - {opts, [warning | warnings]} - else - {Keyword.put(opts, :reasoning_effort, reasoning_effort), warnings} - end + Keyword.put(opts, :reasoning_effort, reasoning_effort) else - {opts, warnings} + opts end {opts, Enum.reverse(warnings)} diff --git a/test/providers/xai_test.exs b/test/providers/xai_test.exs index be58b2b30..4a0578dce 100644 --- a/test/providers/xai_test.exs +++ b/test/providers/xai_test.exs @@ -630,12 +630,14 @@ defmodule ReqLLM.Providers.XAITest do assert Keyword.get(translated_opts, :reasoning_effort) == "high" assert warnings == [] + # Grok-4 family now supports reasoning_effort + # (https://docs.x.ai/developers/model-capabilities/text/reasoning) — the + # parameter is forwarded to the API rather than dropped with a warning. {:ok, grok_4} = ReqLLM.model("xai:grok-4") {translated_opts, warnings} = XAI.translate_options(:chat, grok_4, opts) - refute Keyword.has_key?(translated_opts, :reasoning_effort) - assert length(warnings) == 1 - assert hd(warnings) =~ "Grok-4" + assert Keyword.get(translated_opts, :reasoning_effort) == "high" + assert warnings == [] end end