chore: rename GraySwan to Gray Swan (#15771)

This commit is contained in:
YutaSaito 2025-10-22 07:18:55 +09:00 committed by GitHub
parent d4aadda692
commit 39641e7e68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 38 deletions

View file

@ -1,9 +1,9 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# GraySwan Cygnal Guardrail
# Gray Swan Cygnal Guardrail
Use [GraySwan Cygnal](https://docs.grayswan.ai/cygnal/monitor-requests) to continuously monitor conversations for policy violations, indirect prompt injection (IPI), jailbreak attempts, and other safety risks.
Use [Gray Swan Cygnal](https://docs.grayswan.ai/cygnal/monitor-requests) to continuously monitor conversations for policy violations, indirect prompt injection (IPI), jailbreak attempts, and other safety risks.
Cygnal returns a `violation` score between `0` and `1` (higher means more likely to violate policy), plus metadata such as violated rule indices, mutation detection, and IPI flags. LiteLLM can automatically block or monitor requests based on this signal.
@ -13,7 +13,7 @@ Cygnal returns a `violation` score between `0` and `1` (higher means more likely
### 1. Obtain Credentials
1. Create a GraySwan account and generate a Cygnal API key.
1. Create a Gray Swan account and generate a Cygnal API key.
2. Configure environment variables for the LiteLLM proxy host:
```bash
@ -22,7 +22,7 @@ export GRAYSWAN_API_KEY="your-grayswan-key"
### 2. Configure `config.yaml`
Add a guardrail entry that references the GraySwan integration. Below is a balanced example that monitors both input and output but only blocks once the violation score reaches the configured threshold.
Add a guardrail entry that references the Gray Swan integration. Below is a balanced example that monitors both input and output but only blocks once the violation score reaches the configured threshold.
```yaml
model_list:
@ -63,7 +63,7 @@ litellm --config config.yaml --port 4000
## Choosing Guardrail Modes
GraySwan can run during `pre_call`, `during_call`, and `post_call` stages. Combine modes based on your latency and coverage requirements.
Gray Swan can run during `pre_call`, `during_call`, and `post_call` stages. Combine modes based on your latency and coverage requirements.
| Mode | When it Runs | Protects | Typical Use Case |
|--------------|-------------------|-----------------------|------------------|
@ -138,10 +138,10 @@ Provides the strongest enforcement by inspecting both prompts and responses.
| Parameter | Type | Description |
|---------------------------------------|-----------------|-------------|
| `api_key` | string | GraySwan Cygnal API key. Reads from `GRAYSWAN_API_KEY` if omitted. |
| `api_key` | string | Gray Swan Cygnal API key. Reads from `GRAYSWAN_API_KEY` if omitted. |
| `mode` | string or list | Guardrail stages (`pre_call`, `during_call`, `post_call`). |
| `optional_params.on_flagged_action` | string | `monitor` (log only) or `block` (raise `HTTPException`). |
| `.optional_params.violation_threshold`| number (0-1) | Scores at or above this value are considered violations. |
| `optional_params.reasoning_mode` | string | `off`, `hybrid`, or `thinking`. Enables Cygnals reasoning capabilities. |
| `optional_params.categories` | object | Map of custom category names to descriptions. |
| `optional_params.policy_id` | string | GraySwan policy identifier. |
| `optional_params.policy_id` | string | Gray Swan policy identifier. |

View file

@ -1,4 +1,4 @@
"""GraySwan Cygnal guardrail integration for LiteLLM."""
"""Gray Swan Cygnal guardrail integration for LiteLLM."""
from typing import TYPE_CHECKING
@ -21,7 +21,7 @@ def initialize_guardrail(
guardrail_name = guardrail.get("guardrail_name")
if not guardrail_name:
raise ValueError("GraySwan guardrail requires a guardrail_name")
raise ValueError("Gray Swan guardrail requires a guardrail_name")
optional_params = getattr(litellm_params, "optional_params", None)

View file

@ -1,4 +1,4 @@
"""GraySwan Cygnal guardrail integration."""
"""Gray Swan Cygnal guardrail integration."""
import os
from typing import Any, Dict, Literal, Optional, Union
@ -24,16 +24,16 @@ from litellm.types.utils import LLMResponseTypes
class GraySwanGuardrailMissingSecrets(Exception):
"""Raised when the GraySwan API key is missing."""
"""Raised when the Gray Swan API key is missing."""
class GraySwanGuardrailAPIError(Exception):
"""Raised when the GraySwan API returns an error."""
"""Raised when the Gray Swan API returns an error."""
class GraySwanGuardrail(CustomGuardrail):
"""
Guardrail that calls GraySwan's Cygnal monitoring endpoint.
Guardrail that calls Gray Swan's Cygnal monitoring endpoint.
see: https://docs.grayswan.ai/cygnal/monitor-requests
"""
@ -63,7 +63,7 @@ class GraySwanGuardrail(CustomGuardrail):
api_key_value = api_key or os.getenv("GRAYSWAN_API_KEY")
if not api_key_value:
raise GraySwanGuardrailMissingSecrets(
"GraySwan API key missing. Set `GRAYSWAN_API_KEY` or pass `api_key`."
"Gray Swan API key missing. Set `GRAYSWAN_API_KEY` or pass `api_key`."
)
self.api_key: str = api_key_value
@ -77,7 +77,7 @@ class GraySwanGuardrail(CustomGuardrail):
else:
if action:
verbose_proxy_logger.warning(
"GraySwan Guardrail: Unsupported on_flagged_action '%s', defaulting to '%s'.",
"Gray Swan Guardrail: Unsupported on_flagged_action '%s', defaulting to '%s'.",
action,
self.DEFAULT_ON_FLAGGED_ACTION,
)
@ -131,11 +131,11 @@ class GraySwanGuardrail(CustomGuardrail):
):
return data
verbose_proxy_logger.debug("GraySwan Guardrail: pre-call hook triggered")
verbose_proxy_logger.debug("Gray Swan Guardrail: pre-call hook triggered")
messages = data.get("messages")
if not messages:
verbose_proxy_logger.debug("GraySwan Guardrail: No messages in data")
verbose_proxy_logger.debug("Gray Swan Guardrail: No messages in data")
return data
dynamic_body = self.get_guardrail_dynamic_request_body_params(data) or {}
@ -143,7 +143,7 @@ class GraySwanGuardrail(CustomGuardrail):
payload = self._prepare_payload(messages, dynamic_body)
if payload is None:
verbose_proxy_logger.debug(
"GraySwan Guardrail: no content to scan; skipping request"
"Gray Swan Guardrail: no content to scan; skipping request"
)
return data
@ -181,7 +181,7 @@ class GraySwanGuardrail(CustomGuardrail):
messages = data.get("messages")
if not messages:
verbose_proxy_logger.debug("GraySwan Guardrail: No messages in data")
verbose_proxy_logger.debug("Gray Swan Guardrail: No messages in data")
return data
dynamic_body = self.get_guardrail_dynamic_request_body_params(data) or {}
@ -189,7 +189,7 @@ class GraySwanGuardrail(CustomGuardrail):
payload = self._prepare_payload(messages, dynamic_body)
if payload is None:
verbose_proxy_logger.debug(
"GraySwan Guardrail: no content to scan; skipping request"
"Gray Swan Guardrail: no content to scan; skipping request"
)
return data
@ -227,7 +227,7 @@ class GraySwanGuardrail(CustomGuardrail):
if not response_messages:
verbose_proxy_logger.debug(
"GraySwan Guardrail: no response messages detected; skipping post-call scan"
"Gray Swan Guardrail: no response messages detected; skipping post-call scan"
)
return response
@ -236,7 +236,7 @@ class GraySwanGuardrail(CustomGuardrail):
payload = self._prepare_payload(response_messages, dynamic_body)
if payload is None:
verbose_proxy_logger.debug(
"GraySwan Guardrail: no content to scan; skipping request"
"Gray Swan Guardrail: no content to scan; skipping request"
)
return response
@ -263,13 +263,13 @@ class GraySwanGuardrail(CustomGuardrail):
response.raise_for_status()
result = response.json()
verbose_proxy_logger.debug(
"GraySwan Guardrail: monitor response %s", safe_dumps(result)
"Gray Swan Guardrail: monitor response %s", safe_dumps(result)
)
except HTTPException:
raise
except Exception as exc: # pragma: no cover - depends on HTTP client behaviour
verbose_proxy_logger.exception(
"GraySwan Guardrail: API request failed: %s", exc
"Gray Swan Guardrail: API request failed: %s", exc
)
raise GraySwanGuardrailAPIError(str(exc)) from exc
@ -315,14 +315,14 @@ class GraySwanGuardrail(CustomGuardrail):
flagged = violation_score >= self.violation_threshold
if not flagged:
verbose_proxy_logger.debug(
"GraySwan Guardrail: request passed (score=%s, rules=%s)",
"Gray Swan Guardrail: request passed (score=%s, rules=%s)",
violation_score,
violated_rules,
)
return
verbose_proxy_logger.warning(
"GraySwan Guardrail: violation score %.3f exceeds threshold %.3f",
"Gray Swan Guardrail: violation score %.3f exceeds threshold %.3f",
violation_score,
self.violation_threshold,
)
@ -331,7 +331,7 @@ class GraySwanGuardrail(CustomGuardrail):
raise HTTPException(
status_code=400,
detail={
"error": "Blocked by GraySwan Guardrail",
"error": "Blocked by Gray Swan Guardrail",
"violation": violation_score,
"violated_rules": violated_rules,
"mutation": mutation_detected,
@ -351,7 +351,7 @@ class GraySwanGuardrail(CustomGuardrail):
if normalised in self.SUPPORTED_REASONING_MODES:
return normalised
verbose_proxy_logger.warning(
"GraySwan Guardrail: ignoring unsupported reasoning_mode '%s'",
"Gray Swan Guardrail: ignoring unsupported reasoning_mode '%s'",
candidate,
)
return None

View file

@ -1,4 +1,4 @@
"""GraySwan guardrail configuration models."""
"""Gray Swan guardrail configuration models."""
from typing import Dict, Optional
@ -8,7 +8,7 @@ from .base import GuardrailConfigModel
class GraySwanGuardrailConfigModelOptionalParams(BaseModel):
"""Optional parameters for the GraySwan guardrail."""
"""Optional parameters for the Gray Swan guardrail."""
on_flagged_action: Optional[str] = Field(
default="monitor",
@ -18,36 +18,36 @@ class GraySwanGuardrailConfigModelOptionalParams(BaseModel):
default=0.5,
ge=0.0,
le=1.0,
description="Threshold between 0 and 1 at which GraySwan violations trigger the configured action.",
description="Threshold between 0 and 1 at which Gray Swan violations trigger the configured action.",
)
reasoning_mode: Optional[str] = Field(
default=None,
description="GraySwan reasoning mode override. Accepted values: 'off', 'hybrid', 'thinking'.",
description="Gray Swan reasoning mode override. Accepted values: 'off', 'hybrid', 'thinking'.",
)
policy_id: Optional[str] = Field(
default=None,
description="GraySwan policy identifier to apply during monitoring.",
description="Gray Swan policy identifier to apply during monitoring.",
)
categories: Optional[Dict[str, str]] = Field(
default=None,
description="Default GraySwan category definitions to send with each request.",
description="Default Gray Swan category definitions to send with each request.",
)
class GraySwanGuardrailConfigModel(
GuardrailConfigModel[GraySwanGuardrailConfigModelOptionalParams]
):
"""Configuration parameters for the GraySwan guardrail."""
"""Configuration parameters for the Gray Swan guardrail."""
api_key: Optional[str] = Field(
default=None,
description="API key for GraySwan. Reads from the `GRAYSWAN_API_KEY` environment variable when omitted.",
description="API key for Gray Swan. Reads from the `GRAYSWAN_API_KEY` environment variable when omitted.",
)
api_base: Optional[str] = Field(
default=None,
description="Override for the GraySwan API base URL. Defaults to https://api.grayswan.ai and can be set via `GRAYSWAN_API_BASE`.",
description="Override for the Gray Swan API base URL. Defaults to https://api.grayswan.ai and can be set via `GRAYSWAN_API_BASE`.",
)
@staticmethod
def ui_friendly_name() -> str:
return "GraySwan Guardrail"
return "Gray Swan Guardrail"