mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
fix(xai): stop sending web_search_options to xAI's retired Live Search path
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
e4ff44f623
commit
b9f3736c20
4 changed files with 47 additions and 7 deletions
|
|
@ -214,10 +214,20 @@ class XAIChatConfig(OpenAIGPTConfig):
|
|||
"""
|
||||
Handle https://github.com/BerriAI/litellm/issues/9720
|
||||
|
||||
Filter out 'name' from messages
|
||||
Filter out 'name' from messages, and drop 'web_search_options': xAI retired Live Search on
|
||||
/v1/chat/completions and now answers those requests with a 410. xAI web search lives on the
|
||||
Responses API, where completion() bridges it to a native 'web_search' tool
|
||||
"""
|
||||
messages = strip_name_from_messages(messages)
|
||||
return super().transform_request(model, messages, optional_params, litellm_params, headers)
|
||||
if "web_search_options" in optional_params:
|
||||
verbose_logger.warning(
|
||||
"XAI no longer supports web search on /chat/completions (Live Search is deprecated). "
|
||||
"Dropping 'web_search_options'. Use the Responses API for XAI web search."
|
||||
)
|
||||
|
||||
chat_params: Final = { # mutable-ok: base transform_request takes a plain dict of optional params
|
||||
key: value for key, value in optional_params.items() if key != "web_search_options"
|
||||
}
|
||||
return super().transform_request(model, strip_name_from_messages(messages), chat_params, litellm_params, headers)
|
||||
|
||||
@staticmethod
|
||||
def _fix_choice_finish_reason_for_tool_calls(choice: Choices) -> None:
|
||||
|
|
|
|||
|
|
@ -1028,10 +1028,6 @@ def responses_api_bridge_check(
|
|||
mode = "responses"
|
||||
model_info["mode"] = mode
|
||||
|
||||
if web_search_options is not None and custom_llm_provider == "xai":
|
||||
model_info["mode"] = "responses"
|
||||
model = model.replace("responses/", "")
|
||||
|
||||
except Exception as e:
|
||||
verbose_logger.debug("Error getting model info: %s", e)
|
||||
|
||||
|
|
@ -1040,6 +1036,11 @@ def responses_api_bridge_check(
|
|||
mode = "responses"
|
||||
model_info["mode"] = mode
|
||||
|
||||
# xAI retired Live Search on /v1/chat/completions (410), so web search only works on /v1/responses
|
||||
if web_search_options is not None and custom_llm_provider == "xai":
|
||||
model_info["mode"] = "responses"
|
||||
model = model.replace("responses/", "")
|
||||
|
||||
# OpenAI/Azure GPT-5 chat-completions that need Responses-only fields (e.g.
|
||||
# ``reasoningSummary`` in ``extra_body``) must be bridged; Chat Completions rejects
|
||||
# those keys.
|
||||
|
|
|
|||
|
|
@ -119,6 +119,24 @@ class TestXAIParallelToolCalls:
|
|||
assert result["messages"][0]["role"] == "user"
|
||||
|
||||
|
||||
class TestXAIChatWebSearchOptions:
|
||||
"""XAI answers /chat/completions requests carrying web_search_options with a 410 (Live Search retired)"""
|
||||
|
||||
def test_transform_request_drops_web_search_options(self):
|
||||
config = XAIChatConfig()
|
||||
|
||||
result = config.transform_request(
|
||||
model="xai/grok-4.6",
|
||||
messages=[{"role": "user", "content": "newest litellm version?"}],
|
||||
optional_params={"web_search_options": {"search_context_size": "medium"}, "temperature": 0.5},
|
||||
litellm_params={},
|
||||
headers={},
|
||||
)
|
||||
|
||||
assert "web_search_options" not in result
|
||||
assert result["temperature"] == 0.5
|
||||
|
||||
|
||||
class TestXAIUsageNormalization:
|
||||
def test_preserves_reasoning_tokens_in_total_usage(self):
|
||||
usage = Usage(prompt_tokens=100, completion_tokens=50, total_tokens=200)
|
||||
|
|
|
|||
|
|
@ -204,6 +204,17 @@ class TestXAIResponsesAutoRouting:
|
|||
assert model_info.get("mode") == "responses"
|
||||
assert updated_model == model
|
||||
|
||||
def test_responses_api_bridge_check_with_web_search_options_on_unmapped_model(self):
|
||||
"""web search must reach /responses even for a model missing from the cost map, chat returns 410"""
|
||||
model_info, updated_model = responses_api_bridge_check(
|
||||
model="grok-not-in-cost-map",
|
||||
custom_llm_provider="xai",
|
||||
web_search_options={"search_context_size": "medium"},
|
||||
)
|
||||
|
||||
assert model_info.get("mode") == "responses"
|
||||
assert updated_model == "grok-not-in-cost-map"
|
||||
|
||||
@patch("litellm.completion_extras.responses_api_bridge.completion")
|
||||
def test_completion_with_tools_routes_to_responses_api(
|
||||
self, mock_responses_completion
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue