mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
refactor(router): replace hand-rolled warn-once registry with lru_cache and drop the fragile args dirty-check
This commit is contained in:
parent
c816a6aac6
commit
c81baa0587
3 changed files with 39 additions and 29 deletions
|
|
@ -342,6 +342,19 @@ def _replay_live_router_model_cost() -> None:
|
|||
|
||||
set_live_deployment_replay(_replay_live_router_model_cost)
|
||||
|
||||
_EMPTY_MAPPING: Final[Mapping[str, object]] = MappingProxyType({})
|
||||
|
||||
|
||||
@lru_cache(maxsize=512)
|
||||
def _warn_model_group_strategy_once(model: str, kind: str, fingerprint: str, message: str) -> None:
|
||||
"""
|
||||
Reports a model-group strategy misconfiguration once per
|
||||
(model, problem kind, offending config): distinct problem kinds warn
|
||||
independently, a changed config re-warns, and lru_cache makes the
|
||||
check-and-record atomic under concurrent dispatch.
|
||||
"""
|
||||
verbose_router_logger.warning("%s (model_group=%s, kind=%s, config=%s)", message, model, kind, fingerprint)
|
||||
|
||||
|
||||
class Router:
|
||||
model_names: set = set()
|
||||
|
|
@ -715,7 +728,6 @@ class Router:
|
|||
self._init_routing_groups(self._routing_groups_input)
|
||||
self._override_selectors: dict[str, Any] = {}
|
||||
self._override_selectors_lock = threading.Lock()
|
||||
self._warned_model_group_strategy_keys: set[tuple[str, str, str]] = set() # mutable-ok: warn-once registry
|
||||
self.access_groups = None
|
||||
## USAGE TRACKING ##
|
||||
if isinstance(litellm._async_success_callback, list):
|
||||
|
|
@ -1127,20 +1139,6 @@ class Router:
|
|||
)
|
||||
return self._override_selectors[strategy]
|
||||
|
||||
_EMPTY_MAPPING: Final[Mapping[str, object]] = MappingProxyType({})
|
||||
|
||||
def _warn_model_group_strategy_once(self, model: str, kind: str, fingerprint: str, message: str) -> None:
|
||||
"""
|
||||
Reports a model-group strategy misconfiguration once per
|
||||
(model, problem kind, offending config). Distinct problem kinds warn
|
||||
independently, and a changed config re-warns.
|
||||
"""
|
||||
warn_key: Final = (model, kind, fingerprint)
|
||||
if warn_key in self._warned_model_group_strategy_keys:
|
||||
return
|
||||
self._warned_model_group_strategy_keys.add(warn_key)
|
||||
verbose_router_logger.warning("%s", message)
|
||||
|
||||
def _deployment_strategy_entry(self, idx: int) -> tuple[str | None, Mapping[str, object]] | None:
|
||||
"""
|
||||
The (normalized strategy, args) a deployment declares via
|
||||
|
|
@ -1148,12 +1146,12 @@ class Router:
|
|||
counts as unset, so a PATCH (which cannot delete a model_info key) can
|
||||
still clear the field.
|
||||
"""
|
||||
info: Final = self.model_list[idx].get("model_info") or self._EMPTY_MAPPING
|
||||
info: Final = self.model_list[idx].get("model_info") or _EMPTY_MAPPING
|
||||
raw: Final = info.get("routing_strategy")
|
||||
if not raw:
|
||||
return None
|
||||
normalized: Final = self._normalize_strategy(raw) if isinstance(raw, (str, RoutingStrategy)) else None
|
||||
return normalized, info.get("routing_strategy_args") or self._EMPTY_MAPPING
|
||||
return normalized, info.get("routing_strategy_args") or _EMPTY_MAPPING
|
||||
|
||||
def _get_model_group_strategy_config(self, model: str) -> tuple[str, Mapping[str, object]] | None:
|
||||
"""
|
||||
|
|
@ -1170,7 +1168,7 @@ class Router:
|
|||
return None
|
||||
ordered: Final = sorted(
|
||||
indices,
|
||||
key=lambda idx: str((self.model_list[idx].get("model_info") or self._EMPTY_MAPPING).get("id") or ""),
|
||||
key=lambda idx: str((self.model_list[idx].get("model_info") or _EMPTY_MAPPING).get("id") or ""),
|
||||
)
|
||||
configured: Final = tuple(
|
||||
entry for idx in ordered if (entry := self._deployment_strategy_entry(idx)) is not None
|
||||
|
|
@ -1185,7 +1183,7 @@ class Router:
|
|||
has_invalid: Final = len(valid) < len(configured)
|
||||
has_conflict: Final = len(frozenset(strategy for strategy, _ in valid)) > 1
|
||||
if has_invalid or has_conflict:
|
||||
self._warn_model_group_strategy_once(
|
||||
_warn_model_group_strategy_once(
|
||||
model,
|
||||
"config",
|
||||
",".join(str(strategy) for strategy, _ in configured),
|
||||
|
|
@ -1251,7 +1249,7 @@ class Router:
|
|||
self._evict_stale_model_group_selectors()
|
||||
built: Final = self._build_model_group_selector(strategy, args)
|
||||
if built is None:
|
||||
self._warn_model_group_strategy_once(
|
||||
_warn_model_group_strategy_once(
|
||||
model,
|
||||
"args",
|
||||
selector_key,
|
||||
|
|
|
|||
|
|
@ -16,10 +16,18 @@ sys.path.insert(0, os.path.abspath("../.."))
|
|||
|
||||
import litellm
|
||||
from litellm import Router
|
||||
from litellm.router import _warn_model_group_strategy_once
|
||||
from litellm.router_strategy.lowest_cost import LowestCostLoggingHandler
|
||||
from litellm.router_strategy.lowest_latency import LowestLatencyLoggingHandler
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clear_warn_once_cache():
|
||||
_warn_model_group_strategy_once.cache_clear()
|
||||
yield
|
||||
_warn_model_group_strategy_once.cache_clear()
|
||||
|
||||
|
||||
def _deployment(model_name, model, deployment_id, model_info=None):
|
||||
return {
|
||||
"model_name": model_name,
|
||||
|
|
@ -240,7 +248,7 @@ def test_config_and_args_warnings_fire_independently(caplog):
|
|||
assert len(args_warnings) == 1
|
||||
|
||||
|
||||
def test_changed_bad_args_warn_again():
|
||||
def test_changed_bad_args_warn_again(caplog):
|
||||
router = _build_router(
|
||||
[
|
||||
_deployment(
|
||||
|
|
@ -251,13 +259,15 @@ def test_changed_bad_args_warn_again():
|
|||
)
|
||||
]
|
||||
)
|
||||
router._get_routing_context("quality")
|
||||
assert len(router._warned_model_group_strategy_keys) == 1
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM Router"):
|
||||
router._get_routing_context("quality")
|
||||
router._get_routing_context("quality")
|
||||
|
||||
for idx in router.model_name_to_deployment_indices["quality"]:
|
||||
router.model_list[idx]["model_info"]["routing_strategy_args"] = {"ttl": "still-bogus"}
|
||||
router._get_routing_context("quality")
|
||||
assert len(router._warned_model_group_strategy_keys) == 2
|
||||
for idx in router.model_name_to_deployment_indices["quality"]:
|
||||
router.model_list[idx]["model_info"]["routing_strategy_args"] = {"ttl": "still-bogus"}
|
||||
router._get_routing_context("quality")
|
||||
args_warnings = [r for r in caplog.records if "cannot initialize strategy" in r.getMessage()]
|
||||
assert len(args_warnings) == 2
|
||||
|
||||
|
||||
def test_simple_shuffle_with_args_keeps_shuffle_semantics(caplog):
|
||||
|
|
|
|||
|
|
@ -442,8 +442,10 @@ export default function ModelInfoView({
|
|||
}
|
||||
if (values.routing_strategy_args !== undefined) {
|
||||
const parsedArgs = values.routing_strategy_args ? JSON.parse(values.routing_strategy_args) : {};
|
||||
const storedArgs = localModelData.model_info?.routing_strategy_args ?? {};
|
||||
if (JSON.stringify(parsedArgs) !== JSON.stringify(storedArgs)) {
|
||||
if (
|
||||
hasRoutingStrategyArgs(parsedArgs) ||
|
||||
hasRoutingStrategyArgs(localModelData.model_info?.routing_strategy_args)
|
||||
) {
|
||||
updatedModelInfo = {
|
||||
...updatedModelInfo,
|
||||
routing_strategy_args: parsedArgs,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue