mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-16 04:29:31 +00:00
* refactor(rust-bridge): return an unstarted Execution from native entrypoints and drive it in Python Rust's run_call built Python streams itself and imported litellm.rust_bridge.streams by name, so sync calls were driven in Rust while async calls came back as a Rust-made coroutine. Every native inference entrypoint now returns one unstarted Execution and public_execution.py drives it with lifecycle.run or lifecycle.drive, wrapping streams and _hidden_params on the Python side. Rust only imports the lifecycle step classes through StepTypes, and python-bridge/src/lifecycle.rs, PythonLifecycle and streams.py are gone. The adapter trusts the _native.pyi contract with a single cast instead of a per-request runtime_checkable isinstance. Binding tests assert the adapter starts the execution and returns its completed value rather than checking callable identity. * refactor(rust-bridge): fold lifecycle and public_execution into one execution module The public stream wrappers only forwarded to the lifecycle streams and added the header dict, so the streams now carry _hidden_params themselves. lifecycle.py no longer held any call lifecycle, just the Execution driver, so it is renamed to execution.py and absorbs the synchronous and asynchronous adapters Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(rust-bridge): keep execution.py free of third-party imports The host-python tests load execution.py into a bare interpreter, so the pydantic header validation broke them. The stream head comes from the binding's encode_stream_head, which always builds a str-keyed dict Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(host-python): preserve pre-async hook coverage after execution handoff * style(rust-bridge): sort litellm_host_python imports Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): drive unstarted native calls through execution in the split lifecycle tests Co-Authored-By: Yujong Lee <yujong@berri.ai> * fix(rust-bridge): mark execution TypeVar as Final Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): cover stream close, read failure, and sync suspension in execution 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>
145 lines
5.2 KiB
Python
145 lines
5.2 KiB
Python
import asyncio
|
|
from collections.abc import Coroutine
|
|
from dataclasses import dataclass
|
|
from types import SimpleNamespace
|
|
from typing import Final, cast # noqa: TID251 # narrows the async binding's untyped coroutine
|
|
|
|
import pytest
|
|
|
|
from litellm.rust_bridge import bindings
|
|
from litellm.rust_bridge.chat_completions import entrypoints as chat_completions
|
|
from litellm.rust_bridge.embeddings import entrypoints as embeddings
|
|
from litellm.rust_bridge.execution import Complete, Step
|
|
from litellm.rust_bridge.messages import entrypoints as messages
|
|
from litellm.rust_bridge.ocr import entrypoints as ocr
|
|
from litellm.rust_bridge.public_call import NativeCall
|
|
from litellm.rust_bridge.responses import entrypoints as responses
|
|
from litellm.rust_bridge.transcription import native as transcription
|
|
|
|
|
|
def test_binding_distinguishes_disable_from_reset(monkeypatch) -> None:
|
|
native = SimpleNamespace(route=lambda: "native")
|
|
monkeypatch.setattr(bindings, "get_native_bridge", lambda: native)
|
|
binding: bindings.NativeBinding[object] = bindings.NativeBinding("route", validate=lambda value: value)
|
|
|
|
assert binding.load() is native.route
|
|
|
|
binding.override(None)
|
|
assert binding.load() is None
|
|
|
|
replacement = object()
|
|
binding.override(replacement)
|
|
assert binding.load() is replacement
|
|
|
|
binding.reset()
|
|
assert binding.load() is native.route
|
|
|
|
|
|
@pytest.mark.parametrize(("value", "expected"), ((3, 3), ("invalid", None), (None, None)))
|
|
def test_binding_validates_native_attribute(
|
|
monkeypatch: pytest.MonkeyPatch, value: object, expected: int | None
|
|
) -> None:
|
|
native: Final = SimpleNamespace(route=value)
|
|
monkeypatch.setattr(bindings, "get_native_bridge", lambda: native)
|
|
binding: Final = bindings.NativeBinding("route", validate=lambda item: item if isinstance(item, int) else None)
|
|
|
|
assert binding.load() == expected
|
|
|
|
|
|
EXECUTION_BINDINGS: Final = (
|
|
("completion", chat_completions.NATIVE_COMPLETION, False),
|
|
("acompletion", chat_completions.NATIVE_ACOMPLETION, True),
|
|
("embedding", embeddings.NATIVE_EMBEDDING, False),
|
|
("aembedding", embeddings.NATIVE_AEMBEDDING, True),
|
|
("messages", messages.NATIVE_MESSAGES, False),
|
|
("amessages", messages.NATIVE_AMESSAGES, True),
|
|
("responses", responses.NATIVE_RESPONSES, False),
|
|
("aresponses", responses.NATIVE_ARESPONSES, True),
|
|
("ocr", ocr.NATIVE_OCR, False),
|
|
("aocr", ocr.NATIVE_AOCR, True),
|
|
)
|
|
|
|
TRANSCRIPTION_BINDINGS: Final = (
|
|
("transcription", transcription.NATIVE_TRANSCRIPTION),
|
|
("atranscription", transcription.NATIVE_ATRANSCRIPTION),
|
|
)
|
|
|
|
ROUTE_BINDINGS: Final = tuple((attribute, binding) for attribute, binding, _ in EXECUTION_BINDINGS) + (
|
|
TRANSCRIPTION_BINDINGS
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("attribute", "route_binding"), ROUTE_BINDINGS, ids=[attribute for attribute, _ in ROUTE_BINDINGS]
|
|
)
|
|
def test_route_bindings_reject_non_callable_native_attributes(
|
|
monkeypatch: pytest.MonkeyPatch, attribute: str, route_binding: bindings.NativeBinding[object]
|
|
) -> None:
|
|
monkeypatch.setattr(bindings, "get_native_bridge", lambda: SimpleNamespace(**{attribute: "not callable"}))
|
|
route_binding.reset()
|
|
assert route_binding.load() is None
|
|
route_binding.reset()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("attribute", "route_binding"), TRANSCRIPTION_BINDINGS, ids=[attribute for attribute, _ in TRANSCRIPTION_BINDINGS]
|
|
)
|
|
def test_transcription_bindings_expose_the_native_callable_itself(
|
|
monkeypatch: pytest.MonkeyPatch, attribute: str, route_binding: bindings.NativeBinding[object]
|
|
) -> None:
|
|
def native_route() -> None:
|
|
pass
|
|
|
|
monkeypatch.setattr(bindings, "get_native_bridge", lambda: SimpleNamespace(**{attribute: native_route}))
|
|
route_binding.reset()
|
|
assert route_binding.load() is native_route
|
|
route_binding.reset()
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class _EchoingExecution:
|
|
"""Completes on its first resume with the value it was resumed with."""
|
|
|
|
def send(self, value: object) -> Step:
|
|
return Complete(("completed with", value))
|
|
|
|
def throw(self, error: BaseException) -> Step:
|
|
raise error
|
|
|
|
def close(self) -> None:
|
|
pass
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("attribute", "route_binding", "asynchronous"),
|
|
EXECUTION_BINDINGS,
|
|
ids=[attribute for attribute, _, _ in EXECUTION_BINDINGS],
|
|
)
|
|
def test_execution_bindings_start_the_native_execution_and_return_its_result(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
attribute: str,
|
|
route_binding: bindings.NativeBinding[object],
|
|
asynchronous: bool,
|
|
) -> None:
|
|
call: Final = NativeCall(args=(), kwargs={}, base={})
|
|
received: list[NativeCall] = []
|
|
|
|
def native_route(incoming: NativeCall) -> _EchoingExecution:
|
|
received.append(incoming)
|
|
return _EchoingExecution()
|
|
|
|
monkeypatch.setattr(bindings, "get_native_bridge", lambda: SimpleNamespace(**{attribute: native_route}))
|
|
route_binding.reset()
|
|
bound: Final = route_binding.load()
|
|
assert callable(bound)
|
|
|
|
outcome: Final = bound(call)
|
|
result: Final = (
|
|
asyncio.run(cast("Coroutine[None, None, object]", outcome)) # cast-ok: async bindings return a coroutine
|
|
if asynchronous
|
|
else outcome
|
|
)
|
|
route_binding.reset()
|
|
|
|
assert received == [call]
|
|
assert result == ("completed with", None)
|