mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
fix: honor rust image edit bridge guard
This commit is contained in:
parent
620f461739
commit
d3598d7dc4
4 changed files with 70 additions and 11 deletions
|
|
@ -81,6 +81,7 @@ from litellm.ocr.rust_bridge import (
|
|||
RustImageEdit,
|
||||
load_rust_aimage_edit,
|
||||
load_rust_image_edit,
|
||||
rust_image_edit_enabled,
|
||||
)
|
||||
|
||||
# Cache for ImageEditRequestUtils to avoid repeated __getattr__ calls
|
||||
|
|
@ -119,10 +120,15 @@ def _timeout_to_seconds(
|
|||
if timeout is None:
|
||||
return None
|
||||
if isinstance(timeout, httpx.Timeout):
|
||||
read_timeout = timeout.read
|
||||
if read_timeout is None:
|
||||
return None
|
||||
return float(read_timeout)
|
||||
for timeout_value in (
|
||||
timeout.read,
|
||||
timeout.connect,
|
||||
timeout.write,
|
||||
timeout.pool,
|
||||
):
|
||||
if timeout_value is not None:
|
||||
return float(timeout_value)
|
||||
return None
|
||||
return float(timeout)
|
||||
|
||||
|
||||
|
|
@ -147,11 +153,14 @@ def _read_file_content(file_value: Any) -> bytes:
|
|||
if isinstance(data, str):
|
||||
return data.encode()
|
||||
return bytes(data)
|
||||
if isinstance(file_value, (str, os.PathLike)) and os.path.exists(file_value):
|
||||
with open(file_value, "rb") as file:
|
||||
if isinstance(file_value, (str, os.PathLike)):
|
||||
path = os.fspath(file_value)
|
||||
if not os.path.exists(path):
|
||||
raise FileNotFoundError(
|
||||
f"Image file path does not exist for Rust image_edit: {path}"
|
||||
)
|
||||
with open(path, "rb") as file:
|
||||
return file.read()
|
||||
if isinstance(file_value, str):
|
||||
return file_value.encode()
|
||||
raise TypeError(
|
||||
f"Unsupported image file type for Rust image_edit: {type(file_value)}"
|
||||
)
|
||||
|
|
@ -1065,7 +1074,7 @@ def image_edit(
|
|||
)
|
||||
|
||||
local_vars.update(kwargs)
|
||||
if custom_llm_provider == "vllm":
|
||||
if custom_llm_provider == "vllm" and rust_image_edit_enabled():
|
||||
image_edit_optional_params = (
|
||||
_get_ImageEditRequestUtils().get_requested_image_edit_optional_param(
|
||||
local_vars
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ class _Unset:
|
|||
_UNSET: Final[_Unset] = _Unset()
|
||||
|
||||
_rust_ocr_enabled = False
|
||||
_rust_image_edit_enabled = False
|
||||
_rust_ocr_impl: RustOcr | None = None
|
||||
_rust_aocr_impl: RustAocr | None = None
|
||||
_rust_image_edit_impl: RustImageEdit | None = None
|
||||
|
|
@ -108,9 +109,10 @@ def use_litellm_rust(
|
|||
extension is loaded on demand and any previously injected bridge is
|
||||
preserved. Pass ``None`` explicitly to clear a prior injection.
|
||||
"""
|
||||
global _rust_ocr_enabled, _rust_ocr_impl, _rust_aocr_impl
|
||||
global _rust_ocr_enabled, _rust_image_edit_enabled, _rust_ocr_impl, _rust_aocr_impl
|
||||
global _rust_image_edit_impl, _rust_aimage_edit_impl
|
||||
_rust_ocr_enabled = enabled
|
||||
_rust_image_edit_enabled = enabled
|
||||
if not isinstance(ocr, _Unset):
|
||||
_rust_ocr_impl = ocr
|
||||
if not isinstance(aocr, _Unset):
|
||||
|
|
@ -128,7 +130,7 @@ def rust_ocr_enabled() -> bool:
|
|||
|
||||
def rust_image_edit_enabled() -> bool:
|
||||
"""Whether the Rust image-edit path has been turned on."""
|
||||
return _rust_ocr_enabled
|
||||
return _rust_image_edit_enabled
|
||||
|
||||
|
||||
def load_rust_ocr() -> RustOcr | None:
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.exceptions import APIConnectionError
|
||||
from litellm.images import main as image_main
|
||||
|
||||
rust_bridge = importlib.import_module("litellm.ocr.rust_bridge")
|
||||
|
||||
|
|
@ -142,3 +146,44 @@ async def test_vllm_aimage_edit_routes_to_async_rust_bridge() -> None:
|
|||
assert len(bridge.calls) == 1
|
||||
assert bridge.calls[0]["model"] == "qwen-image-edit"
|
||||
assert bridge.calls[0]["prompt"] == "make it darker"
|
||||
|
||||
|
||||
def test_vllm_image_edit_respects_disabled_rust_bridge() -> None:
|
||||
bridge = RecordingImageEditBridge()
|
||||
litellm.use_litellm_rust(False, image_edit=bridge)
|
||||
|
||||
with pytest.raises(
|
||||
APIConnectionError, match="image edit is not supported for vllm"
|
||||
):
|
||||
litellm.image_edit(
|
||||
model="vllm/qwen-image-edit",
|
||||
image=PNG_BYTES,
|
||||
prompt="make it brighter",
|
||||
api_base="http://localhost:8000",
|
||||
)
|
||||
|
||||
assert bridge.calls == []
|
||||
|
||||
|
||||
def test_vllm_image_edit_missing_path_raises_before_bridge_call(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
bridge = RecordingImageEditBridge()
|
||||
missing_image = tmp_path / "missing-image.png"
|
||||
litellm.use_litellm_rust(True, image_edit=bridge)
|
||||
|
||||
with pytest.raises(APIConnectionError, match="Image file path does not exist"):
|
||||
litellm.image_edit(
|
||||
model="vllm/qwen-image-edit",
|
||||
image=str(missing_image),
|
||||
prompt="make it brighter",
|
||||
api_base="http://localhost:8000",
|
||||
)
|
||||
|
||||
assert bridge.calls == []
|
||||
|
||||
|
||||
def test_timeout_to_seconds_falls_back_to_non_read_timeout() -> None:
|
||||
timeout = httpx.Timeout(connect=7.0, read=None, write=8.0, pool=9.0)
|
||||
|
||||
assert image_main._timeout_to_seconds(timeout) == 7.0
|
||||
|
|
|
|||
|
|
@ -240,10 +240,13 @@ def fake_async_bridge():
|
|||
|
||||
def test_use_litellm_rust_toggles_flag():
|
||||
assert rust_bridge.rust_ocr_enabled() is False
|
||||
assert rust_bridge.rust_image_edit_enabled() is False
|
||||
litellm.use_litellm_rust()
|
||||
assert rust_bridge.rust_ocr_enabled() is True
|
||||
assert rust_bridge.rust_image_edit_enabled() is True
|
||||
litellm.use_litellm_rust(False)
|
||||
assert rust_bridge.rust_ocr_enabled() is False
|
||||
assert rust_bridge.rust_image_edit_enabled() is False
|
||||
|
||||
|
||||
def test_load_rust_ocr_returns_injected_impl():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue