From a1ea01c6f17294ec07a6cedf238e651c2e09a709 Mon Sep 17 00:00:00 2001 From: Vrajesh Sulakhe Date: Fri, 2 Oct 2026 20:52:35 +0530 Subject: [PATCH] fix(agentic): preserve provider prefixes for organization models --- .../chat_completion_agentic_loop.py | 2 +- litellm/llms/custom_httpx/llm_http_handler.py | 2 +- .../test_agentic_loop_prefix_44069.py | 82 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 tests/test_litellm/test_agentic_loop_prefix_44069.py diff --git a/litellm/litellm_core_utils/chat_completion_agentic_loop.py b/litellm/litellm_core_utils/chat_completion_agentic_loop.py index 9d7c9864e62..2e842df4718 100644 --- a/litellm/litellm_core_utils/chat_completion_agentic_loop.py +++ b/litellm/litellm_core_utils/chat_completion_agentic_loop.py @@ -171,7 +171,7 @@ async def _execute_chat_completion_agentic_plan( raise ValueError("Agentic loop plan missing patched messages") full_model_name = patch.model or model - if "/" not in full_model_name: + if custom_llm_provider and not full_model_name.startswith(f"{custom_llm_provider}/"): full_model_name = f"{custom_llm_provider}/{full_model_name}" optional_params_for_followup: Final = {**optional_params, **patch.optional_params} diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index dd97db45a88..bfd60bb0f19 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -5494,7 +5494,7 @@ class BaseLLMHTTPHandler: raise ValueError("Agentic loop plan missing patched messages") full_model_name = patch.model or model - if "/" not in full_model_name: + if custom_llm_provider and not full_model_name.startswith(f"{custom_llm_provider}/"): full_model_name = f"{custom_llm_provider}/{full_model_name}" optional_params_for_followup: Final = dict(optional_params) diff --git a/tests/test_litellm/test_agentic_loop_prefix_44069.py b/tests/test_litellm/test_agentic_loop_prefix_44069.py new file mode 100644 index 00000000000..c3c5c7ddc7b --- /dev/null +++ b/tests/test_litellm/test_agentic_loop_prefix_44069.py @@ -0,0 +1,82 @@ +from typing import Final, Literal +from unittest.mock import AsyncMock, patch + +import pytest + +import litellm +from litellm.integrations.custom_logger import CustomLogger +from litellm.litellm_core_utils.chat_completion_agentic_loop import ( + _execute_chat_completion_agentic_plan, +) +from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler +from litellm.types.integrations.custom_logger import ( + AgenticLoopPlan, + AgenticLoopRequestPatch, +) +from litellm.types.utils import ModelResponse + + +@pytest.mark.asyncio +@pytest.mark.parametrize("execution_path", ("http", "sdk")) +@pytest.mark.parametrize("use_patch_model", (False, True), ids=("original-model", "patch-model")) +@pytest.mark.parametrize( + ("model_name", "custom_llm_provider", "expected_model"), + ( + ("zai-org/GLM-5.3-Flash", "hosted_vllm", "hosted_vllm/zai-org/GLM-5.3-Flash"), + ("hosted_vllm/zai-org/GLM-5.3-Flash", "hosted_vllm", "hosted_vllm/zai-org/GLM-5.3-Flash"), + ("hosted_vllm/zai-org/GLM-5.3-Flash", "", "hosted_vllm/zai-org/GLM-5.3-Flash"), + ), + ids=("organization-model", "already-prefixed", "no-provider"), +) +async def test_agentic_followup_preserves_provider_prefix( + execution_path: Literal["http", "sdk"], + use_patch_model: bool, + model_name: str, + custom_llm_provider: str, + expected_model: str, +) -> None: + model: Final = "original-model" if use_patch_model else model_name + plan: Final = AgenticLoopPlan( + run_agentic_loop=True, + request_patch=AgenticLoopRequestPatch( + model=model_name if use_patch_model else None, + messages=[{"role": "user", "content": "Continue"}], + ), + ) + followup: Final = AsyncMock(wraps=litellm.acompletion) + + with patch("litellm.acompletion", new=followup): + response: Final = ( + await BaseLLMHTTPHandler()._execute_chat_completion_agentic_plan( + plan=plan, + model=model, + messages=[], + optional_params={"mock_response": "Follow-up complete"}, + kwargs={"custom_llm_provider": custom_llm_provider}, + custom_llm_provider=custom_llm_provider, + depth=0, + max_loops=3, + fingerprints=[], + fingerprint="followup", + ) + if execution_path == "http" + else await _execute_chat_completion_agentic_plan( + plan=plan, + callback=CustomLogger(), + model=model, + optional_params={"mock_response": "Follow-up complete"}, + kwargs={"custom_llm_provider": custom_llm_provider}, + logging_obj=None, + custom_llm_provider=custom_llm_provider, + depth=0, + max_loops=3, + fingerprints=[], + fingerprint="followup", + ) + ) + + assert isinstance(response, ModelResponse) + assert response.model == expected_model.removeprefix("hosted_vllm/") + followup.assert_awaited_once() + assert followup.await_args is not None + assert followup.await_args.kwargs["model"] == expected_model