fix(websearch): keep the provider prefix on the responses follow-up model too

The responses patch builder still used the old "a slash means already qualified" test, so
a provider-stripped sub-path model reached the follow-up unqualified on that surface. It
now resolves the model the same way the chat completion builder does.
This commit is contained in:
Priyansh Nandwana 2026-10-02 11:22:59 +05:30
parent 68b3789131
commit 577905e022
2 changed files with 51 additions and 5 deletions

View file

@ -36,6 +36,7 @@ from litellm.integrations.websearch_interception.transformation import (
from litellm.litellm_core_utils.agentic_loop_settings import (
validated_max_agentic_loops,
)
from litellm.litellm_core_utils.core_helpers import qualify_provider_stripped_model
from litellm.llms.base_llm.search.transformation import SearchResponse
from litellm.types.integrations.custom_logger import (
CHAT_COMPLETION_AGENTIC_SURFACE,
@ -1242,9 +1243,10 @@ class WebSearchInterceptionLogger(CustomLogger):
}
}
full_model_name = model
if "/" not in model and isinstance(kwargs.get("custom_llm_provider"), str):
full_model_name = f"{kwargs['custom_llm_provider']}/{model}"
custom_llm_provider: Final = kwargs.get("custom_llm_provider")
full_model_name: Final = qualify_provider_stripped_model(
model, custom_llm_provider if isinstance(custom_llm_provider, str) else ""
)
verbose_logger.debug(
"WebSearchInterception: Built responses request patch model=%s input_items=%d searches=%d",
@ -1910,8 +1912,6 @@ class WebSearchInterceptionLogger(CustomLogger):
k: v for k, v in kwargs.items() if not k.startswith("_websearch_interception") and k not in internal_params
}
from litellm.litellm_core_utils.core_helpers import qualify_provider_stripped_model
full_model_name: Final = qualify_provider_stripped_model(model, kwargs.get("custom_llm_provider", ""))
verbose_logger.debug(

View file

@ -252,6 +252,52 @@ async def test_build_responses_plan_produces_responses_input():
assert patch_obj.model == "openai/gpt-4o"
@pytest.mark.asyncio
async def test_build_responses_plan_keeps_the_provider_on_a_sub_path_model():
"""#38829 on the Responses surface: a provider-stripped model whose remainder still holds a
slash was left unqualified, so the follow-up could not resolve a provider."""
logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.BEDROCK])
tools_dict = {
"tool_calls": [
{
"id": "fc_1",
"call_id": "fc_1",
"type": "function_call",
"name": "litellm_web_search",
"arguments": '{"query": "latest ai news"}',
"input": {"query": "latest ai news"},
}
],
"tool_type": "websearch",
"provider": "bedrock",
"response_format": "responses",
}
with patch.object(
logger,
"_execute_search",
new=AsyncMock(return_value=("a search result", None)),
):
plan = await logger.async_build_responses_agentic_loop_plan(
tools=tools_dict,
model="mantle/anthropic.claude-sonnet-5",
messages=[{"role": "user", "content": "What's the latest AI news?"}],
response=_responses_output_with_web_search(),
optional_params={"tools": [{"type": "function", "name": "litellm_web_search"}]},
logging_obj=MagicMock(),
stream=False,
kwargs={
"custom_llm_provider": "bedrock",
"_agentic_loop_api_surface": RESPONSES_AGENTIC_SURFACE,
},
)
patch_obj = plan.request_patch
assert patch_obj is not None
assert patch_obj.model == "bedrock/mantle/anthropic.claude-sonnet-5"
@pytest.mark.asyncio
async def test_deployment_hook_converts_native_responses_web_search_tool():
"""async_pre_call_deployment_hook converts a native Responses web_search tool