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:
Ishaan Jaff 2026-02-19 13:45:59 -08:00 committed by GitHub
parent 18f8a2cee3
commit 779c355f1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 531 additions and 0 deletions

View file

@ -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",

View file

@ -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"

View file

@ -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