* 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
161 lines
5.2 KiB
Python
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()
|