From a75e75ae1a8af517337daf20244635a00976f431 Mon Sep 17 00:00:00 2001 From: oroxenberg Date: Sun, 26 Oct 2025 13:08:52 -0400 Subject: [PATCH] feat(lasso): Upgrade to Lasso API v3 and fix ULID generation (#15941) * 1. add v3 classify 2. add new classifix for masking 3. support same id for the conversation for pre and post working with duplicates * clean code, remove some debug and run tests * update liter errors * improvment for Code Organization, httpx Error Handling Specificity, Logging Improvements and Type * transfer test test_lasso_guard_config to the new location * Fix type hints and linting errors in lasso.py - Add type: ignore for httpx module when None - Fix return type issues in _handle_classification and _handle_masking - Ensure masked_messages is not None before passing to _apply_masking_to_model_response - Convert LassoResponse to dict for _log_masking_applied call * feat(lasso): Upgrade to Lasso API v3 and fix ULID generation - Update Lasso API endpoints from v2 to v3 (/gateway/v3/classify) - Update masking endpoints from v1 to v3 (/gateway/v3/classifix) - Fix ULID generation: use ulid.new() instead of ULID() constructor - Resolve MemoryView error that occurred with incorrect ULID usage Tested with real proxy server and verified: - Malicious content (jailbreak) properly blocked - Safe content passes through guardrail - PII detection and masking works correctly - No ULID generation errors * docs(lasso): Add ulid-py>=1.1.0 dependency prerequisite Add Prerequisites section documenting the required ulid-py package (version 1.1.0 or higher) for Lasso guardrail conversation tracking. * update docs with the right api_key format --- .../docs/proxy/guardrails/lasso_security.md | 12 +++++++++++- .../proxy/guardrails/guardrail_hooks/lasso/lasso.py | 13 +++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/my-website/docs/proxy/guardrails/lasso_security.md b/docs/my-website/docs/proxy/guardrails/lasso_security.md index b16749af54..21528790af 100644 --- a/docs/my-website/docs/proxy/guardrails/lasso_security.md +++ b/docs/my-website/docs/proxy/guardrails/lasso_security.md @@ -6,6 +6,16 @@ import TabItem from '@theme/TabItem'; Use [Lasso Security](https://www.lasso.security/) to protect your LLM applications from prompt injection attacks, harmful content generation, and other security threats through comprehensive input and output validation. +## Prerequisites + +The Lasso guardrail requires the `ulid-py` package (version 1.1.0 or higher) for generating unique conversation identifiers: + +```shell +pip install ulid-py>=1.1.0 +``` + +This package is used to create lexicographically sortable identifiers for tracking conversations and sessions in the Lasso Security platform. + ## Quick Start ### 1. Define Guardrails on your LiteLLM config.yaml @@ -25,7 +35,7 @@ guardrails: guardrail: lasso mode: "pre_call" api_key: os.environ/LASSO_API_KEY - api_base: "https://server.lasso.security/gateway/v3/classify" # Optional: defaults to v3 endpoint + api_base: "https://server.lasso.security" - guardrail_name: "lasso-post-guard" litellm_params: guardrail: lasso diff --git a/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py b/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py index d0f0752037..99d2b82400 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py +++ b/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py @@ -10,10 +10,11 @@ import uuid from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Type, Union, TypedDict try: - from ulid import ULID + import ulid ULID_AVAILABLE = True except ImportError: + ulid = None # type: ignore ULID_AVAILABLE = False try: @@ -114,8 +115,8 @@ class LassoGuardrail(CustomGuardrail): Generate a ULID (Universally Unique Lexicographically Sortable Identifier). Falls back to UUID if ULID library is not available. """ - if ULID_AVAILABLE: - return str(ULID()) # type: ignore + if ULID_AVAILABLE and ulid is not None: + return str(ulid.new()) # type: ignore else: verbose_proxy_logger.debug("ULID library not available, using UUID") return str(uuid.uuid4()) @@ -222,7 +223,7 @@ class LassoGuardrail(CustomGuardrail): if self.mask: headers = self._prepare_headers(response_data) payload = self._prepare_payload(response_messages, "COMPLETION", response_data) - api_url = f"{self.api_base}/gateway/v1/classifix" + api_url = f"{self.api_base}/gateway/v3/classifix" try: lasso_response = await self._call_lasso_api(headers=headers, payload=payload, api_url=api_url) @@ -370,7 +371,7 @@ class LassoGuardrail(CustomGuardrail): try: headers = self._prepare_headers(data, cache) payload = self._prepare_payload(messages, message_type, data, cache) - api_url = f"{self.api_base}/gateway/v1/classifix" + api_url = f"{self.api_base}/gateway/v3/classifix" response = await self._call_lasso_api(headers=headers, payload=payload, api_url=api_url) self._process_lasso_response(response) @@ -513,7 +514,7 @@ class LassoGuardrail(CustomGuardrail): api_url: Optional[str] = None, ) -> LassoResponse: """Call the Lasso API and return the response.""" - url = api_url or f"{self.api_base}/gateway/v2/classify" + url = api_url or f"{self.api_base}/gateway/v3/classify" verbose_proxy_logger.debug(f"Calling Lasso API with messageType: {payload.get('messageType')}") response = await self.async_handler.post( url=url,