test: migrate wave 1 phase 1 legacy tests to tests/unit

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yuneng 2026-09-20 09:17:57 +00:00
parent 6ef7b86748
commit 03a650c8a9
19 changed files with 14 additions and 229 deletions

View file

@ -1,153 +0,0 @@
"""
Regression test for https://github.com/BerriAI/litellm/issues/28505 -
the Responses API bridge double-strips the provider prefix from the
model name when a Chat Completions request has both `tools` and
`reasoning_effort`.
Root cause: the bridge handler called `litellm.responses()` /
`litellm.aresponses()` without passing the already-resolved
`custom_llm_provider`. The downstream call then re-invoked
`get_llm_provider()` with `custom_llm_provider=None`, which stripped
a second provider prefix from a `provider/provider/model` deployment
string.
This test pins both the sync and async bridge handler call sites:
the resolved `custom_llm_provider` must be forwarded to the underlying
`responses` / `aresponses` call so the provider isn't re-detected.
"""
from unittest.mock import MagicMock, patch
import pytest
from litellm.completion_extras.litellm_responses_transformation.handler import (
ResponsesToCompletionBridgeHandler,
)
def _validated_kwargs():
return {
"model": "openai/openai/openai/gpt-5.5",
"messages": [{"role": "user", "content": "hi"}],
"optional_params": {},
"litellm_params": {},
"headers": {},
"model_response": MagicMock(),
"logging_obj": MagicMock(),
"custom_llm_provider": "openai",
}
def test_sync_completion_forwards_custom_llm_provider():
handler = ResponsesToCompletionBridgeHandler()
handler.transformation_handler = MagicMock()
handler.transformation_handler.transform_request.return_value = {
"model": "openai/openai/openai/gpt-5.5",
"input": [],
# `_build_sanitized_litellm_params` spreads `custom_llm_provider` from
# `litellm_params` into request_data on the real bridge path. Seed
# it here so the test exercises the overwrite (not an explicit kwarg
# that would TypeError against an already-present key).
"custom_llm_provider": "should-be-overwritten",
}
handler.transformation_handler.transform_response.return_value = (
_validated_kwargs()["model_response"]
)
with (
patch.object(
handler, "validate_input_kwargs", return_value=_validated_kwargs()
),
patch(
"litellm.responses",
return_value=MagicMock(spec=[]),
) as mock_responses,
):
# The handler routes ResponsesAPIResponse through transform_response.
# We just want to verify the kwargs going INTO responses().
try:
handler.completion(acompletion=False)
except Exception:
# Downstream handling (transform_response, type checks) is not
# the subject of this test.
pass
assert mock_responses.called
kwargs = mock_responses.call_args.kwargs
assert kwargs.get("custom_llm_provider") == "openai", (
"sync bridge must forward custom_llm_provider to litellm.responses() "
"so the downstream get_llm_provider() call does not re-strip the "
"provider prefix on a provider/provider/model deployment string"
)
@pytest.mark.asyncio
async def test_async_completion_forwards_custom_llm_provider():
handler = ResponsesToCompletionBridgeHandler()
handler.transformation_handler = MagicMock()
handler.transformation_handler.transform_request.return_value = {
"model": "openai/openai/openai/gpt-5.5",
"input": [],
# `_build_sanitized_litellm_params` spreads `custom_llm_provider` from
# `litellm_params` into request_data on the real bridge path. Seed
# it here so the test exercises the overwrite (not an explicit kwarg
# that would TypeError against an already-present key).
"custom_llm_provider": "should-be-overwritten",
}
async def _fake_aresponses(**kwargs):
_fake_aresponses.kwargs = kwargs
return MagicMock(spec=[])
_fake_aresponses.kwargs = {}
with (
patch.object(
handler, "validate_input_kwargs", return_value=_validated_kwargs()
),
patch("litellm.aresponses", _fake_aresponses),
):
try:
await handler.acompletion()
except Exception:
pass
assert _fake_aresponses.kwargs.get("custom_llm_provider") == "openai", (
"async bridge must forward custom_llm_provider to litellm.aresponses() "
"so the downstream get_llm_provider() call does not re-strip the "
"provider prefix on a provider/provider/model deployment string"
)
@pytest.mark.asyncio
async def test_async_completion_forwards_aws_region_name():
handler = ResponsesToCompletionBridgeHandler()
handler.transformation_handler = MagicMock()
handler.transformation_handler.transform_request.return_value = {
"model": "openai.gpt-5.5",
"input": [],
"aws_region_name": "us-east-2",
"api_base": "https://bedrock-mantle.us-east-1.api.aws/v1",
"custom_llm_provider": "bedrock_mantle",
}
async def _fake_aresponses(**kwargs):
_fake_aresponses.kwargs = kwargs
return MagicMock(spec=[])
_fake_aresponses.kwargs = {}
validated = _validated_kwargs()
validated["custom_llm_provider"] = "bedrock_mantle"
validated["litellm_params"] = {
"aws_region_name": "us-east-2",
"api_base": "https://bedrock-mantle.us-east-1.api.aws/v1",
"custom_llm_provider": "bedrock_mantle",
}
with (
patch.object(handler, "validate_input_kwargs", return_value=validated),
patch("litellm.aresponses", _fake_aresponses),
):
try:
await handler.acompletion()
except Exception:
pass
assert _fake_aresponses.kwargs.get("aws_region_name") == "us-east-2"

View file

