mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
refactor: shim becomes a pure alias, hooks now on plugin's ConductGuard
Plugin conduct-litellm-guard 0.2.3 ships SUPPORTED_EVENT_HOOKS + get_supported_event_hooks on ConductGuard directly. The upstream shim's subclass wrapper is now redundant — dropping it clears every strict-rule budget gate (ruff-strict / test-quality / type-discipline / basedpyright) in one pass. Changes: - conduct.py: subclass removed; ConductGuardrail is now an alias for the plugin's ConductGuard (no dynamic base class, no reassignment, no # type: ignore). raise_if_missing_package helper unchanged. - test file: _IMPORT_ERROR → _import_error rename to satisfy reportConstantRedefinition (basedpyright treats SCREAMING_CASE as constant). Also drops unused sys import. - Pin bumped to conduct-litellm-guard>=0.2.3 in the module docstring. Verified all four LiteLLM gate scripts locally against upstream/litellm_internal_staging: ruff_strict_gate OK test_quality_gate OK type_discipline_gate OK type_check_gate OK
This commit is contained in:
parent
7f796fce9d
commit
46eac90614
2 changed files with 29 additions and 68 deletions
|
|
@ -1,83 +1,46 @@
|
|||
"""Conduct Guard as a LiteLLM guardrail.
|
||||
|
||||
Thin adapter over the ``conduct-litellm-guard`` PyPI package. The
|
||||
adapter, response-envelope parser, session-ID chain, fail-mode logic,
|
||||
and the ``guard_check_prompt`` wire client all live in that package —
|
||||
this file only wires the base runtime into LiteLLM's ``CustomGuardrail``
|
||||
contract.
|
||||
Pure alias for the ``conduct-litellm-guard`` PyPI package. The
|
||||
``ConductGuard`` class ships with ``SUPPORTED_EVENT_HOOKS`` +
|
||||
``get_supported_event_hooks`` since plugin 0.2.3, so this file no
|
||||
longer needs a subclass wrapper — keeps LiteLLM's type-discipline /
|
||||
basedpyright / test-quality budget gates satisfied.
|
||||
|
||||
Install: ``pip install "conduct-litellm-guard>=0.2.2"``
|
||||
Install: ``pip install "conduct-litellm-guard>=0.2.3"``
|
||||
Source: https://github.com/sseshachala/conductai/tree/main/packages/conduct-litellm-guard
|
||||
Docs: https://conductai.ai/guard
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.types.guardrails import GuardrailEventHooks
|
||||
|
||||
_IMPORT_ERROR_MESSAGE = (
|
||||
_import_error_message = (
|
||||
"conduct-litellm-guard is required for the Conduct guardrail. "
|
||||
'Install it with: pip install "conduct-litellm-guard>=0.2.2"'
|
||||
'Install it with: pip install "conduct-litellm-guard>=0.2.3"'
|
||||
)
|
||||
|
||||
|
||||
# ── Base plugin import — deferred to init-time via raise_if_missing_package ──
|
||||
# Raising ImportError at module load caused the guardrail-hook auto-loader to
|
||||
# treat a missing ``conduct-litellm-guard`` as "hook unavailable" and silently
|
||||
# drop the registration. Users saw configs load with no guardrail active and
|
||||
# no error message. Instead we fall back to ``CustomGuardrail`` at module load
|
||||
# so the class hierarchy stays intact; ``initialize_guardrail`` (in
|
||||
# ``__init__.py``) calls :func:`raise_if_missing_package` before construction
|
||||
# so the friendly error surfaces when actionable.
|
||||
# ── Base plugin import ─────────────────────────────────────────────────
|
||||
# Raising ImportError at module load caused the guardrail-hook auto-loader
|
||||
# to treat a missing ``conduct-litellm-guard`` as "hook unavailable" and
|
||||
# silently drop the registration. Users saw configs load with no guardrail
|
||||
# active and no error message. Instead we surface the friendly error at
|
||||
# config-load time from :func:`raise_if_missing_package` — called by
|
||||
# ``initialize_guardrail`` before construction.
|
||||
# (cursor[bot] finding on BerriAI/litellm#38143.)
|
||||
|
||||
try:
|
||||
from conduct_litellm_guard import ConductGuard as _BaseConductGuard
|
||||
from conduct_litellm_guard import ConductGuard as ConductGuardrail
|
||||
from conduct_litellm_guard.guardrail import (
|
||||
ConductGuardBlocked as ConductGuardrailBlocked,
|
||||
)
|
||||
from conduct_litellm_guard.guardrail import GuardDecision
|
||||
|
||||
_IMPORT_ERROR: ImportError | None = None
|
||||
_import_error: ImportError | None = None
|
||||
except ImportError as _import_err:
|
||||
_BaseConductGuard = CustomGuardrail
|
||||
ConductGuardrail = None
|
||||
ConductGuardrailBlocked = None
|
||||
GuardDecision = None
|
||||
_IMPORT_ERROR = _import_err
|
||||
|
||||
|
||||
class ConductGuardrail(_BaseConductGuard):
|
||||
"""LiteLLM adapter over ``conduct_litellm_guard.ConductGuard``.
|
||||
|
||||
Inherits its ``__init__`` from the base runtime when the standalone
|
||||
package is installed; otherwise inherits from ``CustomGuardrail``
|
||||
and ``initialize_guardrail`` short-circuits with a friendly error
|
||||
before this class is ever constructed.
|
||||
|
||||
Only two additions on this side:
|
||||
- ``SUPPORTED_EVENT_HOOKS`` / ``get_supported_event_hooks`` so
|
||||
LiteLLM validates configs against modes we actually implement.
|
||||
"""
|
||||
|
||||
# Advertised event hooks. The plugin currently runs at pre_call
|
||||
# (input rail) — a policy block short-circuits before the model
|
||||
# sees the prompt, which is the semantic LiteLLM users expect for
|
||||
# a "guardrail". ``during_call`` / ``post_call`` support lands
|
||||
# with plugin 0.3.x once the underlying Conduct response gate is
|
||||
# wired through ``guard_check_response``. Advertising only
|
||||
# pre_call today prevents silent bypass of ``during_call``
|
||||
# configurations — see veria-ai finding on BerriAI/litellm#38143.
|
||||
SUPPORTED_EVENT_HOOKS: ClassVar[tuple[GuardrailEventHooks, ...]] = (GuardrailEventHooks.pre_call,)
|
||||
|
||||
@classmethod
|
||||
def get_supported_event_hooks(cls) -> list[GuardrailEventHooks]:
|
||||
"""LiteLLM calls this during config validation to reject
|
||||
unsupported ``mode:`` values (e.g. ``during_call`` while only
|
||||
pre_call is implemented)."""
|
||||
return list(cls.SUPPORTED_EVENT_HOOKS)
|
||||
_import_error = _import_err
|
||||
|
||||
|
||||
def raise_if_missing_package() -> None:
|
||||
|
|
@ -86,8 +49,8 @@ def raise_if_missing_package() -> None:
|
|||
Surfaces the friendly ``pip install`` error at the actionable moment
|
||||
(config load) rather than silently dropping the hook at module load.
|
||||
"""
|
||||
if _IMPORT_ERROR is not None:
|
||||
raise ImportError(_IMPORT_ERROR_MESSAGE) from _IMPORT_ERROR
|
||||
if _import_error is not None:
|
||||
raise ImportError(_import_error_message) from _import_error
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ here we only verify the LiteLLM-tree wiring:
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
|
@ -59,15 +58,14 @@ def test_registries_populated() -> None:
|
|||
|
||||
def test_only_pre_call_event_hook_advertised() -> None:
|
||||
"""Regression for veria-ai finding on #38143 —
|
||||
``during_call`` mode was silently accepted but never evaluated
|
||||
because ``async_moderation_hook`` was not overridden. We only
|
||||
advertise pre_call today so LiteLLM validates configs against
|
||||
supported hooks and rejects unsupported modes."""
|
||||
``during_call`` mode was silently accepted but never evaluated.
|
||||
Since plugin 0.2.3 the supported-hooks contract lives on
|
||||
``ConductGuard`` in the plugin package itself; the LiteLLM shim
|
||||
is a pure alias, so we verify against the alias."""
|
||||
from litellm.proxy.guardrails.guardrail_hooks.conduct import ConductGuardrail
|
||||
from litellm.types.guardrails import GuardrailEventHooks
|
||||
|
||||
hooks = ConductGuardrail.get_supported_event_hooks()
|
||||
assert hooks == [GuardrailEventHooks.pre_call]
|
||||
assert hooks == ["pre_call"]
|
||||
|
||||
|
||||
def test_initialize_guardrail_returns_wired_callback(
|
||||
|
|
@ -190,13 +188,13 @@ def test_missing_standalone_package_raises_at_initialize() -> None:
|
|||
``initialize_guardrail`` calls at config-load time when actionable."""
|
||||
from litellm.proxy.guardrails.guardrail_hooks.conduct import conduct as _mod
|
||||
|
||||
original_error = _mod._IMPORT_ERROR
|
||||
original_error = _mod._import_error
|
||||
try:
|
||||
_mod._IMPORT_ERROR = ImportError("simulated missing package")
|
||||
_mod._import_error = ImportError("simulated missing package")
|
||||
with pytest.raises(ImportError, match="pip install"):
|
||||
_mod.raise_if_missing_package()
|
||||
finally:
|
||||
_mod._IMPORT_ERROR = original_error
|
||||
_mod._import_error = original_error
|
||||
|
||||
|
||||
def test_raise_if_missing_package_is_noop_when_present() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue