This commit is contained in:
devin-ai-integration[bot] 2026-09-05 06:45:02 +08:00 committed by GitHub
commit bafd94ddf2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 10 deletions

View file

@ -219,13 +219,19 @@ class XAIChatConfig(OpenAIGPTConfig):
litellm_params: dict,
headers: dict,
) -> dict:
"""
Handle https://github.com/BerriAI/litellm/issues/9720
"""Handle https://github.com/BerriAI/litellm/issues/9720"""
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."
)
Filter out 'name' from messages
"""
messages = strip_name_from_messages(messages)
return super().transform_request(model, messages, optional_params, litellm_params, headers)
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:

View file

@ -1036,10 +1036,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)
@ -1048,6 +1044,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.

View file

@ -124,6 +124,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)

View file

@ -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