mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
refactor(proxy): swap the classifier registry atomically on reload, rename the dual-form resolver parameter
Rebinding the module attribute replaces clear-then-update, so an in-flight request validating mid-reload sees the old set or the new one and never an empty window, matching how vector_store_registry swaps. plugin_path becomes plugin_reference since the resolver accepts a registry name or a dotted path. Also drops a leftover no-op line in the reload test
This commit is contained in:
parent
9a3e784d23
commit
95f6653698
2 changed files with 11 additions and 12 deletions
|
|
@ -4049,7 +4049,7 @@ def resolve_complexity_router_plugins(
|
|||
classifier_plugin_path: Final = complexity_router_config.get("classifier_plugin")
|
||||
if isinstance(classifier_plugin_path, str):
|
||||
resolved_classifier: Final = resolve_classifier_plugin(
|
||||
plugin_path=classifier_plugin_path,
|
||||
plugin_reference=classifier_plugin_path,
|
||||
config_file_path=config_file_path,
|
||||
source_label=f"complexity_router_config.classifier_plugin on model {model_name!r}",
|
||||
)
|
||||
|
|
@ -4079,7 +4079,7 @@ def pin_complexity_router_model_id(model: dict) -> None: # mutable-ok: out-para
|
|||
|
||||
|
||||
def resolve_classifier_plugin(
|
||||
plugin_path: str,
|
||||
plugin_reference: str,
|
||||
config_file_path: str | None,
|
||||
source_label: str,
|
||||
) -> ClassifierPlugin:
|
||||
|
|
@ -4089,15 +4089,15 @@ def resolve_classifier_plugin(
|
|||
sync `def classify` passes the runtime_checkable isinstance and would only fail on the
|
||||
first classified request, so reject it here where the error names the config key.
|
||||
"""
|
||||
registered: Final = litellm.classifier_plugin_registry.get(plugin_path)
|
||||
registered: Final = litellm.classifier_plugin_registry.get(plugin_reference)
|
||||
if registered is not None:
|
||||
return registered
|
||||
resolved: Final = get_instance_fn(value=plugin_path, config_file_path=config_file_path)
|
||||
resolved: Final = get_instance_fn(value=plugin_reference, config_file_path=config_file_path)
|
||||
if not isinstance(resolved, ClassifierPlugin) or not inspect.iscoroutinefunction(
|
||||
getattr(resolved, "classify", None)
|
||||
):
|
||||
raise ValueError(
|
||||
f"{source_label} entry {plugin_path!r} resolved to {resolved!r}, which does not "
|
||||
f"{source_label} entry {plugin_reference!r} resolved to {resolved!r}, which does not "
|
||||
"implement the ClassifierPlugin interface (an async `classify(context)` method). Fix "
|
||||
"the referenced module before starting the proxy."
|
||||
)
|
||||
|
|
@ -5323,7 +5323,7 @@ class ProxyConfig:
|
|||
(
|
||||
str(plugin_name),
|
||||
resolve_classifier_plugin(
|
||||
plugin_path=plugin_path,
|
||||
plugin_reference=plugin_path,
|
||||
config_file_path=config_file_path,
|
||||
source_label=f"classifier_plugins.{plugin_name}",
|
||||
),
|
||||
|
|
@ -5332,10 +5332,10 @@ class ProxyConfig:
|
|||
)
|
||||
# Replace, never merge: a reload that drops a name, empties the block, or removes it
|
||||
# entirely must evict the stale entries, or a deleted plugin stays selectable until
|
||||
# the next restart. Resolution runs before the clear, so a module broken at reload
|
||||
# time keeps the old registry intact.
|
||||
litellm.classifier_plugin_registry.clear()
|
||||
litellm.classifier_plugin_registry.update(resolved_classifier_entries)
|
||||
# the next restart. Rebinding swaps the mapping atomically, so an in-flight request
|
||||
# validating mid-reload sees the old set or the new one, never an empty window, and
|
||||
# a module broken at reload time keeps the old registry intact.
|
||||
litellm.classifier_plugin_registry = dict(resolved_classifier_entries) # mutable-ok: the registry global's type
|
||||
|
||||
## MODEL LIST
|
||||
model_list: Final = config.get("model_list", None)
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ def test_resolve_classifier_plugin_prefers_the_registry_over_dotted_import(monke
|
|||
instance = _Classifier()
|
||||
monkeypatch.setitem(litellm.classifier_plugin_registry, "tier-by-team", instance)
|
||||
resolved = resolve_classifier_plugin(
|
||||
plugin_path="tier-by-team", config_file_path=None, source_label="classifier_plugins.tier-by-team"
|
||||
plugin_reference="tier-by-team", config_file_path=None, source_label="classifier_plugins.tier-by-team"
|
||||
)
|
||||
assert resolved is instance
|
||||
|
||||
|
|
@ -280,7 +280,6 @@ def test_classifier_plugins_config_key_replaces_the_registry_on_reload(monkeypat
|
|||
" stale-name: reg_classifier.instance\n"
|
||||
)
|
||||
monkeypatch.setattr(litellm, "classifier_plugin_registry", {}, raising=True)
|
||||
asyncio.get_event_loop_policy()
|
||||
proxy_config = ProxyConfig()
|
||||
asyncio.run(proxy_config.load_config(router=None, config_file_path=str(config_path)))
|
||||
assert set(litellm.classifier_plugin_registry) == {"stale-name"}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue