mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
feat: add airline off-topic restriction policy template (#21607)
* fix aviation safety topic filter: remove overly broad exceptions, add cockpit access block words * fix airline brand protection filter: add identifier words, competitor/ops block words, tighten exceptions * feat: add airline off-topic restriction policy template * feat: register airline off-topic restriction in policy templates backup * test: add tests for airline off-topic restriction policy template * fix: remove overly broad block words (explain, recommend, suggest, best) to reduce false positives * test: add false-positive tests for recommend/explain airline queries
This commit is contained in:
parent
18f8a2cee3
commit
779c355f1e
3 changed files with 531 additions and 0 deletions
|
|
@ -1844,6 +1844,51 @@
|
|||
"Security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "airline-off-topic-restriction",
|
||||
"title": "Airline Off-Topic Restriction",
|
||||
"description": "Restricts an airline chatbot to airline-related topics only. Blocks off-topic questions about news, sports, coding, politics, entertainment, finance, recipes, homework, and general knowledge using keyword-based detection with no additional LLM calls.",
|
||||
"icon": "ShieldExclamationIcon",
|
||||
"iconColor": "text-orange-500",
|
||||
"iconBg": "bg-orange-50",
|
||||
"guardrails": [
|
||||
"airline-off-topic-filter"
|
||||
],
|
||||
"complexity": "Medium",
|
||||
"guardrailDefinitions": [
|
||||
{
|
||||
"guardrail_name": "airline-off-topic-filter",
|
||||
"litellm_params": {
|
||||
"guardrail": "litellm_content_filter",
|
||||
"mode": "pre_call",
|
||||
"categories": [
|
||||
{
|
||||
"category": "airline_off_topic_restriction",
|
||||
"category_file": "litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/policy_templates/airline_off_topic_restriction.yaml",
|
||||
"enabled": true,
|
||||
"action": "BLOCK",
|
||||
"severity_threshold": "medium"
|
||||
}
|
||||
]
|
||||
},
|
||||
"guardrail_info": {
|
||||
"description": "Blocks off-topic questions unrelated to airline services (news, sports, coding, politics, entertainment, finance, recipes, etc.)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"templateData": {
|
||||
"policy_name": "airline-off-topic-restriction",
|
||||
"description": "Restricts chatbot to airline-related topics. Blocks off-topic questions using keyword matching with no extra LLM calls.",
|
||||
"guardrails_add": [
|
||||
"airline-off-topic-filter"
|
||||
],
|
||||
"guardrails_remove": []
|
||||
},
|
||||
"tags": [
|
||||
"Aviation",
|
||||
"Topic Restriction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "uae-regulatory-compliance",
|
||||
"title": "UAE Regulatory Compliance",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,270 @@
|
|||
# Airline Off-Topic Restriction
|
||||
# Blocks questions unrelated to airline services (news, sports, coding, politics, etc.)
|
||||
# Uses conditional matching: identifier_word + block_word in same sentence = BLOCK
|
||||
# Plus always_block_keywords for unambiguous off-topic phrases
|
||||
category_name: "airline_off_topic_restriction"
|
||||
description: "Blocks off-topic questions unrelated to airline services"
|
||||
default_action: "BLOCK"
|
||||
|
||||
# OFF-TOPIC DOMAIN SIGNALS
|
||||
# These words indicate the user is asking about a non-airline topic.
|
||||
# They only trigger a block when paired with a block_word in the same sentence.
|
||||
identifier_words:
|
||||
# News & current events
|
||||
- "news"
|
||||
- "headlines"
|
||||
- "breaking"
|
||||
- "journalism"
|
||||
- "reporter"
|
||||
# Sports
|
||||
- "sports"
|
||||
- "football"
|
||||
- "soccer"
|
||||
- "basketball"
|
||||
- "baseball"
|
||||
- "cricket"
|
||||
- "tennis"
|
||||
- "championship"
|
||||
- "playoffs"
|
||||
- "tournament"
|
||||
- "league"
|
||||
- "FIFA"
|
||||
- "NBA"
|
||||
- "NFL"
|
||||
# Technology & coding
|
||||
- "code"
|
||||
- "coding"
|
||||
- "programming"
|
||||
- "python"
|
||||
- "javascript"
|
||||
- "software"
|
||||
- "algorithm"
|
||||
- "database"
|
||||
- "API"
|
||||
- "machine learning"
|
||||
- "AI gateway"
|
||||
- "blockchain"
|
||||
- "cryptocurrency"
|
||||
- "bitcoin"
|
||||
- "ethereum"
|
||||
# Entertainment
|
||||
- "movie"
|
||||
- "Netflix"
|
||||
- "TV show"
|
||||
- "series"
|
||||
- "album"
|
||||
- "song"
|
||||
- "lyrics"
|
||||
- "celebrity"
|
||||
- "actor"
|
||||
- "actress"
|
||||
# Politics & government
|
||||
- "election"
|
||||
- "president"
|
||||
- "prime minister"
|
||||
- "congress"
|
||||
- "parliament"
|
||||
- "political party"
|
||||
- "senator"
|
||||
- "governor"
|
||||
- "democrat"
|
||||
- "republican"
|
||||
# Finance & investing
|
||||
- "stock market"
|
||||
- "stock price"
|
||||
- "invest"
|
||||
- "trading"
|
||||
- "forex"
|
||||
- "mutual fund"
|
||||
- "portfolio"
|
||||
# Food & cooking
|
||||
- "recipe"
|
||||
- "cooking"
|
||||
- "restaurant"
|
||||
- "cuisine"
|
||||
- "ingredient"
|
||||
# Education & homework
|
||||
- "homework"
|
||||
- "equation"
|
||||
- "calculus"
|
||||
- "algebra"
|
||||
- "physics"
|
||||
- "chemistry"
|
||||
- "biology"
|
||||
- "history lesson"
|
||||
# Health & medical (non-travel)
|
||||
- "diagnosis"
|
||||
- "symptom"
|
||||
- "treatment"
|
||||
- "prescription"
|
||||
- "surgery"
|
||||
# Real estate
|
||||
- "real estate"
|
||||
- "mortgage"
|
||||
- "apartment"
|
||||
- "house price"
|
||||
# Dating & relationships
|
||||
- "dating"
|
||||
- "relationship advice"
|
||||
- "break up"
|
||||
- "tinder"
|
||||
# Gaming
|
||||
- "video game"
|
||||
- "gaming"
|
||||
- "playstation"
|
||||
- "xbox"
|
||||
- "fortnite"
|
||||
- "minecraft"
|
||||
|
||||
# CONTEXTUAL TRIGGERS
|
||||
# When combined with an identifier_word in the same sentence, triggers a block.
|
||||
additional_block_words:
|
||||
# Action/query words that confirm off-topic intent
|
||||
- "today"
|
||||
- "latest"
|
||||
- "score"
|
||||
- "won"
|
||||
- "winner"
|
||||
- "lost"
|
||||
- "write"
|
||||
- "build"
|
||||
- "create"
|
||||
- "develop"
|
||||
- "debug"
|
||||
- "fix"
|
||||
- "top"
|
||||
- "favorite"
|
||||
- "watch"
|
||||
- "listen"
|
||||
- "play"
|
||||
- "download"
|
||||
- "install"
|
||||
- "price"
|
||||
- "cost"
|
||||
- "buy"
|
||||
- "sell"
|
||||
- "vote"
|
||||
- "voted"
|
||||
- "opinion"
|
||||
- "who won"
|
||||
- "make"
|
||||
- "how to"
|
||||
- "tutorial"
|
||||
- "learn"
|
||||
- "teach"
|
||||
- "solve"
|
||||
- "calculate"
|
||||
- "convert"
|
||||
- "translate"
|
||||
|
||||
# ALWAYS BLOCK - Unambiguous off-topic phrases (blocked regardless of context)
|
||||
always_block_keywords:
|
||||
# News queries
|
||||
- keyword: "what's in the news"
|
||||
severity: "high"
|
||||
- keyword: "what is in the news"
|
||||
severity: "high"
|
||||
- keyword: "latest headlines"
|
||||
severity: "high"
|
||||
- keyword: "what happened in the world"
|
||||
severity: "high"
|
||||
# Jokes & fun
|
||||
- keyword: "tell me a joke"
|
||||
severity: "high"
|
||||
- keyword: "tell me a story"
|
||||
severity: "high"
|
||||
- keyword: "tell me a fun fact"
|
||||
severity: "high"
|
||||
- keyword: "tell me something interesting"
|
||||
severity: "high"
|
||||
# Coding requests
|
||||
- keyword: "write me code"
|
||||
severity: "high"
|
||||
- keyword: "write a script"
|
||||
severity: "high"
|
||||
- keyword: "write a program"
|
||||
severity: "high"
|
||||
- keyword: "help me code"
|
||||
severity: "high"
|
||||
- keyword: "fix my code"
|
||||
severity: "high"
|
||||
- keyword: "debug my code"
|
||||
severity: "high"
|
||||
# General knowledge
|
||||
- keyword: "capital of"
|
||||
severity: "high"
|
||||
- keyword: "who invented"
|
||||
severity: "high"
|
||||
- keyword: "how tall is"
|
||||
severity: "high"
|
||||
- keyword: "how old is"
|
||||
severity: "high"
|
||||
- keyword: "what year did"
|
||||
severity: "high"
|
||||
- keyword: "who is the president"
|
||||
severity: "high"
|
||||
# Math & homework
|
||||
- keyword: "solve this equation"
|
||||
severity: "high"
|
||||
- keyword: "what is 2+2"
|
||||
severity: "high"
|
||||
- keyword: "help me with my homework"
|
||||
severity: "high"
|
||||
# Recipes
|
||||
- keyword: "recipe for"
|
||||
severity: "high"
|
||||
- keyword: "how to cook"
|
||||
severity: "high"
|
||||
- keyword: "how to bake"
|
||||
severity: "high"
|
||||
# Relationship advice
|
||||
- keyword: "relationship advice"
|
||||
severity: "high"
|
||||
- keyword: "should I break up"
|
||||
severity: "high"
|
||||
- keyword: "dating advice"
|
||||
severity: "high"
|
||||
# AI / tech queries
|
||||
- keyword: "what is an AI gateway"
|
||||
severity: "high"
|
||||
- keyword: "explain machine learning"
|
||||
severity: "high"
|
||||
- keyword: "what is blockchain"
|
||||
severity: "high"
|
||||
- keyword: "what is cryptocurrency"
|
||||
severity: "high"
|
||||
|
||||
# EXCEPTIONS - Airline-adjacent contexts that should NOT be blocked
|
||||
exceptions:
|
||||
- "in-flight entertainment"
|
||||
- "flight entertainment"
|
||||
- "in-flight movie"
|
||||
- "airport news"
|
||||
- "travel news"
|
||||
- "airline news"
|
||||
- "flight news"
|
||||
- "aviation news"
|
||||
- "airport restaurant"
|
||||
- "airport lounge"
|
||||
- "travel recommend"
|
||||
- "destination recommend"
|
||||
- "flight price"
|
||||
- "ticket price"
|
||||
- "fare price"
|
||||
- "baggage cost"
|
||||
- "upgrade cost"
|
||||
- "booking cost"
|
||||
- "seat recommend"
|
||||
- "recommend seat"
|
||||
- "recommend flight"
|
||||
- "suggest flight"
|
||||
- "suggest seat"
|
||||
- "suggest upgrade"
|
||||
- "best seat"
|
||||
- "best flight"
|
||||
- "best fare"
|
||||
- "flight movie"
|
||||
- "explain my"
|
||||
- "explain the"
|
||||
- "explain flight"
|
||||
- "explain booking"
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
"""
|
||||
Tests for the airline off-topic restriction policy template.
|
||||
|
||||
Verifies that off-topic messages are blocked and on-topic/conversational messages pass.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../../")
|
||||
) # Adds the parent directory to the system path
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import (
|
||||
ContentFilterGuardrail,
|
||||
)
|
||||
|
||||
POLICY_TEMPLATE_PATH = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"../../../../../../litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/policy_templates/airline_off_topic_restriction.yaml",
|
||||
)
|
||||
|
||||
|
||||
def _make_guardrail():
|
||||
"""Create a ContentFilterGuardrail with the airline off-topic restriction loaded."""
|
||||
return ContentFilterGuardrail(
|
||||
guardrail_name="test-airline-off-topic",
|
||||
categories=[
|
||||
{
|
||||
"category": "airline_off_topic_restriction",
|
||||
"category_file": POLICY_TEMPLATE_PATH,
|
||||
"enabled": True,
|
||||
"action": "BLOCK",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class TestAirlineOffTopicRestriction:
|
||||
"""Test the airline off-topic restriction policy template."""
|
||||
|
||||
def test_on_topic_flight_booking(self):
|
||||
"""Airline booking questions should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
# Should not raise
|
||||
result = guardrail._filter_single_text("I want to book a flight to Dubai")
|
||||
assert result == "I want to book a flight to Dubai"
|
||||
|
||||
def test_on_topic_baggage(self):
|
||||
"""Baggage questions should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("What is the baggage allowance for economy?")
|
||||
assert result == "What is the baggage allowance for economy?"
|
||||
|
||||
def test_on_topic_checkin(self):
|
||||
"""Check-in questions should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("How do I check in online?")
|
||||
assert "check in" in result
|
||||
|
||||
def test_on_topic_delay(self):
|
||||
"""Flight delay questions should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("My flight is delayed, what are my options?")
|
||||
assert "delayed" in result
|
||||
|
||||
def test_conversational_hello(self):
|
||||
"""Greetings should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Hello")
|
||||
assert result == "Hello"
|
||||
|
||||
def test_conversational_thanks(self):
|
||||
"""Thank you should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Thank you for your help")
|
||||
assert "Thank you" in result
|
||||
|
||||
def test_conversational_help(self):
|
||||
"""Help requests should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Can you help me?")
|
||||
assert "help" in result
|
||||
|
||||
def test_conversational_yes_no(self):
|
||||
"""Simple yes/no should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Yes")
|
||||
assert result == "Yes"
|
||||
|
||||
def test_off_topic_news_always_block(self):
|
||||
"""News questions should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("What's in the news today?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_joke_always_block(self):
|
||||
"""Joke requests should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Tell me a joke")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_coding_always_block(self):
|
||||
"""Coding requests should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Write me code in python")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_ai_gateway_always_block(self):
|
||||
"""AI gateway questions should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("What is an AI gateway?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_capital_always_block(self):
|
||||
"""General knowledge questions should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("What is the capital of France?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_sports_conditional(self):
|
||||
"""Sports questions should be blocked via conditional matching."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Who won the football game today?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_recipe_always_block(self):
|
||||
"""Recipe questions should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Give me a recipe for pasta")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_movie_conditional(self):
|
||||
"""Movie questions with a block word should be blocked."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("What is the top movie to watch on Netflix?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_on_topic_recommend_seat(self):
|
||||
"""Airline recommendation questions should pass (not false-positive)."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Can you recommend the best seat?")
|
||||
assert "recommend" in result.lower()
|
||||
|
||||
def test_on_topic_explain_booking(self):
|
||||
"""Explain questions about airline topics should pass."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Can you explain my booking details?")
|
||||
assert "explain" in result.lower()
|
||||
|
||||
def test_off_topic_stock_conditional(self):
|
||||
"""Stock market questions should be blocked via conditional matching."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("What is the stock price of Apple today?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_homework_always_block(self):
|
||||
"""Homework requests should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Help me with my homework")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_relationship_always_block(self):
|
||||
"""Relationship advice should be blocked via always_block_keywords."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Can you give me relationship advice?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_exception_inflight_entertainment(self):
|
||||
"""In-flight entertainment questions should pass (exception)."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text(
|
||||
"What movies are available on the in-flight entertainment?"
|
||||
)
|
||||
assert "in-flight entertainment" in result.lower()
|
||||
|
||||
def test_exception_flight_price(self):
|
||||
"""Flight price questions should pass (exception)."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("What is the flight price to London?")
|
||||
assert "flight price" in result.lower()
|
||||
|
||||
def test_exception_travel_news(self):
|
||||
"""Travel news should pass (exception)."""
|
||||
guardrail = _make_guardrail()
|
||||
result = guardrail._filter_single_text("Any travel news I should know about?")
|
||||
assert "travel news" in result.lower()
|
||||
|
||||
def test_off_topic_president_always_block(self):
|
||||
"""Political questions should be blocked."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("Who is the president of the United States?")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
def test_off_topic_blockchain_always_block(self):
|
||||
"""Blockchain questions should be blocked."""
|
||||
guardrail = _make_guardrail()
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
guardrail._filter_single_text("What is blockchain technology?")
|
||||
assert exc_info.value.status_code == 403
|
||||
Loading…
Add table
Reference in a new issue