@ -1,7 +1,6 @@
import asyncio
import json
import time
from pathlib import Path
import httpx
import pytest
@ -571,20 +570,3 @@ def test_config_manager_returns_wxo_provider():
)
assert config is not None
assert config.__class__.__name__ == "WatsonxOrchestrateA2AConfig"
def test_wxo_dashboard_auth_fields():
fields_path = (
Path(__file__).resolve().parents[5]
/ "litellm/proxy/public_endpoints/agent_create_fields.json"
)
agent_fields = json.loads(fields_path.read_text())
wxo_agent = next(
agent for agent in agent_fields if agent["agent_type"] == "watsonx_orchestrate"
)
fields_by_key = {field["key"]: field for field in wxo_agent["credential_fields"]}
assert fields_by_key["auth_mode"]["default_value"] == "cp4d"
# Username is CP4D-only; UI does not require it so ibm_cloud users are not blocked.
assert fields_by_key["username"]["required"] is False
assert "cp4d" in fields_by_key["username"]["tooltip"].lower()

View file

@ -12,17 +12,28 @@ Line shape decides the parse, not the batch's declared endpoint, so an output
file mixing Responses-shaped and chat-shaped lines sums across both.
"""
from typing import Literal, get_args, get_type_hints
import pytest
import litellm
import litellm.batches.batch_utils as bu
from litellm.types.llms.openai import CreateBatchRequest
MODEL = "gpt-5.6"
@pytest.fixture
def local_model_cost_map(monkeypatch):
original_model_cost = litellm.model_cost
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
litellm.model_cost = litellm.get_model_cost_map(url="")
litellm.get_model_info.cache_clear()
try:
yield
finally:
litellm.model_cost = original_model_cost
litellm.get_model_info.cache_clear()
def _responses_line(input_tokens: int, output_tokens: int) -> dict:
return {
"response": {
@ -107,13 +118,3 @@ async def test_mixed_shape_batch_output_sums_across_both_line_shapes(local_model
assert result.cost == pytest.approx(
133 * model_info["input_cost_per_token_batches"] + 107 * model_info["output_cost_per_token_batches"]
)
def test_create_batch_endpoint_accepts_v1_responses():
"""A type-checked caller can pass endpoint="/v1/responses", which the runtime
already forwarded correctly."""
endpoint_annotation = get_type_hints(CreateBatchRequest)["endpoint"]
assert "/v1/responses" in get_args(endpoint_annotation)
for create_fn in (litellm.create_batch, litellm.acreate_batch):
assert "/v1/responses" in get_args(get_type_hints(create_fn)["endpoint"])

View file

@ -1,11 +1,9 @@
import inspect
from collections.abc import Awaitable, Callable, Mapping
from typing import Final, cast # noqa: TID251 # narrows legacy callable signatures for inspect
from typing import Final, cast # noqa: TID251 # narrows legacy callable signatures
import pytest
import litellm
from litellm import main as python_chat
from litellm.chat_completions.dispatch import (
_ADISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
_DISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
@ -40,15 +38,6 @@ def acompletion_binding(native: NativeAcompletion | None) -> NativeBinding[Nativ
return binding
def test_public_signature_is_the_legacy_signature() -> None:
public_completion: Final = cast(Callable[..., object], litellm.completion)
legacy_completion: Final = cast(Callable[..., object], python_chat.completion)
public_acompletion: Final = cast(Callable[..., object], litellm.acompletion)
legacy_acompletion: Final = cast(Callable[..., object], python_chat.acompletion)
assert inspect.signature(public_completion) == inspect.signature(legacy_completion)
assert inspect.signature(public_acompletion) == inspect.signature(legacy_acompletion)
def test_python_route_forwards_original_call_shape() -> None:
metadata: Final = {"user_id": "u"}
args: Final[tuple[object, ...]] = ("gpt-4o", MESSAGES)

View file

@ -3,7 +3,6 @@ Test HeliconeLogger Gemini/Vertex AI support.
Fixes: https://github.com/BerriAI/litellm/issues/19093
"""
import pytest
def test_helicone_gemini_model_in_list():
@ -36,39 +35,6 @@ def test_helicone_gemini_models_recognized():
assert is_recognized, f"{model} should be recognized by helicone_model_list"
def test_helicone_vertex_ai_models_recognized():
"""
Test that Vertex AI models (GLM, DeepSeek, etc.) are recognized via custom_llm_provider.
"""
# Test models that don't contain "gemini" but are vertex_ai
test_models = [
"vertex_ai/zai-org/glm-4.7-maas",
"vertex_ai/deepseek-ai/deepseek-v3",
"vertex_ai/meta/llama-3.1-405b",
]
for model in test_models:
is_vertex_ai = model.startswith("vertex_ai/")
assert is_vertex_ai, f"{model} should be recognized as vertex_ai model"
def test_helicone_vertex_ai_via_custom_llm_provider():
"""
Test that vertex_ai models are recognized when custom_llm_provider is set.
"""
# Models without vertex_ai/ prefix but with custom_llm_provider="vertex_ai"
test_cases = [
("zai-org/glm-4.7-maas", "vertex_ai"),
("deepseek-ai/deepseek-v3", "vertex_ai"),
]
for model, custom_llm_provider in test_cases:
is_vertex_ai = custom_llm_provider == "vertex_ai" or model.startswith(
"vertex_ai/"
)
assert (
is_vertex_ai
), f"{model} with custom_llm_provider={custom_llm_provider} should be recognized as vertex_ai"
def test_helicone_vertex_gemini_gets_vertex_provider_url():
"""
Test that vertex_ai/gemini-* models route to aiplatform.googleapis.com,