mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
feat(ocr): drive callbacks through native lifecycle
This commit is contained in:
parent
0ca7060706
commit
fc7f15e9e3
4 changed files with 17 additions and 74 deletions
|
|
@ -2476,21 +2476,12 @@ class BaseLLMHTTPHandler:
|
|||
extra_headers=headers,
|
||||
timeout=timeout,
|
||||
)
|
||||
except Exception as rust_error: # noqa: BLE001 # only explicit pre-dispatch declines permit fallback
|
||||
from litellm.rust_bridge.bindings import native_exception_types, upstream_error_details
|
||||
|
||||
exceptions: Final = native_exception_types()
|
||||
if exceptions is not None and isinstance(rust_error, exceptions[0]):
|
||||
return None
|
||||
if exceptions is not None and isinstance(rust_error, exceptions[1]):
|
||||
status, message = upstream_error_details(rust_error)
|
||||
raise litellm.APIError(
|
||||
status_code=status,
|
||||
message=message,
|
||||
llm_provider=custom_llm_provider,
|
||||
model=model,
|
||||
) from rust_error
|
||||
raise
|
||||
except Exception as rust_error: # noqa: BLE001 # rollout-safety fallback: any Rust bridge failure must fall back to the Python path
|
||||
verbose_logger.debug(
|
||||
"Rust Anthropic messages bridge raised %s; falling back to Python path",
|
||||
type(rust_error).__name__,
|
||||
)
|
||||
return None
|
||||
if rust_response is None:
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from enum import Enum
|
|||
from typing import Final, Generic, NoReturn, TypeAlias, TypeVar
|
||||
|
||||
from litellm.exceptions import APIError
|
||||
from litellm.rust_bridge.bindings import native_exception_types, upstream_error_details
|
||||
from litellm.rust_bridge.bindings import native_exception_types
|
||||
|
||||
NativeT = TypeVar("NativeT")
|
||||
ResultT = TypeVar("ResultT")
|
||||
|
|
@ -137,9 +137,13 @@ def _required_reason(result: RustDeclined | RustUnavailable) -> str:
|
|||
|
||||
|
||||
def _raise_upstream(error: BaseException, context: BridgeErrorContext) -> NoReturn:
|
||||
status, message = upstream_error_details(error)
|
||||
args: Final[tuple[object, ...]] = error.args
|
||||
status_value: Final = args[0] if args else 0
|
||||
message_value: Final = args[1] if len(args) > 1 else str(error)
|
||||
status: Final = status_value if isinstance(status_value, int) else 0
|
||||
message: Final = message_value if isinstance(message_value, str) else str(message_value)
|
||||
raise APIError(
|
||||
status_code=status,
|
||||
status_code=status or 500,
|
||||
message=f"litellm rust {context.route}: {message}",
|
||||
llm_provider=context.provider,
|
||||
model=context.model,
|
||||
|
|
|
|||
|
|
@ -238,66 +238,17 @@ async def test_gate_invokes_rust_and_marks_response_header():
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_gate_propagates_unclassified_bridge_failure():
|
||||
async def test_gate_falls_back_to_python_when_bridge_raises():
|
||||
bridge = RaisingAsyncMessages()
|
||||
litellm.rust(True)
|
||||
rust_messages.set_rust_messages(amessages=bridge)
|
||||
|
||||
with pytest.raises(RuntimeError, match="upstream request failed"):
|
||||
await _gate()
|
||||
response = await _gate()
|
||||
|
||||
assert response is None
|
||||
assert bridge.calls == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("status", [0, 400, 429, 500])
|
||||
async def test_gate_never_falls_back_after_possible_dispatch(monkeypatch, status):
|
||||
from types import SimpleNamespace
|
||||
from litellm.rust_bridge import bindings
|
||||
|
||||
class Declined(Exception):
|
||||
pass
|
||||
|
||||
class Upstream(Exception):
|
||||
pass
|
||||
|
||||
error = Upstream(status, "provider failure")
|
||||
|
||||
async def bridge(**kwargs):
|
||||
raise error
|
||||
|
||||
monkeypatch.setattr(
|
||||
bindings, "get_native_bridge", lambda: SimpleNamespace(RustBridgeDeclined=Declined, RustUpstreamError=Upstream)
|
||||
)
|
||||
litellm.rust(True)
|
||||
rust_messages.set_rust_messages(amessages=bridge)
|
||||
with pytest.raises(litellm.APIError) as caught:
|
||||
await _gate()
|
||||
assert caught.value.status_code == (status or 500)
|
||||
assert caught.value.__cause__ is error
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_gate_falls_back_only_for_explicit_decline(monkeypatch):
|
||||
from types import SimpleNamespace
|
||||
from litellm.rust_bridge import bindings
|
||||
|
||||
class Declined(Exception):
|
||||
pass
|
||||
|
||||
class Upstream(Exception):
|
||||
pass
|
||||
|
||||
async def bridge(**kwargs):
|
||||
raise Declined("unsupported before dispatch")
|
||||
|
||||
monkeypatch.setattr(
|
||||
bindings, "get_native_bridge", lambda: SimpleNamespace(RustBridgeDeclined=Declined, RustUpstreamError=Upstream)
|
||||
)
|
||||
litellm.rust(True)
|
||||
rust_messages.set_rust_messages(amessages=bridge)
|
||||
assert await _gate() is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_gate_skips_rust_when_flag_absent():
|
||||
bridge = ExplodingAsyncMessages()
|
||||
|
|
|
|||
|
|
@ -55,9 +55,6 @@ class _FakeNative:
|
|||
def _fake_native_bridge(monkeypatch):
|
||||
"""Expose the bridge's exception classes without the compiled extension."""
|
||||
monkeypatch.setattr(bridge, "get_native_bridge", lambda: _FakeNative())
|
||||
from litellm.rust_bridge import bindings
|
||||
|
||||
monkeypatch.setattr(bindings, "get_native_bridge", lambda: _FakeNative())
|
||||
|
||||
|
||||
def _hide_native_bridge(monkeypatch):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue