From 1f0cf4bf4236add5048b0726c2c01eefe4498085 Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Wed, 16 Sep 2026 23:23:36 +0000 Subject: [PATCH] fix(rust_bridge): bind Python fallbacks at import so module patches do not leak into public entrypoints Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/chat_completions/dispatch.py | 18 ++++++++++-------- litellm/messages/dispatch.py | 18 ++++++++++-------- litellm/ocr/dispatch.py | 17 +++++++++-------- litellm/responses/dispatch.py | 18 ++++++++++-------- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/litellm/chat_completions/dispatch.py b/litellm/chat_completions/dispatch.py index 274aceb4ccd..968cdb5b720 100644 --- a/litellm/chat_completions/dispatch.py +++ b/litellm/chat_completions/dispatch.py @@ -41,8 +41,10 @@ def _python_acompletion() -> PythonAcompletion: ) -_COMPLETION: Final = signature(_python_completion()) -_ACOMPLETION: Final = signature(_python_acompletion()) +_PYTHON_COMPLETION: Final = _python_completion() +_COMPLETION: Final = signature(_PYTHON_COMPLETION) +_PYTHON_ACOMPLETION: Final = _python_acompletion() +_ACOMPLETION: Final = signature(_PYTHON_ACOMPLETION) def _public_request( @@ -86,7 +88,7 @@ def completion( *args: object, **kwargs: object, # kwargs-ok: preserve the public chat completions call shape ) -> ChatResult | Coroutine[object, object, ChatResult]: - python: Final = _python_completion() + python: Final = _PYTHON_COMPLETION return _DISPATCH.run( args, kwargs, @@ -97,7 +99,7 @@ def completion( async def acompletion(*args: object, **kwargs: object) -> ChatResult: # kwargs-ok: preserve the public call shape - python: Final = _python_acompletion() + python: Final = _PYTHON_ACOMPLETION return await _ADISPATCH.arun( args, kwargs, @@ -116,7 +118,7 @@ def _context(request: LiteLLMChatCompletionsRequest) -> Context: ) -completion.__doc__ = _python_completion().__doc__ -completion.__wrapped__ = _python_completion() # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature -acompletion.__doc__ = _python_acompletion().__doc__ -acompletion.__wrapped__ = _python_acompletion() # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature +completion.__doc__ = _PYTHON_COMPLETION.__doc__ +completion.__wrapped__ = _PYTHON_COMPLETION # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature +acompletion.__doc__ = _PYTHON_ACOMPLETION.__doc__ +acompletion.__wrapped__ = _PYTHON_ACOMPLETION # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature diff --git a/litellm/messages/dispatch.py b/litellm/messages/dispatch.py index 3932b0b96c8..c5c5c36593e 100644 --- a/litellm/messages/dispatch.py +++ b/litellm/messages/dispatch.py @@ -40,8 +40,10 @@ def _python_amessages() -> PythonAmessages: ) -_MESSAGES: Final = signature(_python_messages()) -_AMESSAGES: Final = signature(_python_amessages()) +_PYTHON_MESSAGES: Final = _python_messages() +_MESSAGES: Final = signature(_PYTHON_MESSAGES) +_PYTHON_AMESSAGES: Final = _python_amessages() +_AMESSAGES: Final = signature(_PYTHON_AMESSAGES) def _public_request( @@ -85,7 +87,7 @@ def anthropic_messages_handler( *args: object, **kwargs: object, # kwargs-ok: preserve the public Anthropic Messages call shape ) -> MessagesResult | Coroutine[object, object, MessagesResult]: - python: Final = _python_messages() + python: Final = _PYTHON_MESSAGES return _DISPATCH.run( args, kwargs, @@ -96,7 +98,7 @@ def anthropic_messages_handler( async def anthropic_messages(*args: object, **kwargs: object) -> MessagesResult: # kwargs-ok: public call shape - python: Final = _python_amessages() + python: Final = _PYTHON_AMESSAGES return await _ADISPATCH.arun( args, kwargs, @@ -115,7 +117,7 @@ def _context(request: LiteLLMMessagesRequest) -> Context: ) -anthropic_messages_handler.__doc__ = _python_messages().__doc__ -anthropic_messages_handler.__wrapped__ = _python_messages() # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature -anthropic_messages.__doc__ = _python_amessages().__doc__ -anthropic_messages.__wrapped__ = _python_amessages() # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature +anthropic_messages_handler.__doc__ = _PYTHON_MESSAGES.__doc__ +anthropic_messages_handler.__wrapped__ = _PYTHON_MESSAGES # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature +anthropic_messages.__doc__ = _PYTHON_AMESSAGES.__doc__ +anthropic_messages.__wrapped__ = _PYTHON_AMESSAGES # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature diff --git a/litellm/ocr/dispatch.py b/litellm/ocr/dispatch.py index 41ea9ee074b..3b43eecf001 100644 --- a/litellm/ocr/dispatch.py +++ b/litellm/ocr/dispatch.py @@ -42,6 +42,13 @@ def _public_request(name: str, args: tuple[object, ...], kwargs: Mapping[str, ob raise TypeError(str(error).replace("_bind_request()", f"{name}()")) from None +_PYTHON_OCR: Final = cast( # cast-ok: forward the original call shape through the Python @client decorator + Callable[..., OCRResponse | Coroutine[object, object, OCRResponse]], main.ocr +) +_PYTHON_AOCR: Final = cast( # cast-ok: forward the original call shape through the Python @client decorator + Callable[..., Awaitable[OCRResponse]], main.aocr +) + _DISPATCH: Final = PublicDispatch( route=Route.OCR, request=lambda args, kwargs: _public_request("ocr", args, kwargs), @@ -60,26 +67,20 @@ def ocr( *args: object, **kwargs: object, # kwargs-ok: preserve the public OCR call shape ) -> OCRResponse | Coroutine[object, object, OCRResponse]: - python_ocr: Final = cast( # cast-ok: forward the original call shape through the Python @client decorator - Callable[..., OCRResponse | Coroutine[object, object, OCRResponse]], main.ocr - ) return _DISPATCH.run( args, kwargs, - python=python_ocr, + python=_PYTHON_OCR, binding=NATIVE_OCR, native=lambda hook, request, call_args, call_kwargs: hook(request, call_args, call_kwargs), ) async def aocr(*args: object, **kwargs: object) -> OCRResponse: # kwargs-ok: preserve the public OCR call shape - fallback: Final = cast( # cast-ok: forward the original call shape through the Python @client decorator - Callable[..., Awaitable[OCRResponse]], main.aocr - ) return await _ADISPATCH.arun( args, kwargs, - python=fallback, + python=_PYTHON_AOCR, binding=NATIVE_AOCR, native=lambda hook, request, call_args, call_kwargs: hook(request, call_args, call_kwargs), ) diff --git a/litellm/responses/dispatch.py b/litellm/responses/dispatch.py index 3041a669362..8c629f9d1f2 100644 --- a/litellm/responses/dispatch.py +++ b/litellm/responses/dispatch.py @@ -34,8 +34,10 @@ def _python_aresponses() -> PythonAresponses: ) -_RESPONSES: Final = signature(_python_responses()) -_ARESPONSES: Final = signature(_python_aresponses()) +_PYTHON_RESPONSES: Final = _python_responses() +_RESPONSES: Final = signature(_PYTHON_RESPONSES) +_PYTHON_ARESPONSES: Final = _python_aresponses() +_ARESPONSES: Final = signature(_PYTHON_ARESPONSES) def _public_request( @@ -78,7 +80,7 @@ def responses( *args: object, **kwargs: object, # kwargs-ok: preserve the public Responses call shape ) -> ResponsesResult | Coroutine[object, object, ResponsesResult]: - python: Final = _python_responses() + python: Final = _PYTHON_RESPONSES return _DISPATCH.run( args, kwargs, @@ -89,7 +91,7 @@ def responses( async def aresponses(*args: object, **kwargs: object) -> ResponsesResult: # kwargs-ok: preserve the public call shape - python: Final = _python_aresponses() + python: Final = _PYTHON_ARESPONSES return await _ADISPATCH.arun( args, kwargs, @@ -108,7 +110,7 @@ def _context(request: LiteLLMResponsesRequest) -> Context: ) -responses.__doc__ = _python_responses().__doc__ -responses.__wrapped__ = _python_responses() # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature -aresponses.__doc__ = _python_aresponses().__doc__ -aresponses.__wrapped__ = _python_aresponses() # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature +responses.__doc__ = _PYTHON_RESPONSES.__doc__ +responses.__wrapped__ = _PYTHON_RESPONSES # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature +aresponses.__doc__ = _PYTHON_ARESPONSES.__doc__ +aresponses.__wrapped__ = _PYTHON_ARESPONSES # pyright: ignore[reportFunctionMemberAccess] # inspect.signature follows __wrapped__ to the legacy signature