mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Merge 45aa2e406a into f066b01b0a
This commit is contained in:
commit
2b68987b74
4 changed files with 186 additions and 0 deletions
43
litellm/proxy/guardrails/guardrail_hooks/conduct/__init__.py
Normal file
43
litellm/proxy/guardrails/guardrail_hooks/conduct/__init__.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
from litellm.types.guardrails import SupportedGuardrailIntegrations
|
||||
|
||||
from .conduct import ConductGuardrail
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.types.guardrails import Guardrail, LitellmParams
|
||||
|
||||
|
||||
def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail"):
|
||||
"""Initialize the Conduct guardrail from LiteLLM's config block.
|
||||
|
||||
Maps LiteLLM's idiomatic ``api_base`` / ``api_key`` to Conduct's
|
||||
``api_url`` / ``agent_token`` constructor arguments. All other
|
||||
settings pass through unchanged.
|
||||
"""
|
||||
import litellm
|
||||
|
||||
_conduct_callback: Final = ConductGuardrail(
|
||||
api_url=getattr(litellm_params, "api_base", None),
|
||||
agent_token=getattr(litellm_params, "api_key", None),
|
||||
workspace_id=getattr(litellm_params, "workspace_id", None),
|
||||
fail_mode=getattr(litellm_params, "fail_mode", "fail_closed"),
|
||||
tool_name=getattr(litellm_params, "tool_name", "llm_call"),
|
||||
timeout=getattr(litellm_params, "timeout", 8.0),
|
||||
guardrail_name=guardrail.get("guardrail_name", ""),
|
||||
event_hook=litellm_params.mode,
|
||||
default_on=litellm_params.default_on,
|
||||
)
|
||||
litellm.logging_callback_manager.add_litellm_callback(_conduct_callback)
|
||||
|
||||
return _conduct_callback
|
||||
|
||||
|
||||
guardrail_initializer_registry: Final = {
|
||||
SupportedGuardrailIntegrations.CONDUCT.value: initialize_guardrail,
|
||||
}
|
||||
|
||||
|
||||
guardrail_class_registry: Final = {
|
||||
SupportedGuardrailIntegrations.CONDUCT.value: ConductGuardrail,
|
||||
}
|
||||
26
litellm/proxy/guardrails/guardrail_hooks/conduct/conduct.py
Normal file
26
litellm/proxy/guardrails/guardrail_hooks/conduct/conduct.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
"""Conduct Guard as a LiteLLM guardrail.
|
||||
|
||||
Thin re-export. The adapter, response-envelope parser, session-ID chain,
|
||||
and fail-mode logic all live in the `conduct-litellm-guard` PyPI package,
|
||||
which is where issues, versioning, and standalone-user support live.
|
||||
|
||||
Install: `pip install conduct-litellm-guard`
|
||||
Source: https://github.com/sseshachala/conductai/tree/main/packages/conduct-litellm-guard
|
||||
Docs: https://conductai.ai/guard
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from conduct_litellm_guard import ConductGuard as ConductGuardrail
|
||||
from conduct_litellm_guard.guardrail import (
|
||||
ConductGuardBlocked as ConductGuardrailBlocked,
|
||||
)
|
||||
from conduct_litellm_guard.guardrail import GuardDecision
|
||||
except ImportError as _e:
|
||||
raise ImportError(
|
||||
"conduct-litellm-guard is required for the Conduct guardrail. Install with: pip install conduct-litellm-guard"
|
||||
) from _e
|
||||
|
||||
|
||||
__all__ = ["ConductGuardrail", "ConductGuardrailBlocked", "GuardDecision"]
|
||||
|
|
@ -134,6 +134,7 @@ class SupportedGuardrailIntegrations(Enum):
|
|||
HEADROOM = "headroom"
|
||||
COMPRESR = "compresr"
|
||||
STRAIKER = "straiker"
|
||||
CONDUCT = "conduct"
|
||||
|
||||
|
||||
class Role(Enum):
|
||||
|
|
|
|||
116
tests/test_litellm/proxy/guardrails/test_conduct_guardrail.py
Normal file
116
tests/test_litellm/proxy/guardrails/test_conduct_guardrail.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
"""Smoke tests for the Conduct guardrail integration.
|
||||
|
||||
The adapter itself is tested in the ``conduct-litellm-guard`` PyPI
|
||||
package. Here we only verify:
|
||||
* the LiteLLM-tree module imports cleanly when the standalone package
|
||||
is installed
|
||||
* the enum + registry entries are wired
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
# The Conduct guardrail imports its runtime from `conduct-litellm-guard`
|
||||
# on PyPI. When the package is not installed in the CI environment,
|
||||
# skip — the wiring smoke tests only make sense against the real dep.
|
||||
pytest.importorskip(
|
||||
"conduct_litellm_guard",
|
||||
reason="Install `conduct-litellm-guard` to test the Conduct guardrail integration.",
|
||||
)
|
||||
|
||||
|
||||
def test_import_module() -> None:
|
||||
"""The wrapper module imports without side effects."""
|
||||
module = importlib.import_module("litellm.proxy.guardrails.guardrail_hooks.conduct")
|
||||
assert module.ConductGuardrail is not None
|
||||
|
||||
|
||||
def test_class_is_custom_guardrail_subclass() -> None:
|
||||
from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.proxy.guardrails.guardrail_hooks.conduct import ConductGuardrail
|
||||
|
||||
assert issubclass(ConductGuardrail, CustomGuardrail)
|
||||
|
||||
|
||||
def test_enum_value_registered() -> None:
|
||||
from litellm.types.guardrails import SupportedGuardrailIntegrations
|
||||
|
||||
assert SupportedGuardrailIntegrations.CONDUCT.value == "conduct"
|
||||
|
||||
|
||||
def test_registries_populated() -> None:
|
||||
from litellm.proxy.guardrails.guardrail_hooks.conduct import (
|
||||
guardrail_class_registry,
|
||||
guardrail_initializer_registry,
|
||||
)
|
||||
|
||||
assert "conduct" in guardrail_class_registry
|
||||
assert "conduct" in guardrail_initializer_registry
|
||||
|
||||
|
||||
def test_initialize_guardrail_returns_wired_callback(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""``initialize_guardrail`` maps LiteLLM params to Conduct kwargs and
|
||||
registers the callback with ``logging_callback_manager``. This test
|
||||
exercises the full function body so coverage reports don't flag it
|
||||
as dead code."""
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
monkeypatch.setenv("CONDUCT_AGENT_TOKEN", "cond_agt_test_placeholder")
|
||||
|
||||
added_callbacks: list[object] = []
|
||||
fake_manager = SimpleNamespace(
|
||||
add_litellm_callback=lambda cb: added_callbacks.append(cb),
|
||||
)
|
||||
|
||||
import litellm
|
||||
|
||||
monkeypatch.setattr(litellm, "logging_callback_manager", fake_manager)
|
||||
|
||||
from litellm.proxy.guardrails.guardrail_hooks.conduct import (
|
||||
ConductGuardrail,
|
||||
initialize_guardrail,
|
||||
)
|
||||
|
||||
litellm_params = SimpleNamespace(
|
||||
api_base=None,
|
||||
api_key=None,
|
||||
mode="pre_call",
|
||||
default_on=True,
|
||||
)
|
||||
guardrail = MagicMock()
|
||||
guardrail.get.return_value = "conduct-guard"
|
||||
|
||||
callback = initialize_guardrail(litellm_params, guardrail)
|
||||
|
||||
assert isinstance(callback, ConductGuardrail)
|
||||
assert added_callbacks == [callback]
|
||||
|
||||
|
||||
def test_missing_standalone_package_raises_helpful_error(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""When ``conduct-litellm-guard`` is not installed, the import fails
|
||||
with a message pointing users at the ``pip install`` command."""
|
||||
# Ensure the module is re-imported without the standalone package.
|
||||
for name in list(sys.modules):
|
||||
if name.startswith(("conduct_litellm_guard", "litellm.proxy.guardrails.guardrail_hooks.conduct")):
|
||||
monkeypatch.delitem(sys.modules, name, raising=False)
|
||||
|
||||
real_import = __builtins__["__import__"] if isinstance(__builtins__, dict) else __builtins__.__import__
|
||||
|
||||
def _fake_import(name: str, *args: object, **kwargs: object) -> object:
|
||||
if name.startswith("conduct_litellm_guard"):
|
||||
raise ImportError("simulated missing package")
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr("builtins.__import__", _fake_import)
|
||||
|
||||
with pytest.raises(ImportError, match="pip install conduct-litellm-guard"):
|
||||
importlib.import_module("litellm.proxy.guardrails.guardrail_hooks.conduct.conduct")
|
||||
Loading…
Add table
Reference in a new issue