This commit is contained in:
Sudhi Seshachala 2026-08-26 11:08:35 -07:00 committed by GitHub
commit 2b68987b74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 186 additions and 0 deletions

View 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,
}

View 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"]

View file

@ -134,6 +134,7 @@ class SupportedGuardrailIntegrations(Enum):
HEADROOM = "headroom"
COMPRESR = "compresr"
STRAIKER = "straiker"
CONDUCT = "conduct"
class Role(Enum):

View 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")