glacis 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glacis/__init__.py +62 -1
- glacis/__main__.py +1 -80
- glacis/client.py +60 -31
- glacis/config.py +141 -0
- glacis/controls/__init__.py +232 -0
- glacis/controls/base.py +104 -0
- glacis/controls/jailbreak.py +224 -0
- glacis/controls/pii.py +855 -0
- glacis/crypto.py +70 -1
- glacis/integrations/__init__.py +53 -3
- glacis/integrations/anthropic.py +207 -142
- glacis/integrations/base.py +476 -0
- glacis/integrations/openai.py +156 -121
- glacis/models.py +277 -24
- glacis/storage.py +324 -8
- glacis/verify.py +154 -0
- glacis-0.2.0.dist-info/METADATA +275 -0
- glacis-0.2.0.dist-info/RECORD +21 -0
- glacis/wasm/s3p_core_wasi.wasm +0 -0
- glacis/wasm_runtime.py +0 -533
- glacis-0.1.3.dist-info/METADATA +0 -324
- glacis-0.1.3.dist-info/RECORD +0 -16
- {glacis-0.1.3.dist-info → glacis-0.2.0.dist-info}/WHEEL +0 -0
- {glacis-0.1.3.dist-info → glacis-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GLACIS integration base module.
|
|
3
|
+
|
|
4
|
+
Provides shared functionality for all provider integrations:
|
|
5
|
+
- GlacisBlockedError exception
|
|
6
|
+
- Thread-local receipt storage
|
|
7
|
+
- Evidence retrieval
|
|
8
|
+
- Logger suppression
|
|
9
|
+
- Config and client initialization helpers
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import threading
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from glacis import Glacis
|
|
20
|
+
from glacis.config import GlacisConfig
|
|
21
|
+
from glacis.controls import ControlsRunner
|
|
22
|
+
from glacis.models import (
|
|
23
|
+
AttestReceipt,
|
|
24
|
+
ControlExecution,
|
|
25
|
+
ControlPlaneAttestation,
|
|
26
|
+
JailbreakSummary,
|
|
27
|
+
OfflineAttestReceipt,
|
|
28
|
+
PiiPhiSummary,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Thread-local storage for the last receipt
|
|
33
|
+
_thread_local = threading.local()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class GlacisBlockedError(Exception):
|
|
37
|
+
"""Raised when a control blocks the request."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, message: str, control_type: str, score: Optional[float] = None):
|
|
40
|
+
super().__init__(message)
|
|
41
|
+
self.control_type = control_type
|
|
42
|
+
self.score = score
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_last_receipt() -> Optional[Union["AttestReceipt", "OfflineAttestReceipt"]]:
|
|
46
|
+
"""
|
|
47
|
+
Get the last attestation receipt from the current thread.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The last AttestReceipt or OfflineAttestReceipt, or None if no attestation
|
|
51
|
+
has been made in this thread.
|
|
52
|
+
"""
|
|
53
|
+
return getattr(_thread_local, "last_receipt", None)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def set_last_receipt(receipt: Union["AttestReceipt", "OfflineAttestReceipt"]) -> None:
|
|
57
|
+
"""Store the last receipt in thread-local storage."""
|
|
58
|
+
_thread_local.last_receipt = receipt
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_evidence(attestation_id: str) -> Optional[dict[str, Any]]:
|
|
62
|
+
"""
|
|
63
|
+
Get the full evidence for an attestation by ID.
|
|
64
|
+
|
|
65
|
+
Evidence includes the full input, output, and control_plane_results that
|
|
66
|
+
were attested. This data is stored locally and never sent to GLACIS servers.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
attestation_id: The attestation ID (att_xxx or oatt_xxx)
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Dict with input, output, control_plane_results, and metadata,
|
|
73
|
+
or None if not found
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> receipt = get_last_receipt()
|
|
77
|
+
>>> evidence = get_evidence(receipt.attestation_id)
|
|
78
|
+
>>> print(evidence["input"]["messages"])
|
|
79
|
+
>>> print(evidence["control_plane_results"]["pii_phi"])
|
|
80
|
+
"""
|
|
81
|
+
from glacis.storage import ReceiptStorage
|
|
82
|
+
|
|
83
|
+
storage = ReceiptStorage()
|
|
84
|
+
return storage.get_evidence(attestation_id)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Loggers to suppress for clean customer experience
|
|
88
|
+
NOISY_LOGGERS = [
|
|
89
|
+
"glacis",
|
|
90
|
+
"presidio-analyzer",
|
|
91
|
+
"presidio-anonymizer",
|
|
92
|
+
"presidio_analyzer",
|
|
93
|
+
"presidio_anonymizer",
|
|
94
|
+
"spacy",
|
|
95
|
+
"httpx",
|
|
96
|
+
"httpcore",
|
|
97
|
+
"httpcore.http11",
|
|
98
|
+
"httpcore.connection",
|
|
99
|
+
"transformers",
|
|
100
|
+
"urllib3",
|
|
101
|
+
"urllib3.connectionpool",
|
|
102
|
+
"huggingface_hub",
|
|
103
|
+
"filelock",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def suppress_noisy_loggers(provider_loggers: list[str] | None = None) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Suppress noisy third-party loggers for clean customer experience.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
provider_loggers: Additional provider-specific loggers to suppress
|
|
113
|
+
"""
|
|
114
|
+
loggers = NOISY_LOGGERS.copy()
|
|
115
|
+
if provider_loggers:
|
|
116
|
+
loggers.extend(provider_loggers)
|
|
117
|
+
|
|
118
|
+
for logger_name in loggers:
|
|
119
|
+
logging.getLogger(logger_name).setLevel(logging.WARNING)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def initialize_config(
|
|
123
|
+
config_path: Optional[str],
|
|
124
|
+
redaction: Union[bool, Literal["fast", "full"], None],
|
|
125
|
+
offline: Optional[bool],
|
|
126
|
+
glacis_api_key: Optional[str],
|
|
127
|
+
default_service_id: str,
|
|
128
|
+
service_id: str,
|
|
129
|
+
) -> tuple["GlacisConfig", bool, str]:
|
|
130
|
+
"""
|
|
131
|
+
Initialize and configure Glacis settings.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
config_path: Path to glacis.yaml config file
|
|
135
|
+
redaction: PII redaction mode override
|
|
136
|
+
offline: Offline mode override
|
|
137
|
+
glacis_api_key: Glacis API key (implies online mode if provided)
|
|
138
|
+
default_service_id: Default service ID for this provider
|
|
139
|
+
service_id: User-provided service ID
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Tuple of (config, effective_offline, effective_service_id)
|
|
143
|
+
"""
|
|
144
|
+
from glacis.config import load_config
|
|
145
|
+
|
|
146
|
+
cfg: GlacisConfig = load_config(config_path)
|
|
147
|
+
|
|
148
|
+
# Handle backward-compatible redaction parameter
|
|
149
|
+
if redaction is not None:
|
|
150
|
+
if redaction is True:
|
|
151
|
+
cfg.controls.pii_phi.enabled = True
|
|
152
|
+
cfg.controls.pii_phi.mode = "fast"
|
|
153
|
+
elif redaction is False:
|
|
154
|
+
cfg.controls.pii_phi.enabled = False
|
|
155
|
+
else:
|
|
156
|
+
cfg.controls.pii_phi.enabled = True
|
|
157
|
+
cfg.controls.pii_phi.mode = redaction
|
|
158
|
+
|
|
159
|
+
# Determine offline mode
|
|
160
|
+
if offline is not None:
|
|
161
|
+
effective_offline = offline
|
|
162
|
+
elif glacis_api_key:
|
|
163
|
+
effective_offline = False
|
|
164
|
+
else:
|
|
165
|
+
effective_offline = cfg.attestation.offline
|
|
166
|
+
|
|
167
|
+
# Determine service ID
|
|
168
|
+
effective_service_id = (
|
|
169
|
+
service_id if service_id != default_service_id else cfg.attestation.service_id
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return cfg, effective_offline, effective_service_id
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def create_glacis_client(
|
|
176
|
+
offline: bool,
|
|
177
|
+
signing_seed: Optional[bytes],
|
|
178
|
+
glacis_api_key: Optional[str],
|
|
179
|
+
glacis_base_url: str,
|
|
180
|
+
debug: bool,
|
|
181
|
+
) -> "Glacis":
|
|
182
|
+
"""
|
|
183
|
+
Create a Glacis client (online or offline).
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
offline: Whether to use offline mode
|
|
187
|
+
signing_seed: 32-byte signing seed (required for offline)
|
|
188
|
+
glacis_api_key: API key (required for online)
|
|
189
|
+
glacis_base_url: Base URL for Glacis API
|
|
190
|
+
debug: Enable debug logging
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Configured Glacis client
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
ValueError: If required parameters are missing
|
|
197
|
+
"""
|
|
198
|
+
from glacis import Glacis
|
|
199
|
+
|
|
200
|
+
if offline:
|
|
201
|
+
if not signing_seed:
|
|
202
|
+
raise ValueError("signing_seed is required for offline mode")
|
|
203
|
+
return Glacis(
|
|
204
|
+
mode="offline",
|
|
205
|
+
signing_seed=signing_seed,
|
|
206
|
+
debug=debug,
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
if not glacis_api_key:
|
|
210
|
+
raise ValueError("glacis_api_key is required for online mode")
|
|
211
|
+
return Glacis(
|
|
212
|
+
api_key=glacis_api_key,
|
|
213
|
+
base_url=glacis_base_url,
|
|
214
|
+
debug=debug,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def create_controls_runner(
|
|
219
|
+
cfg: "GlacisConfig",
|
|
220
|
+
debug: bool,
|
|
221
|
+
) -> Optional["ControlsRunner"]:
|
|
222
|
+
"""
|
|
223
|
+
Create controls runner if any control is enabled.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
cfg: Glacis configuration
|
|
227
|
+
debug: Enable debug logging
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
ControlsRunner if any control is enabled, None otherwise
|
|
231
|
+
"""
|
|
232
|
+
if cfg.controls.pii_phi.enabled or cfg.controls.jailbreak.enabled:
|
|
233
|
+
from glacis.controls import ControlsRunner
|
|
234
|
+
return ControlsRunner(cfg.controls, debug=debug)
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def store_evidence(
|
|
239
|
+
receipt: Union["AttestReceipt", "OfflineAttestReceipt"],
|
|
240
|
+
service_id: str,
|
|
241
|
+
operation_type: str,
|
|
242
|
+
input_data: dict[str, Any],
|
|
243
|
+
output_data: dict[str, Any],
|
|
244
|
+
control_plane_results: Optional["ControlPlaneAttestation"],
|
|
245
|
+
metadata: dict[str, Any],
|
|
246
|
+
debug: bool,
|
|
247
|
+
) -> None:
|
|
248
|
+
"""
|
|
249
|
+
Store attestation evidence locally for audit trail.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
receipt: Attestation receipt
|
|
253
|
+
service_id: Service identifier
|
|
254
|
+
operation_type: Type of operation
|
|
255
|
+
input_data: Input payload
|
|
256
|
+
output_data: Output payload
|
|
257
|
+
control_plane_results: Control plane attestation
|
|
258
|
+
metadata: Additional metadata
|
|
259
|
+
debug: Enable debug logging
|
|
260
|
+
"""
|
|
261
|
+
from glacis.models import OfflineAttestReceipt
|
|
262
|
+
from glacis.storage import ReceiptStorage
|
|
263
|
+
|
|
264
|
+
storage = ReceiptStorage()
|
|
265
|
+
attestation_hash = (
|
|
266
|
+
receipt.payload_hash
|
|
267
|
+
if isinstance(receipt, OfflineAttestReceipt)
|
|
268
|
+
else receipt.attestation_hash
|
|
269
|
+
)
|
|
270
|
+
storage.store_evidence(
|
|
271
|
+
attestation_id=receipt.attestation_id,
|
|
272
|
+
attestation_hash=attestation_hash,
|
|
273
|
+
mode="offline" if isinstance(receipt, OfflineAttestReceipt) else "online",
|
|
274
|
+
service_id=service_id,
|
|
275
|
+
operation_type=operation_type,
|
|
276
|
+
timestamp=receipt.timestamp,
|
|
277
|
+
input_data=input_data,
|
|
278
|
+
output_data=output_data,
|
|
279
|
+
control_plane_results=control_plane_results,
|
|
280
|
+
metadata=metadata,
|
|
281
|
+
)
|
|
282
|
+
if debug:
|
|
283
|
+
print(f"[glacis] Attestation created: {receipt.attestation_id}")
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
__all__ = [
|
|
287
|
+
"GlacisBlockedError",
|
|
288
|
+
"get_last_receipt",
|
|
289
|
+
"set_last_receipt",
|
|
290
|
+
"get_evidence",
|
|
291
|
+
"suppress_noisy_loggers",
|
|
292
|
+
"initialize_config",
|
|
293
|
+
"create_glacis_client",
|
|
294
|
+
"create_controls_runner",
|
|
295
|
+
"store_evidence",
|
|
296
|
+
"NOISY_LOGGERS",
|
|
297
|
+
"ControlResultsAccumulator",
|
|
298
|
+
"process_text_for_controls",
|
|
299
|
+
"create_control_plane_attestation_from_accumulator",
|
|
300
|
+
"handle_blocked_request",
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# --- Shared Control Execution Logic ---
|
|
305
|
+
|
|
306
|
+
class ControlResultsAccumulator:
|
|
307
|
+
"""Accumulates results from multiple control execution runs."""
|
|
308
|
+
|
|
309
|
+
def __init__(self) -> None:
|
|
310
|
+
self.pii_summary: Optional["PiiPhiSummary"] = None
|
|
311
|
+
self.jailbreak_summary: Optional["JailbreakSummary"] = None
|
|
312
|
+
self.control_executions: list["ControlExecution"] = []
|
|
313
|
+
self.should_block: bool = False
|
|
314
|
+
|
|
315
|
+
def update(self, results: list[Any]) -> None:
|
|
316
|
+
"""Update accumulator with check results."""
|
|
317
|
+
from glacis.models import ControlExecution, JailbreakSummary, PiiPhiSummary
|
|
318
|
+
|
|
319
|
+
for result in results:
|
|
320
|
+
if result.control_type == "pii" and result.detected:
|
|
321
|
+
if self.pii_summary:
|
|
322
|
+
self.pii_summary.categories = sorted(
|
|
323
|
+
set(self.pii_summary.categories) | set(result.categories)
|
|
324
|
+
)
|
|
325
|
+
self.pii_summary.count += len(result.categories)
|
|
326
|
+
else:
|
|
327
|
+
self.pii_summary = PiiPhiSummary(
|
|
328
|
+
detected=True,
|
|
329
|
+
action="redacted",
|
|
330
|
+
categories=result.categories,
|
|
331
|
+
count=len(result.categories),
|
|
332
|
+
)
|
|
333
|
+
self.control_executions.append(
|
|
334
|
+
ControlExecution(
|
|
335
|
+
id="glacis-pii-redactor",
|
|
336
|
+
type="pii",
|
|
337
|
+
version="0.3.0",
|
|
338
|
+
provider="glacis",
|
|
339
|
+
latency_ms=result.latency_ms,
|
|
340
|
+
status="flag",
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
elif result.control_type == "jailbreak":
|
|
345
|
+
if not self.jailbreak_summary or (result.score or 0) > self.jailbreak_summary.score:
|
|
346
|
+
self.jailbreak_summary = JailbreakSummary(
|
|
347
|
+
detected=result.detected,
|
|
348
|
+
score=result.score or 0.0,
|
|
349
|
+
action=result.action,
|
|
350
|
+
categories=result.categories,
|
|
351
|
+
backend=result.metadata.get("backend", ""),
|
|
352
|
+
)
|
|
353
|
+
self.control_executions.append(
|
|
354
|
+
ControlExecution(
|
|
355
|
+
id="glacis-jailbreak-detector",
|
|
356
|
+
type="jailbreak",
|
|
357
|
+
version="0.3.0",
|
|
358
|
+
provider="glacis",
|
|
359
|
+
latency_ms=result.latency_ms,
|
|
360
|
+
status=result.action if result.detected else "pass",
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
if result.action == "block":
|
|
364
|
+
self.should_block = True
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def process_text_for_controls(
|
|
368
|
+
runner: "ControlsRunner",
|
|
369
|
+
text: str,
|
|
370
|
+
accumulator: ControlResultsAccumulator
|
|
371
|
+
) -> str:
|
|
372
|
+
"""Run controls on text, update accumulator, and return (potentially redacted) text."""
|
|
373
|
+
results = runner.run(text)
|
|
374
|
+
accumulator.update(results)
|
|
375
|
+
final_text = runner.get_final_text(results) or text
|
|
376
|
+
return final_text
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def create_control_plane_attestation_from_accumulator(
|
|
380
|
+
accumulator: ControlResultsAccumulator,
|
|
381
|
+
cfg: "GlacisConfig",
|
|
382
|
+
model: str,
|
|
383
|
+
provider: str,
|
|
384
|
+
endpoint: str,
|
|
385
|
+
) -> Any:
|
|
386
|
+
"""Create ControlPlaneAttestation from accumulated results."""
|
|
387
|
+
from glacis.models import (
|
|
388
|
+
ControlPlaneAttestation,
|
|
389
|
+
Determination,
|
|
390
|
+
ModelInfo,
|
|
391
|
+
PolicyContext,
|
|
392
|
+
PolicyScope,
|
|
393
|
+
SafetyScores,
|
|
394
|
+
SamplingDecision,
|
|
395
|
+
SamplingMetadata,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
action: Literal["forwarded", "redacted", "blocked"]
|
|
399
|
+
trigger: Optional[str]
|
|
400
|
+
if accumulator.pii_summary:
|
|
401
|
+
action, trigger = "redacted", "pii"
|
|
402
|
+
elif accumulator.jailbreak_summary and accumulator.jailbreak_summary.detected:
|
|
403
|
+
action = "blocked" if accumulator.jailbreak_summary.action == "block" else "forwarded"
|
|
404
|
+
trigger = "jailbreak"
|
|
405
|
+
else:
|
|
406
|
+
action, trigger = "forwarded", None
|
|
407
|
+
|
|
408
|
+
return ControlPlaneAttestation(
|
|
409
|
+
policy=PolicyContext(
|
|
410
|
+
id=cfg.policy.id,
|
|
411
|
+
version=cfg.policy.version,
|
|
412
|
+
model=ModelInfo(model_id=model, provider=provider),
|
|
413
|
+
scope=PolicyScope(
|
|
414
|
+
tenant_id=cfg.policy.tenant_id,
|
|
415
|
+
endpoint=endpoint,
|
|
416
|
+
),
|
|
417
|
+
),
|
|
418
|
+
determination=Determination(action=action, trigger=trigger, confidence=1.0),
|
|
419
|
+
controls=accumulator.control_executions,
|
|
420
|
+
safety=SafetyScores(
|
|
421
|
+
overall_risk=accumulator.jailbreak_summary.score
|
|
422
|
+
if accumulator.jailbreak_summary
|
|
423
|
+
else 0.0
|
|
424
|
+
),
|
|
425
|
+
pii_phi=accumulator.pii_summary,
|
|
426
|
+
jailbreak=accumulator.jailbreak_summary,
|
|
427
|
+
sampling=SamplingMetadata(
|
|
428
|
+
level="L0",
|
|
429
|
+
decision=SamplingDecision(sampled=True, reason="forced", rate=1.0),
|
|
430
|
+
),
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def handle_blocked_request(
|
|
435
|
+
glacis_client: "Glacis",
|
|
436
|
+
service_id: str,
|
|
437
|
+
input_data: dict[str, Any],
|
|
438
|
+
control_plane_results: Any,
|
|
439
|
+
provider: str,
|
|
440
|
+
model: str,
|
|
441
|
+
jailbreak_score: float,
|
|
442
|
+
debug: bool,
|
|
443
|
+
) -> None:
|
|
444
|
+
"""Attest a blocked request and raise GlacisBlockedError."""
|
|
445
|
+
output_data = {"blocked": True, "reason": "jailbreak_detected"}
|
|
446
|
+
metadata = {"provider": provider, "model": model, "blocked": str(True)}
|
|
447
|
+
|
|
448
|
+
try:
|
|
449
|
+
receipt = glacis_client.attest(
|
|
450
|
+
service_id=service_id,
|
|
451
|
+
operation_type="completion",
|
|
452
|
+
input=input_data,
|
|
453
|
+
output=output_data,
|
|
454
|
+
metadata=metadata,
|
|
455
|
+
control_plane_results=control_plane_results,
|
|
456
|
+
)
|
|
457
|
+
set_last_receipt(receipt)
|
|
458
|
+
store_evidence(
|
|
459
|
+
receipt=receipt,
|
|
460
|
+
service_id=service_id,
|
|
461
|
+
operation_type="completion",
|
|
462
|
+
input_data=input_data,
|
|
463
|
+
output_data=output_data,
|
|
464
|
+
control_plane_results=control_plane_results,
|
|
465
|
+
metadata=metadata,
|
|
466
|
+
debug=debug,
|
|
467
|
+
)
|
|
468
|
+
except Exception as e:
|
|
469
|
+
if debug:
|
|
470
|
+
print(f"[glacis] Attestation failed: {e}")
|
|
471
|
+
|
|
472
|
+
raise GlacisBlockedError(
|
|
473
|
+
f"Jailbreak detected (score={jailbreak_score:.2f})",
|
|
474
|
+
control_type="jailbreak",
|
|
475
|
+
score=jailbreak_score,
|
|
476
|
+
)
|