fix(decisions): count proxy /v1/decisions calls in least-busy's in-flight totals

This commit is contained in:
mateo-berri 2026-10-29 09:53:31 -07:00
parent 6ad23ff70d
commit d9357038e8
3 changed files with 44 additions and 4 deletions

View file

@ -557,6 +557,7 @@ def prepare_call(
litellm_params={
"litellm_call_id": kwargs.get("litellm_call_id"),
"api_base": provider_config.get_complete_url(resolved_api_base, canonical_model),
**({} if (model_info := kwargs.get("model_info")) is None else {"model_info": model_info}),
},
custom_llm_provider=provider,
)

View file

@ -6,6 +6,8 @@ import copy
import json
import re
import threading
import uuid
from datetime import datetime
from collections.abc import Mapping, Sequence
from concurrent.futures import wait
from types import MappingProxyType
@ -296,6 +298,43 @@ async def test_router_dispatches_typesafe_decisions_without_api_base(
assert response.answers["sentiment"].choice == "positive"
@pytest.mark.asyncio
async def test_least_busy_frees_a_decisions_deployment_once_a_proxy_logged_call_finishes(
respx_mock: respx.MockRouter,
) -> None:
respx_mock.post("https://api.typesafe.ai/v1/systemone").respond(json=_RESPONSE)
router: Final = litellm.Router(
model_list=[
{
"model_name": "jev",
"litellm_params": {"model": "typesafe/jev-latest", "api_key": "k"},
"model_info": {"id": deployment_id},
}
for deployment_id in ("jev-a", "jev-b")
],
routing_strategy="least-busy",
)
async def call_through_proxy_logging() -> object:
logging_obj, _ = litellm.utils.function_setup(
original_function="adecisions",
rules_obj=litellm.utils.Rules(),
start_time=datetime.now(),
model="jev",
litellm_call_id=str(uuid.uuid4()),
)
response: Final = await router.adecisions(
model="jev",
input="Is this a defect?",
questions=[{"type": "predicate", "name": "is_defect", "instructions": "Is this a defect?"}],
litellm_logging_obj=logging_obj,
)
await _drain_logging_worker()
return response._hidden_params["model_id"]
assert (await call_through_proxy_logging(), await call_through_proxy_logging()) == ("jev-a", "jev-a")
def test_decisions_uses_the_same_wire_contract_for_sync_calls(respx_mock: respx.MockRouter) -> None:
route: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)

View file

@ -236,9 +236,7 @@ class OpenBreakerRedis(SharedRedisCounters):
@pytest.mark.asyncio
async def test_an_open_circuit_breaker_falls_back_without_a_warning_per_request(
caplog: pytest.LogCaptureFixture,
) -> None:
async def test_an_open_circuit_breaker_falls_back_without_a_warning_per_request(caplog: pytest.LogCaptureFixture) -> None:
worker: Final = _worker(OpenBreakerRedis())
with caplog.at_level(logging.DEBUG, logger="LiteLLM Router"):
@ -410,7 +408,9 @@ async def test_router_picks_least_busy_deployment_and_completion_restores_counts
if use_async
else router.get_available_deployment(model=ROUTER_GROUP, messages=None)
)
response: Final = await router.acompletion(model=ROUTER_GROUP, messages=[{"role": "user", "content": "hi"}])
response: Final = await router.acompletion(
model=ROUTER_GROUP, messages=[{"role": "user", "content": "hi"}]
)
for _ in range(10):
await asyncio.sleep(0)
await GLOBAL_LOGGING_WORKER.flush()