mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-22 00:31:44 +00:00
fix(ocr): forward the supplied client on the Python path and build pooled clients outside the lock
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
b2d6cd1fcf
commit
542ad7dbac
3 changed files with 44 additions and 5 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex, PoisonError},
|
||||
sync::{Arc, Mutex, MutexGuard, PoisonError},
|
||||
};
|
||||
|
||||
use reqwest::dns::Resolve;
|
||||
|
|
@ -38,13 +38,15 @@ impl HttpClientPool {
|
|||
variant: ClientVariant,
|
||||
) -> Result<reqwest::Client, Error> {
|
||||
let key = (config.clone(), variant);
|
||||
let mut clients = self.clients.lock().unwrap_or_else(PoisonError::into_inner);
|
||||
if let Some(client) = clients.get(&key) {
|
||||
if let Some(client) = self.lock().get(&key) {
|
||||
return Ok(client.clone());
|
||||
}
|
||||
let client = self.apply(variant, config.client_builder()?).build()?;
|
||||
clients.insert(key, client.clone());
|
||||
Ok(client)
|
||||
Ok(self.lock().entry(key).or_insert(client).clone())
|
||||
}
|
||||
|
||||
fn lock(&self) -> MutexGuard<'_, HashMap<(HttpClientConfig, ClientVariant), reqwest::Client>> {
|
||||
self.clients.lock().unwrap_or_else(PoisonError::into_inner)
|
||||
}
|
||||
|
||||
fn apply(
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ from litellm.llms.base_llm.ocr.transformation import (
|
|||
OCRResponse,
|
||||
parse_ocr_request_format,
|
||||
)
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.types.utils import CustomPricingLiteLLMParams
|
||||
|
|
@ -52,6 +53,11 @@ class _PreparedOCRRequest:
|
|||
litellm_logging_obj: LiteLLMLoggingObj
|
||||
|
||||
|
||||
def _supplied_client(kwargs: Mapping[str, object]) -> HTTPHandler | AsyncHTTPHandler | None:
|
||||
candidate: Final = kwargs.get("client")
|
||||
return candidate if isinstance(candidate, (HTTPHandler, AsyncHTTPHandler)) else None
|
||||
|
||||
|
||||
def _prepare_ocr_request(
|
||||
model: str,
|
||||
document: Mapping[str, object],
|
||||
|
|
@ -238,6 +244,7 @@ async def aocr(
|
|||
api_key=prepared.api_key,
|
||||
api_base=prepared.api_base,
|
||||
custom_llm_provider=prepared.custom_llm_provider,
|
||||
client=_supplied_client(kwargs),
|
||||
aocr=True,
|
||||
headers=prepared.extra_headers,
|
||||
provider_config=prepared.provider_config,
|
||||
|
|
@ -404,6 +411,7 @@ def ocr(
|
|||
api_key=prepared.api_key,
|
||||
api_base=prepared.api_base,
|
||||
custom_llm_provider=prepared.custom_llm_provider,
|
||||
client=_supplied_client(kwargs),
|
||||
aocr=_is_async,
|
||||
headers=prepared.extra_headers,
|
||||
provider_config=prepared.provider_config,
|
||||
|
|
|
|||
|
|
@ -113,6 +113,35 @@ async def test_python_request_response_and_callbacks(
|
|||
assert logger.log_pre_api_call.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("asynchronous", [False, True])
|
||||
async def test_python_uses_the_supplied_client(provider: Mock, asynchronous: bool) -> None:
|
||||
supplied: Final = Mock(return_value=provider.return_value)
|
||||
transport: Final = httpx.MockTransport(supplied)
|
||||
arguments: Final = {
|
||||
"model": "mistral/mistral-ocr-latest",
|
||||
"document": dict(PRICING_DOCUMENT),
|
||||
"api_key": "test-key",
|
||||
"api_base": "https://ocr.test/v1",
|
||||
}
|
||||
|
||||
async def call() -> OCRResponse:
|
||||
if not asynchronous:
|
||||
with httpx.Client(transport=transport) as sync_client:
|
||||
return litellm.ocr(**arguments, client=HTTPHandler(client=sync_client))
|
||||
async with httpx.AsyncClient(transport=transport) as async_client:
|
||||
handler: Final = AsyncHTTPHandler()
|
||||
await handler.client.aclose()
|
||||
handler.client = async_client
|
||||
return await litellm.aocr(**arguments, client=handler)
|
||||
|
||||
response: Final = await call()
|
||||
assert response.pages[0].markdown == "parsed document"
|
||||
assert supplied.call_count == 1
|
||||
assert str(supplied.call_args.args[0].url) == "https://ocr.test/v1/ocr"
|
||||
assert provider.call_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("asynchronous", [False, True])
|
||||
async def test_python_provider_errors_keep_public_exception(provider: Mock, asynchronous: bool) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue