mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-19 00:01:29 +00:00
route stuff through dispatch no direct main
This commit is contained in:
parent
56ba988b62
commit
b170d61b8d
18 changed files with 306 additions and 27 deletions
|
|
@ -1405,10 +1405,22 @@ from .images.main import *
|
|||
from .videos.main import *
|
||||
from .batch_completion.main import *
|
||||
from .rerank_api.main import *
|
||||
from .llms.anthropic.experimental_pass_through.messages.handler import *
|
||||
from .messages.dispatch import *
|
||||
from .responses.main import *
|
||||
from .responses.dispatch import *
|
||||
from .responses.main import (
|
||||
acancel_responses,
|
||||
acompact_responses,
|
||||
adelete_responses,
|
||||
aget_responses,
|
||||
alist_input_items,
|
||||
aresponses_api_with_mcp,
|
||||
cancel_responses,
|
||||
compact_responses,
|
||||
delete_responses,
|
||||
get_responses,
|
||||
list_input_items,
|
||||
mock_responses_api_response,
|
||||
)
|
||||
|
||||
# Interactions API is available as litellm.interactions module
|
||||
# Usage: litellm.interactions.create(), litellm.interactions.get(), etc.
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ This is an __init__.py file to allow the following interface
|
|||
from collections.abc import AsyncIterator, Coroutine, Iterator
|
||||
from typing import Any
|
||||
|
||||
from litellm.llms.anthropic.experimental_pass_through.messages.handler import (
|
||||
from litellm.messages import (
|
||||
anthropic_messages as _async_anthropic_messages,
|
||||
)
|
||||
from litellm.llms.anthropic.experimental_pass_through.messages.handler import (
|
||||
from litellm.messages import (
|
||||
anthropic_messages_handler as _sync_anthropic_messages,
|
||||
)
|
||||
from litellm.types.llms.anthropic_messages.anthropic_response import (
|
||||
|
|
|
|||
|
|
@ -31,13 +31,15 @@ PythonAcompletion: TypeAlias = Callable[..., Awaitable[ChatResult]]
|
|||
|
||||
def _python_completion() -> PythonCompletion:
|
||||
return cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
PythonCompletion, main.completion
|
||||
PythonCompletion,
|
||||
main.completion, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
def _python_acompletion() -> PythonAcompletion:
|
||||
return cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
PythonAcompletion, main.acompletion
|
||||
PythonAcompletion,
|
||||
main.acompletion, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ from ..utils import is_reasoning_auto_summary_enabled
|
|||
from .interceptors import get_messages_interceptors
|
||||
from .utils import AnthropicMessagesRequestUtils, mock_response
|
||||
|
||||
__all__ = ("anthropic_messages", "anthropic_messages_handler")
|
||||
|
||||
# Providers that are routed directly to the OpenAI Responses API instead of
|
||||
# going through chat/completions.
|
||||
_RESPONSES_API_PROVIDERS: Final = frozenset({"openai"})
|
||||
|
|
|
|||
|
|
@ -414,9 +414,7 @@ async def _call_messages_handler(
|
|||
Using the public function (decorated with @client) ensures logging, retries,
|
||||
and provider resolution all work correctly, identical to a direct user call.
|
||||
"""
|
||||
from litellm.llms.anthropic.experimental_pass_through.messages.handler import (
|
||||
anthropic_messages,
|
||||
)
|
||||
from litellm.messages import anthropic_messages
|
||||
|
||||
return await anthropic_messages(
|
||||
model=model,
|
||||
|
|
|
|||
|
|
@ -5968,7 +5968,7 @@ def responses_with_retries(*args, **kwargs):
|
|||
except Exception as e:
|
||||
raise Exception(f"tenacity import failed please run `pip install tenacity`. Error{e}")
|
||||
|
||||
from litellm.responses.main import responses
|
||||
from litellm.responses.dispatch import responses
|
||||
|
||||
num_retries: Final = kwargs.pop("num_retries", 3)
|
||||
# reset retries in .responses()
|
||||
|
|
@ -5998,7 +5998,7 @@ async def aresponses_with_retries(*args, **kwargs):
|
|||
except Exception as e:
|
||||
raise Exception(f"tenacity import failed please run `pip install tenacity`. Error{e}")
|
||||
|
||||
from litellm.responses.main import aresponses
|
||||
from litellm.responses.dispatch import aresponses
|
||||
|
||||
num_retries: Final = kwargs.pop("num_retries", 3)
|
||||
kwargs["max_retries"] = 0
|
||||
|
|
|
|||
|
|
@ -30,13 +30,15 @@ PythonAmessages: TypeAlias = Callable[..., Awaitable[MessagesResult]]
|
|||
|
||||
def _python_messages() -> PythonMessages:
|
||||
return cast( # cast-ok: forward the original call shape through the legacy handler
|
||||
PythonMessages, main.anthropic_messages_handler
|
||||
PythonMessages,
|
||||
main.anthropic_messages_handler, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
def _python_amessages() -> PythonAmessages:
|
||||
return cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
PythonAmessages, main.anthropic_messages
|
||||
PythonAmessages,
|
||||
main.anthropic_messages, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,10 +43,12 @@ def _public_request(name: str, args: tuple[object, ...], kwargs: Mapping[str, ob
|
|||
|
||||
|
||||
_PYTHON_OCR: Final = cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
Callable[..., OCRResponse | Coroutine[object, object, OCRResponse]], main.ocr
|
||||
Callable[..., OCRResponse | Coroutine[object, object, OCRResponse]],
|
||||
main.ocr, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
_PYTHON_AOCR: Final = cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
Callable[..., Awaitable[OCRResponse]], main.aocr
|
||||
Callable[..., Awaitable[OCRResponse]],
|
||||
main.aocr, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,15 @@ PythonAresponses: TypeAlias = Callable[..., Awaitable[ResponsesResult]]
|
|||
|
||||
def _python_responses() -> PythonResponses:
|
||||
return cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
PythonResponses, main.responses
|
||||
PythonResponses,
|
||||
main.responses, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
def _python_aresponses() -> PythonAresponses:
|
||||
return cast( # cast-ok: forward the original call shape through the Python @client decorator
|
||||
PythonAresponses, main.aresponses
|
||||
PythonAresponses,
|
||||
main.aresponses, # noqa: TID251 # dispatch boundary owns this Python fallback
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ def _synthesize_responses_api_response(
|
|||
|
||||
|
||||
async def _call_aresponses(input, model, tools, **kwargs): # pragma: no cover – thin wrapper for patching in tests
|
||||
from litellm.responses.main import aresponses
|
||||
from litellm.responses.main import aresponses # noqa: TID251 # inner call must not re-enter file-search emulation
|
||||
|
||||
return await aresponses(input=input, model=model, tools=tools, **kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,23 @@ else:
|
|||
|
||||
from .streaming_iterator import BaseResponsesAPIStreamingIterator
|
||||
|
||||
__all__ = (
|
||||
"acancel_responses",
|
||||
"acompact_responses",
|
||||
"adelete_responses",
|
||||
"aget_responses",
|
||||
"alist_input_items",
|
||||
"aresponses",
|
||||
"aresponses_api_with_mcp",
|
||||
"cancel_responses",
|
||||
"compact_responses",
|
||||
"delete_responses",
|
||||
"get_responses",
|
||||
"list_input_items",
|
||||
"mock_responses_api_response",
|
||||
"responses",
|
||||
)
|
||||
|
||||
####### ENVIRONMENT VARIABLES ###################
|
||||
# Initialize any necessary instances or variables here
|
||||
base_llm_http_handler = BaseLLMHTTPHandler()
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from litellm.proxy._experimental.mcp_server.utils import (
|
|||
split_server_prefix_from_name,
|
||||
strip_known_server_prefix,
|
||||
)
|
||||
from litellm.responses.main import aresponses
|
||||
from litellm.responses.main import aresponses # noqa: TID251 # inner call must skip the MCP gateway that invoked it
|
||||
from litellm.responses.streaming_iterator import BaseResponsesAPIStreamingIterator
|
||||
from litellm.types.llms.openai import (
|
||||
ResponseInputParam,
|
||||
|
|
|
|||
|
|
@ -609,7 +609,7 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator):
|
|||
"""Create the initial response iterator by making the first LLM call"""
|
||||
try:
|
||||
# Import the core aresponses function that doesn't have MCP logic
|
||||
from litellm.responses.main import aresponses
|
||||
from litellm.responses.main import aresponses # noqa: TID251 # core call without MCP logic
|
||||
|
||||
# Make the initial response API call - but avoid the MCP wrapper
|
||||
params: Final[dict[str, object]] = self.original_request_params.copy()
|
||||
|
|
@ -773,7 +773,7 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator):
|
|||
self.base_iterator = None
|
||||
return
|
||||
|
||||
from litellm.responses.main import aresponses
|
||||
from litellm.responses.main import aresponses # noqa: TID251 # follow-up call without MCP logic
|
||||
from litellm.responses.mcp.litellm_proxy_mcp_handler import (
|
||||
LiteLLM_Proxy_MCP_Handler,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -57,3 +57,15 @@ max-args = 5
|
|||
"typing_extensions.TypeGuard".msg = "Same as typing.TypeGuard."
|
||||
"typing.TypeIs".msg = "Unverified narrowing (the body is trusted). Parse into a concrete type instead."
|
||||
"typing_extensions.TypeIs".msg = "Same as typing.TypeIs."
|
||||
# Dispatched public entry points: import them from their dispatch module so every
|
||||
# supported call path selects Rust or Python in one place. Only the dispatch
|
||||
# modules and internal recursive calls may reach the Python implementation
|
||||
# directly, each with a `# noqa: TID251 # <reason>`.
|
||||
"litellm.responses.main.responses".msg = "Import litellm.responses.dispatch.responses so the call routes through dispatch."
|
||||
"litellm.responses.main.aresponses".msg = "Import litellm.responses.dispatch.aresponses so the call routes through dispatch."
|
||||
"litellm.llms.anthropic.experimental_pass_through.messages.handler.anthropic_messages".msg = "Import litellm.messages.anthropic_messages so the call routes through dispatch."
|
||||
"litellm.llms.anthropic.experimental_pass_through.messages.handler.anthropic_messages_handler".msg = "Import litellm.messages.anthropic_messages_handler so the call routes through dispatch."
|
||||
"litellm.ocr.main.ocr".msg = "Import litellm.ocr.dispatch.ocr so the call routes through dispatch."
|
||||
"litellm.ocr.main.aocr".msg = "Import litellm.ocr.dispatch.aocr so the call routes through dispatch."
|
||||
"litellm.main.completion".msg = "Import litellm.completion so the call routes through dispatch."
|
||||
"litellm.main.acompletion".msg = "Import litellm.acompletion so the call routes through dispatch."
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import inspect
|
||||
from collections.abc import Callable, Mapping
|
||||
from collections.abc import Awaitable, Callable, Mapping
|
||||
from typing import Final, cast # noqa: TID251 # narrows legacy callable signatures for inspect
|
||||
|
||||
import pytest
|
||||
|
|
@ -10,9 +10,12 @@ from litellm.chat_completions.dispatch import (
|
|||
_ADISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
_DISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
)
|
||||
from litellm.rust_bridge import catalog
|
||||
from litellm.rust_bridge.bindings import NativeBinding
|
||||
from litellm.rust_bridge.catalog import Route, Rule
|
||||
from litellm.rust_bridge.chat_completions.entrypoints import (
|
||||
NATIVE_ACOMPLETION,
|
||||
NATIVE_COMPLETION,
|
||||
LiteLLMChatCompletionsRequest,
|
||||
NativeAcompletion,
|
||||
NativeCompletion,
|
||||
|
|
@ -219,3 +222,50 @@ def test_binding_errors_delegate_to_python(args: tuple[object, ...], kwargs: Map
|
|||
is response
|
||||
)
|
||||
assert captured == [(args, kwargs)]
|
||||
|
||||
|
||||
def test_public_completion_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: Final[list[LiteLLMChatCompletionsRequest]] = []
|
||||
expected: Final = ModelResponse()
|
||||
|
||||
def native(
|
||||
request: LiteLLMChatCompletionsRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> ModelResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_COMPLETION.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_completion: Final = cast(Callable[..., ModelResponse], litellm.completion)
|
||||
try:
|
||||
result: Final = public_completion(model="gpt-4o", messages=MESSAGES)
|
||||
finally:
|
||||
NATIVE_COMPLETION.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["gpt-4o"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_public_acompletion_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: Final[list[LiteLLMChatCompletionsRequest]] = []
|
||||
expected: Final = ModelResponse()
|
||||
|
||||
async def native(
|
||||
request: LiteLLMChatCompletionsRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> ModelResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_ACOMPLETION.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_acompletion: Final = cast(Callable[..., Awaitable[ModelResponse]], litellm.acompletion)
|
||||
try:
|
||||
result: Final = await public_acompletion(model="gpt-4o", messages=MESSAGES)
|
||||
finally:
|
||||
NATIVE_ACOMPLETION.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["gpt-4o"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import inspect
|
||||
from collections.abc import Callable, Mapping
|
||||
from collections.abc import Awaitable, Callable, Mapping
|
||||
from typing import Final, cast # noqa: TID251 # narrows legacy callable signatures for inspect
|
||||
|
||||
import pytest
|
||||
|
|
@ -10,10 +10,13 @@ from litellm.messages.dispatch import (
|
|||
_ADISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
_DISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
)
|
||||
from litellm.rust_bridge import catalog
|
||||
from litellm.rust_bridge.bindings import NativeBinding
|
||||
from litellm.rust_bridge.catalog import Route, Rule, Rules
|
||||
from litellm.rust_bridge.configuration import Rollout
|
||||
from litellm.rust_bridge.messages.entrypoints import (
|
||||
NATIVE_AMESSAGES,
|
||||
NATIVE_MESSAGES,
|
||||
LiteLLMMessagesRequest,
|
||||
NativeAmessages,
|
||||
NativeMessages,
|
||||
|
|
@ -235,3 +238,50 @@ def test_binding_errors_delegate_to_python(args: tuple[object, ...], kwargs: Map
|
|||
)
|
||||
assert result is expected
|
||||
assert captured == [(args, kwargs)]
|
||||
|
||||
|
||||
def test_anthropic_create_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: Final[list[LiteLLMMessagesRequest]] = []
|
||||
expected: Final = response()
|
||||
|
||||
def native(
|
||||
request: LiteLLMMessagesRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> AnthropicMessagesResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_MESSAGES.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_create: Final = cast(Callable[..., AnthropicMessagesResponse], litellm.anthropic.create)
|
||||
try:
|
||||
result: Final = public_create(max_tokens=16, messages=MESSAGES, model="claude-sonnet-4-5")
|
||||
finally:
|
||||
NATIVE_MESSAGES.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["claude-sonnet-4-5"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_anthropic_acreate_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: Final[list[LiteLLMMessagesRequest]] = []
|
||||
expected: Final = response()
|
||||
|
||||
async def native(
|
||||
request: LiteLLMMessagesRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> AnthropicMessagesResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_AMESSAGES.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_acreate: Final = cast(Callable[..., Awaitable[AnthropicMessagesResponse]], litellm.anthropic.acreate)
|
||||
try:
|
||||
result: Final = await public_acreate(max_tokens=16, messages=MESSAGES, model="claude-sonnet-4-5")
|
||||
finally:
|
||||
NATIVE_AMESSAGES.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["claude-sonnet-4-5"]
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
from collections.abc import Mapping
|
||||
from typing import Final
|
||||
from collections.abc import Awaitable, Callable, Mapping
|
||||
from typing import Final, cast # noqa: TID251 # narrows legacy callable signatures for inspect
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.llms.base_llm.ocr.transformation import OCRResponse
|
||||
from litellm.ocr.dispatch import (
|
||||
_ADISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
_DISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
)
|
||||
from litellm.rust_bridge import catalog
|
||||
from litellm.rust_bridge.bindings import NativeBinding
|
||||
from litellm.rust_bridge.catalog import Route, Rule, Rules
|
||||
from litellm.rust_bridge.configuration import Rollout
|
||||
from litellm.rust_bridge.ocr.entrypoints import LiteLLMOcrRequest, NativeAocr, NativeOcr
|
||||
from litellm.rust_bridge.ocr.entrypoints import (
|
||||
NATIVE_AOCR,
|
||||
NATIVE_OCR,
|
||||
LiteLLMOcrRequest,
|
||||
NativeAocr,
|
||||
NativeOcr,
|
||||
)
|
||||
|
||||
PYTHON_RULES: Final[Rules] = (Rule(Route.OCR, Rollout.PYTHON_ONLY),)
|
||||
RUST_RULES: Final[Rules] = (Rule(Route.OCR, Rollout.RUST_REQUIRED),)
|
||||
|
|
@ -324,3 +332,58 @@ async def test_aocr_parser_errors_before_python_or_native(
|
|||
native=lambda hook, request, call_args, call_kwargs: hook(request, call_args, call_kwargs),
|
||||
rules=RUST_RULES,
|
||||
)
|
||||
|
||||
|
||||
def test_public_ocr_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
document: Final[Mapping[str, object]] = {
|
||||
"type": "document_url",
|
||||
"document_url": "https://example.invalid/document.pdf",
|
||||
}
|
||||
captured: Final[list[LiteLLMOcrRequest]] = []
|
||||
expected: Final = response()
|
||||
|
||||
def native(
|
||||
request: LiteLLMOcrRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> OCRResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_OCR.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_ocr: Final = cast(Callable[..., OCRResponse], litellm.ocr)
|
||||
try:
|
||||
result: Final = public_ocr(model="mistral/mistral-ocr-latest", document=document)
|
||||
finally:
|
||||
NATIVE_OCR.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["mistral/mistral-ocr-latest"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_public_aocr_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
document: Final[Mapping[str, object]] = {
|
||||
"type": "document_url",
|
||||
"document_url": "https://example.invalid/document.pdf",
|
||||
}
|
||||
captured: Final[list[LiteLLMOcrRequest]] = []
|
||||
expected: Final = response()
|
||||
|
||||
async def native(
|
||||
request: LiteLLMOcrRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> OCRResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_AOCR.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_aocr: Final = cast(Callable[..., Awaitable[OCRResponse]], litellm.aocr)
|
||||
try:
|
||||
result: Final = await public_aocr(model="mistral/mistral-ocr-latest", document=document)
|
||||
finally:
|
||||
NATIVE_AOCR.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["mistral/mistral-ocr-latest"]
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
import inspect
|
||||
from collections.abc import Callable, Mapping
|
||||
from collections.abc import Awaitable, Callable, Mapping
|
||||
from typing import Final, cast # noqa: TID251 # narrows legacy callable signatures for inspect
|
||||
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.responses import dispatch as responses_dispatch
|
||||
from litellm.responses import main as python_responses
|
||||
from litellm.responses.dispatch import (
|
||||
_ADISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
_DISPATCH, # pyright: ignore[reportPrivateUsage] # tests configured dispatch
|
||||
)
|
||||
from litellm.rust_bridge import catalog
|
||||
from litellm.rust_bridge.bindings import NativeBinding
|
||||
from litellm.rust_bridge.catalog import Route, Rule
|
||||
from litellm.rust_bridge.configuration import Rollout
|
||||
from litellm.rust_bridge.responses.entrypoints import (
|
||||
NATIVE_ARESPONSES,
|
||||
NATIVE_RESPONSES,
|
||||
LiteLLMResponsesRequest,
|
||||
NativeAresponses,
|
||||
NativeResponses,
|
||||
|
|
@ -253,3 +257,66 @@ def test_binding_errors_delegate_unchanged_to_python(
|
|||
is response
|
||||
)
|
||||
assert captured == [(args, kwargs)]
|
||||
|
||||
|
||||
def test_public_responses_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: Final[list[LiteLLMResponsesRequest]] = []
|
||||
expected: Final = _response()
|
||||
|
||||
def native(
|
||||
request: LiteLLMResponsesRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> ResponsesAPIResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_RESPONSES.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_responses: Final = cast(Callable[..., ResponsesAPIResponse], litellm.responses)
|
||||
try:
|
||||
result: Final = public_responses(input=INPUT, model="gpt-4o")
|
||||
finally:
|
||||
NATIVE_RESPONSES.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["gpt-4o"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_public_aresponses_routes_through_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: Final[list[LiteLLMResponsesRequest]] = []
|
||||
expected: Final = _response()
|
||||
|
||||
async def native(
|
||||
request: LiteLLMResponsesRequest,
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> ResponsesAPIResponse:
|
||||
captured.append(request)
|
||||
return expected
|
||||
|
||||
NATIVE_ARESPONSES.override(native)
|
||||
monkeypatch.setattr(catalog, "RULES", RUST_RULES)
|
||||
public_aresponses: Final = cast(Callable[..., Awaitable[ResponsesAPIResponse]], litellm.aresponses)
|
||||
try:
|
||||
result: Final = await public_aresponses(input=INPUT, model="gpt-4o")
|
||||
finally:
|
||||
NATIVE_ARESPONSES.reset()
|
||||
assert result is expected
|
||||
assert [request.model for request in captured] == ["gpt-4o"]
|
||||
|
||||
|
||||
def test_responses_with_retries_uses_the_dispatch_entrypoint(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
calls: Final[list[Mapping[str, object]]] = []
|
||||
expected: Final = _response()
|
||||
|
||||
def dispatch_responses(*args: object, **kwargs: object) -> ResponsesAPIResponse: # kwargs-ok: records call shape
|
||||
calls.append(kwargs)
|
||||
return expected
|
||||
|
||||
monkeypatch.setattr(responses_dispatch, "responses", dispatch_responses)
|
||||
retry: Final = cast(Callable[..., ResponsesAPIResponse], litellm.responses_with_retries)
|
||||
result: Final = retry(input=INPUT, model="gpt-4o", num_retries=1)
|
||||
assert result is expected
|
||||
assert calls[0]["num_retries"] == 0
|
||||
assert calls[0]["max_retries"] == 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue