mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-09 22:31:41 +00:00
test(native): cover shared dispatch contracts
This commit is contained in:
parent
c363987dad
commit
77bf16b847
2 changed files with 98 additions and 2 deletions
|
|
@ -11,8 +11,8 @@ import pytest
|
|||
|
||||
import litellm
|
||||
from litellm.llms.base_llm.ocr.transformation import OCRResponse
|
||||
from litellm.rust_bridge.timeouts import timeout_to_seconds
|
||||
from litellm.rust_bridge import configuration
|
||||
from litellm.rust_bridge.timeouts import timeout_to_seconds
|
||||
|
||||
# `litellm/__init__.py` does `from .ocr.main import *`, which binds the `ocr`
|
||||
# function onto `litellm.ocr` and shadows the submodule, so import the modules
|
||||
|
|
@ -864,6 +864,25 @@ async def test_ocr_fallback_skips_native_preparation(
|
|||
fallback.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_aocr_rejects_empty_python_fallback_response(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fake_exception_type(**kwargs: object) -> CapturedException:
|
||||
captured.update(kwargs)
|
||||
return CapturedException("wrapped")
|
||||
|
||||
monkeypatch.setattr(ocr_main.litellm, "exception_type", fake_exception_type)
|
||||
monkeypatch.setattr(ocr_main.base_llm_http_handler, "ocr", AsyncMock(return_value=None))
|
||||
|
||||
with pytest.raises(CapturedException, match="wrapped"):
|
||||
await litellm.aocr(model=MODEL, document=DOCUMENT, api_key="sk-test")
|
||||
|
||||
original: Final = captured["original_exception"]
|
||||
assert isinstance(original, ValueError)
|
||||
assert str(original) == "Got an unexpected None response from the OCR API: None"
|
||||
|
||||
|
||||
def test_ocr_provider_configs_expose_api_key_env_vars():
|
||||
from litellm.llms.azure_ai.ocr.document_intelligence.transformation import (
|
||||
AzureDocumentIntelligenceOCRConfig,
|
||||
|
|
|
|||
|
|
@ -292,6 +292,19 @@ def test_require_explains_why_rust_did_not_handle_request(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_arequire_explains_unavailable_native_binding() -> None:
|
||||
endpoint: Final = runtime.EndpointBinding(route="messages", load=lambda: None, enabled=enabled)
|
||||
|
||||
with pytest.raises(RuntimeError, match=r"^native messages endpoint is unavailable$"):
|
||||
await endpoint.arequire(
|
||||
prepare=lambda: pytest.fail("must not prepare"),
|
||||
call=lambda _binding, _request: pytest.fail("must not invoke"),
|
||||
adapt=str,
|
||||
error_context=context(),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("state", "expected", "expected_events"),
|
||||
(
|
||||
|
|
@ -358,6 +371,68 @@ def test_native_endpoint_applies_partial_overrides_and_reset(monkeypatch: pytest
|
|||
assert endpoint.asynchronous.load() is native_async
|
||||
|
||||
|
||||
def test_direct_endpoint_binding_rejects_native_state_controls() -> None:
|
||||
endpoint: Final = runtime.EndpointBinding(route="test", load=object, enabled=enabled)
|
||||
|
||||
with pytest.raises(RuntimeError, match="only native Rust bridges support binding overrides"):
|
||||
endpoint.override(object())
|
||||
with pytest.raises(RuntimeError, match="only native Rust bridges support binding resets"):
|
||||
endpoint.reset()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("enabled_state", "reason", "expected"),
|
||||
(
|
||||
pytest.param(False, None, runtime.PythonFallbackReason.NATIVE_DISABLED, id="disabled"),
|
||||
pytest.param(True, "unsupported model", runtime.PythonFallbackReason.NATIVE_DECLINED, id="declined"),
|
||||
pytest.param(True, None, None, id="accepted"),
|
||||
),
|
||||
)
|
||||
def test_assess_reports_binding_eligibility(
|
||||
enabled_state: bool,
|
||||
reason: str | None,
|
||||
expected: runtime.PythonFallbackReason | None,
|
||||
) -> None:
|
||||
binding: Final = object()
|
||||
checked: list[object] = []
|
||||
endpoint: Final = runtime.EndpointBinding(route="test", load=lambda: binding, enabled=lambda: enabled_state)
|
||||
|
||||
result: Final = endpoint.assess(check=lambda value: checked.append(value) or reason)
|
||||
|
||||
assert (result.reason if result is not None else None) is expected
|
||||
assert (result.detail if result is not None else None) == reason
|
||||
assert checked == ([binding] if enabled_state else [])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("asynchronous", (False, True))
|
||||
async def test_dispatch_require_returns_adapted_native_success_without_exception_metadata(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
asynchronous: bool,
|
||||
) -> None:
|
||||
monkeypatch.setattr(bindings, "get_native_bridge", lambda: None)
|
||||
endpoint: Final = runtime.EndpointDispatch(
|
||||
sync=runtime.EndpointBinding(route="test", load=object, enabled=enabled),
|
||||
asynchronous=runtime.EndpointBinding(route="test", load=object, enabled=enabled),
|
||||
)
|
||||
|
||||
async def acall(_binding: object, request: int) -> int:
|
||||
return request * 2
|
||||
|
||||
result: Final = (
|
||||
await endpoint.arequire(prepare=lambda: 3, call=acall, adapt=str, error_context=context())
|
||||
if asynchronous
|
||||
else endpoint.require(
|
||||
prepare=lambda: 3,
|
||||
call=lambda _binding, request: request * 2,
|
||||
adapt=str,
|
||||
error_context=context(),
|
||||
)
|
||||
)
|
||||
|
||||
assert result == "6"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("asynchronous", (False, True))
|
||||
async def test_response_adaptation_failure_never_authorizes_fallback(asynchronous: bool) -> None:
|
||||
|
|
@ -479,7 +554,9 @@ async def test_preflight_runs_after_binding_selection_before_preparation(
|
|||
assert events == (
|
||||
["load", "preflight", "prepare", "native"]
|
||||
if available and accepted
|
||||
else ["load", "preflight", "python"] if available else ["load", "python"]
|
||||
else ["load", "preflight", "python"]
|
||||
if available
|
||||
else ["load", "python"]
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue