litellm/ui/litellm-dashboard/scripts/generate_compliance_prompts.py
Ishaan Jaff 08520a9ed7
feat: add insults content filter + topic blocking compliance UI (#21729)
* add denied_insults.yaml content filter category

* add block_insults.csv eval set (299 cases)

* add block_insults.jsonl eval set (299 cases)

* add insults eval results (100% F1)

* add TestInsultsContentFilter eval class

* add generate_compliance_prompts.py script

* add insultsCompliancePrompts.ts (299 prompts from CSV)

* add financialCompliancePrompts.ts (207 prompts from CSV)

* add Topic Blocking framework to compliance playground UI
2026-02-20 19:10:31 -08:00

161 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""
Generate a TypeScript compliance-prompts data file from a CSV eval file.
Usage example:
python generate_compliance_prompts.py \
--csv ../../litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/guardrail_benchmarks/evals/block_insults.csv \
--framework "Insults & Abuse" \
--framework-icon "alert-triangle" \
--framework-description "Detects insults, name-calling, and personal attacks — blocks abuse while allowing legitimate complaints." \
--category "Insults & Personal Attacks" \
--category-icon "alert-triangle" \
--category-description "Detects insults, name-calling, and personal attacks directed at the chatbot, staff, or other people" \
--output ../src/data/insultsCompliancePrompts.ts
"""
import argparse
import csv
import os
import sys
def escape_ts_string(s: str) -> str:
"""Escape a string for use inside a TypeScript double-quoted string literal."""
s = s.replace("\\", "\\\\")
s = s.replace('"', '\\"')
return s
def main() -> None:
parser = argparse.ArgumentParser(
description="Generate a TypeScript CompliancePrompt[] file from a CSV eval file."
)
parser.add_argument(
"--csv",
required=True,
help="Path to the input CSV file (columns: prompt, expected_result, framework, category).",
)
parser.add_argument(
"--framework",
required=True,
help='Framework display name, e.g. "Insults & Abuse".',
)
parser.add_argument(
"--framework-icon",
required=True,
help='Lucide icon name for the framework, e.g. "alert-triangle".',
)
parser.add_argument(
"--framework-description",
required=True,
help="One-line description of the framework.",
)
parser.add_argument(
"--category",
required=True,
help='Category display name, e.g. "Insults & Personal Attacks".',
)
parser.add_argument(
"--category-icon",
required=True,
help='Lucide icon name for the category, e.g. "alert-triangle".',
)
parser.add_argument(
"--category-description",
required=True,
help="One-line description of the category.",
)
parser.add_argument(
"--var-prefix",
required=True,
help='Prefix for exported variable names, e.g. "insults" -> insultsCompliancePrompts.',
)
parser.add_argument(
"--output",
required=True,
help="Path to the output .ts file.",
)
args = parser.parse_args()
# --- Read CSV ---
csv_path = os.path.abspath(args.csv)
if not os.path.isfile(csv_path):
print(f"Error: CSV file not found: {csv_path}", file=sys.stderr)
sys.exit(1)
rows: list[dict[str, str]] = []
with open(csv_path, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
rows.append(row)
if not rows:
print("Error: CSV file is empty.", file=sys.stderr)
sys.exit(1)
# --- Derive variable names from --var-prefix ---
prefix = args.var_prefix
array_name = f"{prefix}CompliancePrompts"
meta_name = f"{prefix}FrameworkMeta"
# --- Build TypeScript source ---
lines: list[str] = []
# Header comment
csv_basename = os.path.basename(args.csv)
lines.append(
f"// Auto-generated from {csv_basename} — do not edit manually."
)
lines.append(
f"// Regenerate: python scripts/generate_compliance_prompts.py --csv ... --output ..."
)
lines.append("")
lines.append(
'import type { CompliancePrompt, ComplianceFramework } from "./compliancePrompts";'
)
lines.append("")
lines.append(f"export const {array_name}: CompliancePrompt[] = [")
for idx, row in enumerate(rows, start=1):
prompt_text = escape_ts_string(row["prompt"].strip())
expected = row["expected_result"].strip().lower()
csv_category = row.get("category", "unknown").strip()
prompt_id = f"{csv_category}-{idx}"
lines.append(" {")
lines.append(f' id: "{prompt_id}",')
lines.append(f' framework: "{escape_ts_string(args.framework)}",')
lines.append(f' category: "{escape_ts_string(args.category)}",')
lines.append(f' categoryIcon: "{escape_ts_string(args.category_icon)}",')
lines.append(
f' categoryDescription: "{escape_ts_string(args.category_description)}",'
)
lines.append(f' prompt: "{prompt_text}",')
lines.append(f' expectedResult: "{expected}",')
lines.append(" },")
lines.append("];")
lines.append("")
lines.append(f"export const {meta_name} = {{")
lines.append(f' name: "{escape_ts_string(args.framework)}",')
lines.append(f' icon: "{escape_ts_string(args.framework_icon)}",')
lines.append(
f' description: "{escape_ts_string(args.framework_description)}",'
)
lines.append("};")
lines.append("")
# --- Write output ---
output_path = os.path.abspath(args.output)
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n".join(lines))
print(f"Generated {len(rows)} prompts -> {output_path}")
if __name__ == "__main__":
main()