mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-25 05:54:53 +00:00
* test(rust-bridge): pin Messages and Responses callback contracts on the native path Each test runs the native route and asserts what the Python path does today, so a known gap is a strict xfail that names its reason and the exception it fails with Messages and Responses share one Surface and hook recorder in support/callbacks.py, and each surface folder lists its own expected counts and gaps. Body-level hooks from the function body (pre-request hook, websearch short-circuit, Messages interceptors, planned and legacy agentic loops, streaming deployment hook) are xfail until native runs them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(rust-bridge): give every route folder the same one-concern-per-file layout Route data, the route Surface and the server fixture move into each route folder, and support/ keeps only route-neutral helpers. test_inference.py splits into chat_completions/ and responses/, ocr/test_requests.py splits into requests, failures and settings, and Messages and Responses get streaming and body hook files. Messages request tests that duplicate inference-messages request.rs are removed, and drop_params read from litellm.drop_params is now covered Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(rust-bridge): expect the required-route decline error for unsupported Responses requests Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): import route data from the route folders in the secrets unit tests Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): import NoReturn in the OCR dispatch tests Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): move route-neutral helpers out of support/callbacks.py Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): cover deployment rejection and concurrent callbacks * test(rust-bridge): assert native serving via the x-litellm-rust marker in the route folder tests Co-Authored-By: Yujong Lee <yujong@berri.ai> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
83 lines
3.5 KiB
Python
83 lines
3.5 KiB
Python
from typing import Final
|
|
|
|
import pytest
|
|
from pydantic import TypeAdapter
|
|
|
|
import litellm
|
|
from litellm.integrations.custom_logger import CustomLogger
|
|
from litellm.models.credentials import CredentialItem
|
|
from litellm.types.llms.openai import ResponsesAPIResponse
|
|
from litellm.types.utils import CallTypes
|
|
from tests.test_litellm_rust.responses.data import call
|
|
from tests.test_litellm_rust.support.callback_recorder import RecordingLogger
|
|
from tests.test_litellm_rust.support.recording_server import RecordingServer
|
|
from tests.test_litellm_rust.support.routing import assert_served_natively
|
|
|
|
pytestmark = pytest.mark.requires_rust_extension
|
|
|
|
_OBJECT: Final = TypeAdapter(dict[str, object])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize("from_credentials", (False, True))
|
|
async def test_resource_setup_uses_deployment_hook_arguments(
|
|
server: RecordingServer, monkeypatch: pytest.MonkeyPatch, from_credentials: bool
|
|
) -> None:
|
|
invalid_settings: Final = {"ssl_verify": object()}
|
|
credential: Final = CredentialItem(
|
|
credential_name="resource-settings", credential_info={}, credential_values=invalid_settings
|
|
)
|
|
monkeypatch.setattr(litellm, "credential_list", [credential])
|
|
|
|
class Prepare(CustomLogger):
|
|
async def async_pre_call_deployment_hook(
|
|
self, kwargs: dict[str, object], call_type: CallTypes | None
|
|
) -> dict[str, object]:
|
|
return {
|
|
**kwargs,
|
|
**({"litellm_credential_name": credential.credential_name} if from_credentials else invalid_settings),
|
|
}
|
|
|
|
litellm.callbacks.append(Prepare())
|
|
recorder: Final = RecordingLogger()
|
|
server.expected_requests = 0
|
|
|
|
with pytest.raises(ValueError, match=r"request\.ssl_verify") as caught:
|
|
await call(server, callbacks=[recorder])
|
|
|
|
failure: Final = await recorder.wait_for_async("async_log_failure_event")
|
|
assert len(failure) == 1
|
|
assert _OBJECT.validate_python(failure[0].kwargs)["exception"] is caught.value
|
|
assert not server.requests
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize("asynchronous", (False, True))
|
|
@pytest.mark.parametrize("source", ("explicit", "base_url", "global", "provider", "environment", "empty"))
|
|
async def test_connection_settings_reach_the_provider(
|
|
server: RecordingServer, monkeypatch: pytest.MonkeyPatch, asynchronous: bool, source: str
|
|
) -> None:
|
|
key: Final = "selected-key"
|
|
explicit: Final = source in ("explicit", "base_url")
|
|
monkeypatch.setattr(litellm, "api_key", key if source in ("global", "empty") else ("unused" if explicit else None))
|
|
monkeypatch.setattr(litellm, "openai_key", key if source == "provider" else ("unused" if explicit else None))
|
|
monkeypatch.setattr(
|
|
litellm,
|
|
"api_base",
|
|
None if source == "environment" else ("http://127.0.0.1:1" if explicit else server.base_url),
|
|
)
|
|
monkeypatch.setenv("OPENAI_API_KEY", key if source == "environment" else "unused")
|
|
for name in ("OPENAI_BASE_URL", "OPENAI_API_BASE"):
|
|
monkeypatch.setenv(name, server.base_url if source == "environment" else "http://127.0.0.1:1")
|
|
|
|
result: Final = await call(
|
|
server,
|
|
asynchronous=asynchronous,
|
|
api_key=key if explicit else ("" if source == "empty" else None),
|
|
api_base=server.base_url if source == "explicit" else ("" if source == "empty" else None),
|
|
**({"base_url": server.base_url} if source == "base_url" else {}),
|
|
)
|
|
|
|
assert isinstance(result, ResponsesAPIResponse)
|
|
assert_served_natively(server)
|
|
assert server.requests[0].headers["authorization"] == f"Bearer {key}"
|