Skip to content

Commit 7fd1ef2

Browse files
author
Paweł Kędzia
committed
**Move random provider acquisition to FirstAvailableStrategyI and clean up FirstAvailableStrategy; update default balance strategy to first_available**
1 parent 5ba0379 commit 7fd1ef2

File tree

3 files changed

+82
-78
lines changed

3 files changed

+82
-78
lines changed

llm_router_api/base/lb/first_available.py

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -225,79 +225,3 @@ def put_provider(
225225
raise
226226

227227
provider.pop("__chosen_field", None)
228-
229-
def _try_acquire_random_provider(
230-
self, redis_key: str, providers: List[Dict]
231-
) -> Optional[Dict]:
232-
"""
233-
Attempt to lock a provider chosen at random.
234-
235-
The method works in three stages:
236-
237-
1. **Shuffle** – a shallow copy of ``providers`` is shuffled so that each
238-
provider has an equal probability of being tried first. The original
239-
list is left untouched.
240-
2. **Atomic acquisition** – each shuffled provider is passed to the
241-
``_acquire_script`` Lua script which atomically sets the corresponding
242-
Redis hash field to ``'true'`` *only if* it is currently ``'false'`` or
243-
missing. The first provider for which the script returns ``1`` is
244-
considered successfully acquired.
245-
3. **Fallback** – if none of the providers can be locked (e.g., all are
246-
currently in use), the method falls back to the *first* provider in the
247-
original ``providers`` list, marks its ``"__chosen_field"`` for
248-
consistency, and returns it. This fallback mirrors the behaviour of
249-
the non‑random acquisition path and ensures the caller always receives
250-
a provider dictionary (or ``None`` when ``providers`` is empty).
251-
252-
Parameters
253-
----------
254-
redis_key : str
255-
The Redis hash key associated with the model (e.g., ``model:<name>``).
256-
providers : List[Dict]
257-
A list of provider configuration dictionaries. Each dictionary must
258-
contain sufficient information for :meth:`_provider_field` to generate
259-
a unique field name within the Redis hash.
260-
261-
Returns
262-
-------
263-
Optional[Dict]
264-
The selected provider dictionary with an additional ``"__chosen_field"``
265-
entry indicating the Redis hash field that was locked. Returns ``None``
266-
only when the input ``providers`` list is empty.
267-
268-
Raises
269-
------
270-
Exception
271-
Propagates any unexpected exceptions raised by the Lua script execution;
272-
callers may catch these to implement retry or logging logic.
273-
274-
Notes
275-
-----
276-
* The random selection is *non‑deterministic* on each call; however, the
277-
fallback to the first provider ensures deterministic behaviour when
278-
all providers are currently busy.
279-
* The method does **not** block; it returns immediately after trying all
280-
shuffled providers.
281-
"""
282-
shuffled = providers[:]
283-
random.shuffle(shuffled)
284-
for provider in shuffled:
285-
provider_field = self._provider_field(provider)
286-
try:
287-
ok = int(
288-
self._acquire_script(keys=[redis_key], args=[provider_field])
289-
)
290-
if ok == 1:
291-
provider["__chosen_field"] = provider_field
292-
return provider
293-
except Exception:
294-
continue
295-
return None
296-
297-
def _get_active_providers(
298-
self, model_name: str, providers: List[Dict]
299-
) -> List[Dict]:
300-
active_providers = self._monitor.get_providers(
301-
model_name=model_name, only_active=True
302-
)
303-
return active_providers

llm_router_api/base/lb/first_available_i.py

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import random
12
import time
23
import logging
34
from abc import ABC
@@ -186,6 +187,85 @@ def _init_flag(self, model_name: str) -> str:
186187
"""
187188
return f"{self._get_redis_key(model_name)}:initialized"
188189

190+
# ----------------------------------------------------------------------
191+
# Helper methods
192+
# ----------------------------------------------------------------------
193+
def _try_acquire_random_provider(
194+
self, redis_key: str, providers: List[Dict]
195+
) -> Optional[Dict]:
196+
"""
197+
Attempt to lock a provider chosen at random.
198+
199+
The method works in three stages:
200+
201+
1. **Shuffle** – a shallow copy of ``providers`` is shuffled so that each
202+
provider has an equal probability of being tried first. The original
203+
list is left untouched.
204+
2. **Atomic acquisition** – each shuffled provider is passed to the
205+
``_acquire_script`` Lua script which atomically sets the corresponding
206+
Redis hash field to ``'true'`` *only if* it is currently ``'false'`` or
207+
missing. The first provider for which the script returns ``1`` is
208+
considered successfully acquired.
209+
3. **Fallback** – if none of the providers can be locked (e.g., all are
210+
currently in use), the method falls back to the *first* provider in the
211+
original ``providers`` list, marks its ``"__chosen_field"`` for
212+
consistency, and returns it. This fallback mirrors the behaviour of
213+
the non‑random acquisition path and ensures the caller always receives
214+
a provider dictionary (or ``None`` when ``providers`` is empty).
215+
216+
Parameters
217+
----------
218+
redis_key : str
219+
The Redis hash key associated with the model (e.g., ``model:<name>``).
220+
providers : List[Dict]
221+
A list of provider configuration dictionaries. Each dictionary must
222+
contain sufficient information for :meth:`_provider_field` to generate
223+
a unique field name within the Redis hash.
224+
225+
Returns
226+
-------
227+
Optional[Dict]
228+
The selected provider dictionary with an additional ``"__chosen_field"``
229+
entry indicating the Redis hash field that was locked. Returns ``None``
230+
only when the input ``providers`` list is empty.
231+
232+
Raises
233+
------
234+
Exception
235+
Propagates any unexpected exceptions raised by the Lua script execution;
236+
callers may catch these to implement retry or logging logic.
237+
238+
Notes
239+
-----
240+
* The random selection is *non‑deterministic* on each call; however, the
241+
fallback to the first provider ensures deterministic behaviour when
242+
all providers are currently busy.
243+
* The method does **not** block; it returns immediately after trying all
244+
shuffled providers.
245+
"""
246+
shuffled = providers[:]
247+
random.shuffle(shuffled)
248+
for provider in shuffled:
249+
provider_field = self._provider_field(provider)
250+
try:
251+
ok = int(
252+
self._acquire_script(keys=[redis_key], args=[provider_field])
253+
)
254+
if ok == 1:
255+
provider["__chosen_field"] = provider_field
256+
return provider
257+
except Exception:
258+
continue
259+
return None
260+
261+
def _get_active_providers(
262+
self, model_name: str, providers: List[Dict]
263+
) -> List[Dict]:
264+
active_providers = self._monitor.get_providers(
265+
model_name=model_name, only_active=True
266+
)
267+
return active_providers
268+
189269
def _initialize_providers(self, model_name: str, providers: List[Dict]) -> None:
190270
"""
191271
Ensure that the provider lock fields for *model_name* exist in Redis.
@@ -282,6 +362,6 @@ def _print_provider_status(self, redis_key: str, providers: List[Dict]) -> None:
282362
status = hash_data.get(field, "false")
283363
icon = "🔴" if status == "true" else "🟢"
284364
# Show a short identifier for the provider (fallback to field)
285-
provider_id = provider.get("id") or provider.get("name") or field
365+
provider_id = provider.get("id") or provider.get("api_host") or field
286366
print(f"{icon} {provider_id:<30} [{field}]")
287367
print("-" * 40)

run-rest-api-gunicorn.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
export LLM_ROUTER_SERVER_TYPE=${LLM_ROUTER_SERVER_TYPE:-gunicorn}
1414
export LLM_ROUTER_SERVER_PORT=${LLM_ROUTER_SERVER_PORT:-8080}
1515
export LLM_ROUTER_SERVER_HOST=${LLM_ROUTER_SERVER_HOST:-"0.0.0.0"}
16-
export LLM_ROUTER_BALANCE_STRATEGY=${LLM_ROUTER_BALANCE_STRATEGY:-"first_available_optim"}
16+
export LLM_ROUTER_BALANCE_STRATEGY=${LLM_ROUTER_BALANCE_STRATEGY:-"first_available"}
1717
export LLM_ROUTER_REDIS_HOST=${LLM_ROUTER_REDIS_HOST:-"192.168.100.67"}
1818
export LLM_ROUTER_REDIS_PORT=${LLM_ROUTER_REDIS_PORT:-6379}
1919
export LLM_ROUTER_SERVER_WORKERS_COUNT=${LLM_ROUTER_SERVER_WORKERS_COUNT:-4}

0 commit comments

Comments
 (0)