litellm/tests/unit/embeddings/test_dispatch.py
yujonglee 1bc85fad40
refactor(python-bridge): ship a signature base and read the resolved call (#45450)
* refactor(python-bridge): ship a signature base and read the resolved call

NativeCall carries base (positionals by name plus signature defaults) instead
of the fully bound dict. resolved lays kwargs over base, which is what bound
held, so every pre-hook read and every route host keeps seeing the same values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(bedrock): build the transcription NativeCall with an empty base

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(dispatch): read the resolved call instead of bound

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(host-python): rename effective to effective_py_args and note the shallow copy

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-08 17:20:29 -07:00

87 lines
3.3 KiB
Python

from __future__ import annotations
from collections.abc import Awaitable, Callable
from typing import Final
import pytest
from pydantic import TypeAdapter
import litellm
from litellm.embeddings import dispatch
from litellm.rust_bridge.bindings import NativeBinding
from litellm.rust_bridge.catalog import Route, RouteRule, Rules
from litellm.rust_bridge.configuration import Rollout
from litellm.rust_bridge.public_call import NativeCall, native_call_hook
from litellm.types.utils import EmbeddingResponse
@pytest.mark.asyncio
async def test_public_embedding_calls_keep_the_python_result() -> None:
vector: Final = [0.1, 0.2]
sync_response: Final = litellm.embedding(model="openai/test-model", input="hello", mock_response=vector)
async_response: Final = await litellm.aembedding(model="openai/test-model", input="hello", mock_response=vector)
assert isinstance(sync_response, EmbeddingResponse)
rows: Final = TypeAdapter(list[dict[str, object]])
assert rows.validate_python(sync_response.model_dump()["data"])[0]["embedding"] == vector
assert rows.validate_python(async_response.model_dump()["data"])[0]["embedding"] == vector
def test_sync_embedding_request_projects_public_arguments() -> None:
rules: Final[Rules] = (RouteRule(Route.EMBEDDINGS, Rollout.RUST_REQUIRED),)
expected: Final = EmbeddingResponse(model="test-model", data=[])
def native(request: NativeCall) -> EmbeddingResponse:
assert request.resolved["model"] == "test-model"
assert request.resolved["input"] == "hello"
assert request.resolved["custom_llm_provider"] == "openai"
return expected
binding: Final[NativeBinding[Callable[[NativeCall], EmbeddingResponse]]] = NativeBinding(
"embedding", validate=lambda _: None
)
binding.override(native)
response: Final = dispatch._DISPATCH.run( # pyright: ignore[reportPrivateUsage] # test an explicit route decision
("test-model", "hello"),
{"custom_llm_provider": "openai", "dimensions": 8},
python=lambda *args, **kwargs: pytest.fail("required native route must handle this call"),
binding=binding,
native=native_call_hook,
rules=rules,
)
assert response is expected
@pytest.mark.asyncio
async def test_async_embedding_falls_back_after_native_declines() -> None:
from litellm.rust_bridge.bindings import native_exception_types
native_types: Final = native_exception_types()
if native_types is None:
pytest.skip("native bridge is unavailable")
declined, _ = native_types
expected: Final = EmbeddingResponse(model="test-model", data=[])
rules: Final[Rules] = (RouteRule(Route.EMBEDDINGS, Rollout.RUST_OPT_OUT),)
async def native(request: NativeCall) -> EmbeddingResponse:
raise declined("unsupported")
async def python(*args: object, **kwargs: object) -> EmbeddingResponse:
return expected
binding: Final[NativeBinding[Callable[[NativeCall], Awaitable[EmbeddingResponse]]]] = NativeBinding(
"aembedding", validate=lambda _: None
)
binding.override(native)
response: Final = await dispatch._ADISPATCH.arun( # pyright: ignore[reportPrivateUsage] # test an explicit route decision
("test-model", "hello"),
{},
python=python,
binding=binding,
native=native_call_hook,
rules=rules,
)
assert response is expected