From da0623ebf54d2a2f24019fd231e1969eaacb19d0 Mon Sep 17 00:00:00 2001 From: Akshith Ambekar Date: Mon, 22 Sep 2025 15:36:08 -0400 Subject: [PATCH] fixed torch/nn issues when being called from maiko --- benchmarks/run.py | 252 ---------------- tueri/output_scanners/factual_consistency.py | 2 +- tueri/transformers_helpers.py | 6 + tueri_api/app/scanner.py | 4 +- tueri_api/config/scanners.json | 289 +++++++++++++++++-- tueri_api/docker-compose.yml | 1 + 6 files changed, 270 insertions(+), 284 deletions(-) delete mode 100644 benchmarks/run.py diff --git a/benchmarks/run.py b/benchmarks/run.py deleted file mode 100644 index 7ea66cbf..00000000 --- a/benchmarks/run.py +++ /dev/null @@ -1,252 +0,0 @@ -import argparse -import json -import timeit -from functools import lru_cache -from typing import Dict, List - -import numpy -import torch - -from tueri import input_scanners, output_scanners -from tueri.input_scanners.anonymize_helpers import DEBERTA_AI4PRIVACY_v2_CONF -from tueri.input_scanners.ban_substrings import MatchType as BanSubstringsMatchType -from tueri.input_scanners.base import Scanner as InputScanner -from tueri.output_scanners.base import Scanner as OutputScanner -from tueri.vault import Vault - -torch.set_float32_matmul_precision("high") - -import torch._inductor.config - -torch._inductor.config.fx_graph_cache = True - -vault = Vault() - - -def build_input_scanner(scanner_name: str, use_onnx: bool) -> InputScanner: - if scanner_name == "Anonymize": - return input_scanners.Anonymize( - vault=vault, use_onnx=use_onnx, recognizer_conf=DEBERTA_AI4PRIVACY_v2_CONF - ) - - if scanner_name == "BanCode": - return input_scanners.BanCode(use_onnx=use_onnx) - - if scanner_name == "BanCompetitors": - return input_scanners.BanCompetitors( - competitors=["Google", "Bing", "Yahoo"], - threshold=0.5, - use_onnx=use_onnx, - ) - - if scanner_name == "BanSubstrings": - return input_scanners.BanSubstrings( - substrings=["backdoor", "malware", "virus"], - match_type=BanSubstringsMatchType.WORD, - ) - - if scanner_name == "BanTopics": - return input_scanners.BanTopics(topics=["violence", "attack", "war"], use_onnx=use_onnx) - - if scanner_name == "Code": - return input_scanners.Code(languages=["Java"], is_blocked=True, use_onnx=use_onnx) - - if scanner_name == "Gibberish": - return input_scanners.Gibberish(use_onnx=use_onnx) - - if scanner_name == "InvisibleText": - return input_scanners.InvisibleText() - - if scanner_name == "Language": - return input_scanners.Language(valid_languages=["en", "es"], use_onnx=use_onnx) - - if scanner_name == "PromptInjection": - return input_scanners.PromptInjection(use_onnx=use_onnx) - - if scanner_name == "Regex": - return input_scanners.Regex(patterns=[r"Bearer [A-Za-z0-9-._~+/]+"]) - - if scanner_name == "Secrets": - return input_scanners.Secrets() - - if scanner_name == "Sentiment": - return input_scanners.Sentiment() - - if scanner_name == "TokenLimit": - return input_scanners.TokenLimit(limit=50) - - if scanner_name == "Toxicity": - return input_scanners.Toxicity(use_onnx=use_onnx) - - raise ValueError("Scanner not found") - - -def build_output_scanner(scanner_name: str, use_onnx: bool) -> OutputScanner: - if scanner_name == "BanCode": - return output_scanners.BanCode(use_onnx=use_onnx) - - if scanner_name == "BanCompetitors": - return output_scanners.BanCompetitors( - competitors=["Google", "Bing", "Yahoo"], - threshold=0.5, - use_onnx=use_onnx, - ) - - if scanner_name == "BanSubstrings": - return output_scanners.BanSubstrings( - substrings=["backdoor", "malware", "virus"], - match_type=BanSubstringsMatchType.WORD, - ) - - if scanner_name == "BanTopics": - return output_scanners.BanTopics(topics=["violence", "attack", "war"], use_onnx=use_onnx) - - if scanner_name == "Bias": - return output_scanners.Bias(use_onnx=use_onnx) - - if scanner_name == "Code": - return output_scanners.Code(languages=["Java"], is_blocked=True, use_onnx=use_onnx) - - if scanner_name == "Deanonymize": - return output_scanners.Deanonymize(vault) - - if scanner_name == "JSON": - return output_scanners.JSON() - - if scanner_name == "Language": - return output_scanners.Language(valid_languages=["en", "es"], use_onnx=use_onnx) - - if scanner_name == "LanguageSame": - return output_scanners.LanguageSame(use_onnx=use_onnx) - - if scanner_name == "MaliciousURLs": - return output_scanners.MaliciousURLs(use_onnx=use_onnx) - - if scanner_name == "NoRefusal": - return output_scanners.NoRefusal(use_onnx=use_onnx) - - if scanner_name == "NoRefusalLight": - return output_scanners.NoRefusalLight(use_onnx=use_onnx) - - if scanner_name == "ReadingTime": - return output_scanners.ReadingTime(max_time=0.5, truncate=True) - - if scanner_name == "FactualConsistency": - return output_scanners.FactualConsistency(use_onnx=use_onnx) - - if scanner_name == "Gibberish": - return output_scanners.Gibberish(use_onnx=use_onnx) - - if scanner_name == "Regex": - return output_scanners.Regex(patterns=[r"Bearer [A-Za-z0-9-._~+/]+"]) - - if scanner_name == "Relevance": - return output_scanners.Relevance(use_onnx=use_onnx) - - if scanner_name == "Sensitive": - return output_scanners.Sensitive( - redact=True, use_onnx=use_onnx, recognizer_conf=DEBERTA_AI4PRIVACY_v2_CONF - ) - - if scanner_name == "Sentiment": - return output_scanners.Sentiment() - - if scanner_name == "Toxicity": - return output_scanners.Toxicity(use_onnx=use_onnx) - - if scanner_name == "URLReachability": - return output_scanners.URLReachability() - - raise ValueError("Scanner not found") - - -@lru_cache(maxsize=None) -def get_input_test_data() -> Dict: - with open("input_examples.json", "r") as file: - return json.load(file) - - -@lru_cache(maxsize=None) -def get_output_test_data() -> (str, str): - with open("output_examples.json", "r") as file: - data = json.load(file) - - return {key: tuple(value) for key, value in data.items()} - - -def benchmark_input_scanner(scanner_name: str, repeat_times: int, use_onnx: bool) -> (List, int): - scanner = build_input_scanner(scanner_name, use_onnx=use_onnx) - - prompt = get_input_test_data()[scanner_name] - - latency_list = timeit.repeat(lambda: scanner.scan(prompt), number=1, repeat=repeat_times) - - return latency_list, len(prompt) - - -def benchmark_output_scanner(scanner_name: str, repeat_times: int, use_onnx: bool) -> (List, int): - scanner = build_output_scanner(scanner_name, use_onnx=use_onnx) - - prompt, output = get_output_test_data()[scanner_name] - - latency_list = timeit.repeat( - lambda: scanner.scan(prompt, output), number=1, repeat=repeat_times - ) - - return latency_list, len(output) - - -def get_output(scanner_name: str, scanner_type: str, latency_list, input_length: int) -> Dict: - latency_ms = sum(latency_list) / float(len(latency_list)) * 1000.0 - latency_variance = numpy.var(latency_list, dtype=numpy.float64) * 1000.0 - throughput = input_length * (1000.0 / latency_ms) - - return { - "scanner": scanner_name, - "scanner Type": scanner_type, - "input_length": input_length, - "test_times": len(latency_list), - "latency_variance": f"{latency_variance:.2f}", - "latency_90_percentile": f"{numpy.percentile(latency_list, 90) * 1000.0:.2f}", - "latency_95_percentile": f"{numpy.percentile(latency_list, 95) * 1000.0:.2f}", - "latency_99_percentile": f"{numpy.percentile(latency_list, 99) * 1000.0:.2f}", - "average_latency_ms": f"{latency_ms:.2f}", - "QPS": f"{throughput:.2f}", - } - - -def main(): - parser = argparse.ArgumentParser(description="Benchmark scanners in Tueri library.") - parser.add_argument( - "type", choices=["input", "output"], help="Type of the scanner to benchmark." - ) - parser.add_argument("scanner", type=str, help="Name of the scanner class to benchmark.") - parser.add_argument( - "--repeat", - type=int, - default=5, - help="Number of times to repeat the benchmark.", - ) - parser.add_argument( - "--use-onnx", - type=bool, - default=False, - help="Whether to use ONNX for inference, when possible.", - ) - - args = parser.parse_args() - - if args.type == "input": - latency_list, length = benchmark_input_scanner(args.scanner, args.repeat, args.use_onnx) - elif args.type == "output": - latency_list, length = benchmark_output_scanner(args.scanner, args.repeat, args.use_onnx) - else: - raise ValueError("Type is not found") - - # Structured Output - output = get_output(args.scanner, args.type, latency_list, length) - print(json.dumps(output, indent=4)) - - -if __name__ == "__main__": - main() diff --git a/tueri/output_scanners/factual_consistency.py b/tueri/output_scanners/factual_consistency.py index 588c1811..1cb7bb63 100644 --- a/tueri/output_scanners/factual_consistency.py +++ b/tueri/output_scanners/factual_consistency.py @@ -49,8 +49,8 @@ def __init__( model=model, use_onnx=use_onnx, ) - self._model = self._model.to(device()) if not use_onnx: + self._model = self._model.to(device()) self._model.eval() def scan(self, prompt: str, output: str) -> tuple[str, bool, float]: diff --git a/tueri/transformers_helpers.py b/tueri/transformers_helpers.py index 7021230c..8fa34edb 100644 --- a/tueri/transformers_helpers.py +++ b/tueri/transformers_helpers.py @@ -89,8 +89,11 @@ def get_tokenizer_and_model_for_classification( model.path, subfolder=model.subfolder, revision=model.revision, + torch_dtype="auto", + low_cpu_mem_usage=False, **model.kwargs, ) + tf_model = tf_model.to(device()) LOGGER.debug("Initialized classification model", model=model, device=device()) return tf_tokenizer, tf_model @@ -124,8 +127,11 @@ def get_tokenizer_and_model_for_ner( model.path, subfolder=model.subfolder, revision=model.revision, + torch_dtype="auto", + low_cpu_mem_usage=False, **model.kwargs, ) + tf_model = tf_model.to(device()) LOGGER.debug("Initialized NER model", model=model, device=device()) return tf_tokenizer, tf_model diff --git a/tueri_api/app/scanner.py b/tueri_api/app/scanner.py index 6d3e3472..bdb25fdb 100644 --- a/tueri_api/app/scanner.py +++ b/tueri_api/app/scanner.py @@ -30,7 +30,7 @@ LOGGER = structlog.getLogger(__name__) -MONGO_URI = os.getenv("MONGO_URI", "mongodb://root:example@localhost:27017/") +MONGO_URL = os.getenv("MONGO_URL", "mongodb://root:example@localhost:27017/") MONGO_DB, MONGO_COLLECTION = os.getenv("MONGO_DB", "ChatApp"), os.getenv("MONGO_COLLECTION", "TueriScanners") # Suppress MongoDB heartbeat logs @@ -38,7 +38,7 @@ logging.getLogger("pymongo.serverSelection").setLevel(logging.WARNING) try: - mongo_client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000, heartbeatFrequencyMS=60000) + mongo_client = MongoClient(MONGO_URL, serverSelectionTimeoutMS=5000, heartbeatFrequencyMS=60000) db = mongo_client[MONGO_DB] scanners_collection = db[MONGO_COLLECTION] mongo_client.admin.command("ping") diff --git a/tueri_api/config/scanners.json b/tueri_api/config/scanners.json index c528926d..59d2c567 100644 --- a/tueri_api/config/scanners.json +++ b/tueri_api/config/scanners.json @@ -1,31 +1,262 @@ [ - { "type": "input", "id": "Anonymize", "params": { "threshold": 0.50, "language": "en", "preamble": "" }, "order": 1 }, - { "type": "input", "id": "BanTopics", "params": { "topics": ["violence and physical harm to individuals", "hate speech and discriminatory content", "illegal activities and criminal activity", "pornography, erotica, adult and sexual content", "terrorism and indiscriminate Weapons", "self-harm and suicide", "medical diagnosis without professional oversight", "legal advice without professional qualification", "tax evasion and financial fraud methods", "insurance fraud and illegal practices", "cybersecurity exploits and hacking techniques", "software piracy and copyright infringement", "political opinions and partisan commentary", "religious discussions and theological debates", "personal relationship advice and dating guidance", "substance abuse and drug-related discussions", "gambling advice and betting strategies", "harassment and workplace bullying", "conspiracy theories and misinformation spreading", "defamatory statements about individuals or organizations", "spam and deceptive content generation", "gender identity and sexual orientation"], "threshold": 0.6 }, "order": 2 }, - { "type": "input", "id": "MaskCode", "params": { "threshold": 0.97, "mask_token": "[CODE]", "language_threshold": 0.5 }, "order": 3 }, - { "type": "input", "id": "InvisibleText", "params": {}, "order": 4 }, - { "type": "input", "id": "PromptInjection", "params": { "threshold": 0.95, "match_type": "truncate_head_tail" }, "order": 5 }, - { "type": "input", "id": "Regex", "params": { "patterns": ["Bearer [A-Za-z0-9-._~+/]+"], "is_blocked": true, "match_type": "search", "redact": true }, "order": 6 }, - { "type": "input", "id": "Secrets", "params": { "redact_mode": "all" }, "order": 7 }, - { "type": "input", "id": "BanCompetitors", "params": { "competitors": ["OpenAI", "Google", "Microsoft"], "threshold": 0.5, "redact": true }, "order": 8 }, - { "type": "input", "id": "BanSubstrings", "params": { "substrings": ["bypass", "jailbreak", "ignore instructions"], "match_type": "str", "case_sensitive": false, "redact": false, "contains_all": false }, "order": 9 }, - { "type": "input", "id": "Language", "params": { "valid_languages": ["en"], "threshold": 0.6, "match_type": "full" }, "order": 10 }, - { "type": "input", "id": "Sentiment", "params": { "threshold": -0.3, "lexicon": "vader_lexicon" }, "order": 11 }, - { "type": "input", "id": "TokenLimit", "params": { "limit": 4096, "encoding_name": "cl100k_base" }, "order": 12 }, - { "type": "output", "id": "BanTopics", "params": { "topics": ["violence and physical harm to individuals", "hate speech and discriminatory content", "illegal activities and criminal activity", "pornography, erotica, adult and sexual content", "terrorism and indiscriminate Weapons", "self-harm and suicide", "medical diagnosis without professional oversight", "legal advice without professional qualification", "tax evasion and financial fraud methods", "insurance fraud and illegal practices", "cybersecurity exploits and hacking techniques", "software piracy and copyright infringement", "political opinions and partisan commentary", "religious discussions and theological debates", "personal relationship advice and dating guidance", "substance abuse and drug-related discussions", "gambling advice and betting strategies", "harassment and workplace bullying", "conspiracy theories and misinformation spreading", "defamatory statements about individuals or organizations", "spam and deceptive content generation", "gender identity and sexual orientation"], "threshold": 0.6 }, "order": 13 }, - { "type": "output", "id": "Bias", "params": { "threshold": 0.97, "match_type": "full" }, "order": 14 }, - { "type": "output", "id": "Deanonymize", "params": { "matching_strategy": "exact" }, "order": 15 }, - { "type": "output", "id": "FactualConsistency", "params": { "minimum_score": 0.5 }, "order": 16 }, - { "type": "output", "id": "BadURL", "params": { "threshold": 0.95 }, "order": 17 }, - { "type": "output", "id": "Regex", "params": { "patterns": ["Bearer [A-Za-z0-9-._~+/]+"], "is_blocked": true, "match_type": "search", "redact": true }, "order": 18 }, - { "type": "output", "id": "Sensitive", "params": { "redact": false, "threshold": 0.50, "language": "en" }, "order": 19 }, - { "type": "output", "id": "BanCompetitors", "params": { "competitors": ["OpenAI", "Google", "Microsoft"], "threshold": 0.5, "redact": true }, "order": 20 }, - { "type": "output", "id": "BanSubstrings", "params": { "substrings": ["confidential", "internal"], "match_type": "str", "case_sensitive": false, "redact": true, "contains_all": false }, "order": 21 }, - { "type": "output", "id": "JSON", "params": { "required_elements": 0, "repair": true }, "order": 22 }, - { "type": "output", "id": "Language", "params": { "valid_languages": ["en"], "threshold": 0.6, "match_type": "full" }, "order": 23 }, - { "type": "output", "id": "LanguageSame", "params": { "threshold": 0.6 }, "order": 24 }, - { "type": "output", "id": "MaskCode", "params": { "threshold": 0.5, "mask_token": "[CODE]", "language_threshold": 0.5 }, "order": 25 }, - { "type": "output", "id": "NoRefusal", "params": { "threshold": 0.75, "match_type": "full" }, "order": 26 }, - { "type": "output", "id": "NoRefusalLight", "params": {}, "order": 27 }, - { "type": "output", "id": "Relevance", "params": { "threshold": 0.5 }, "order": 28 }, - { "type": "output", "id": "Sentiment", "params": { "threshold": -0.3, "lexicon": "vader_lexicon" }, "order": 29 } +{ + "type": "input", + "id": "Anonymize", + "riskLevel": "high", + "definition": "Detects and removes personally identifiable information (PII) from prompts", + "threshold_desc": "Detect and anonymize PII in prompts. Higher threshold = less aggressive removal.", + "params": { "threshold": 0.50, "language": "en", "preamble": "" }, + "order": 1 +}, +{ + "type": "input", + "id": "BanTopics", + "riskLevel": "high", + "definition": "Blocks prompts about specific banned topics (violence, politics, etc.)", + "params": { "topics": ["violence and physical harm to individuals", "hate speech and discriminatory content", "illegal activities and criminal activity", "pornography, erotica, adult and sexual content", "terrorism and indiscriminate Weapons", "self-harm and suicide", "medical diagnosis without professional oversight", "legal advice without professional qualification", "tax evasion and financial fraud methods", "insurance fraud and illegal practices", "cybersecurity exploits and hacking techniques", "software piracy and copyright infringement", "political opinions and partisan commentary", "religious discussions and theological debates", "personal relationship advice and dating guidance", "substance abuse and drug-related discussions", "gambling advice and betting strategies", "harassment and workplace bullying", "conspiracy theories and misinformation spreading", "defamatory statements about individuals or organizations", "spam and deceptive content generation", "gender identity and sexual orientation"], "threshold": 0.6 }, + "order": 2 +}, +{ + "type": "input", + "id": "MaskCode", + "riskLevel": "medium", + "definition": "Detects and blocks code snippets in prompts", + "threshold_desc": "Higher values = stricter code detection. 0.97 blocks most code, 0.5 blocks obvious code only.", + "params": { "threshold": 0.97, "mask_token": "[CODE]", "language_threshold": 0.5 }, + "order": 3 +}, +{ + "type": "input", + "id": "InvisibleText", + "riskLevel": "high", + "definition": "Detects hidden or invisible Unicode characters", + "threshold_desc": "Confidence for detecting hidden Unicode characters. Higher = only obvious invisible text.", + "params": {}, + "order": 4 +}, +{ + "type": "input", + "id": "PromptInjection", + "riskLevel": "high", + "definition": "Detects prompt injection attacks and manipulation attempts", + "threshold_desc": "Injection attack detection sensitivity. Higher = only block obvious attacks.", + "params": { "threshold": 0.95, "match_type": "truncate_head_tail" }, + "order": 5 +}, +{ + "type": "input", + "id": "Regex", + "riskLevel": "medium", + "definition": "Custom regex pattern matching for specific content", + "threshold_desc": "Pattern matching confidence. Adjust based on your regex complexity.", + "params": { "patterns": ["Bearer [A-Za-z0-9-._~+/]"], "is_blocked": true, "match_type": "search", "redact": true }, + "order": 6 +}, +{ + "type": "input", + "id": "Secrets", + "riskLevel": "high", + "definition": "Detects API keys, passwords, and other secrets", + "threshold_desc": "Secret detection confidence. Higher = only block obvious secrets (API keys, passwords).", + "params": { "redact_mode": "all" }, + "order": 7 +}, +{ + "type": "input", + "id": "BanCompetitors", + "riskLevel": "low", + "definition": "Blocks mentions of competitor names", + "threshold_desc": "Confidence level for competitor name detection. Higher = fewer false positives.", + "params": { "competitors": ["OpenAI", "Google", "Microsoft"], "threshold": 0.5, "redact": true }, + "order": 8 +}, +{ + "type": "input", + "id": "BanSubstrings", + "riskLevel": "medium", + "definition": "Blocks prompts containing specified banned substrings", + "threshold_desc": "How strictly to match banned substrings. Higher = exact matches only, lower = fuzzy matching.", + "params": { "substrings": ["bypass", "jailbreak", "ignore instructions"], "match_type": "str", "case_sensitive": false, "redact": false, "contains_all": false }, + "order": 9 +}, +{ + "type": "input", + "id": "Language", + "riskLevel": "low", + "definition": "Validates prompt language against allowed languages", + "threshold_desc": "Language detection confidence. Higher = more certain language identification required.", + "params": { "valid_languages": ["en"], "threshold": 0.6, "match_type": "full" }, + "order": 10 +}, +{ + "type": "input", + "id": "Sentiment", + "riskLevel": "medium", + "definition": "Analyzes sentiment and blocks overly negative content", + "threshold_desc": "Sentiment threshold (-1.0 to 1.0). -1.0 = block very negative only, 0.0 = allow neutral+, 1.0 = block all negative.", + "params": { "threshold": -0.3, "lexicon": "vader_lexicon" }, + "order": 11 +}, +{ + "type": "input", + "id": "TokenLimit", + "riskLevel": "low", + "definition": "Enforces maximum token limits for prompts", + "threshold_desc": "Token limit enforcement. Blocks prompts exceeding the specified token count.", + "params": { "limit": 4096, "encoding_name": "cl100k_base" }, + "order": 12 +}, +{ + "type": "output", + "id": "BanTopics", + "riskLevel": "high", + "definition": "Blocks outputs about banned topics", + "threshold_desc": "Block responses about banned topics. Higher = only clearly related responses.", + "params": { "topics": ["violence and physical harm to individuals", "hate speech and discriminatory content", "illegal activities and criminal activity", "pornography, erotica, adult and sexual content", "terrorism and indiscriminate Weapons", "self-harm and suicide", "medical diagnosis without professional oversight", "legal advice without professional qualification", "tax evasion and financial fraud methods", "insurance fraud and illegal practices", "cybersecurity exploits and hacking techniques", "software piracy and copyright infringement", "political opinions and partisan commentary", "religious discussions and theological debates", "personal relationship advice and dating guidance", "substance abuse and drug-related discussions", "gambling advice and betting strategies", "harassment and workplace bullying", "conspiracy theories and misinformation spreading", "defamatory statements about individuals or organizations", "spam and deceptive content generation", "gender identity and sexual orientation"], "threshold": 0.6 }, + "order": 13 +}, +{ + "type": "output", + "id": "Bias", + "riskLevel": "medium", + "definition": "Detects bias in model outputs", + "threshold_desc": "Detect biased language in outputs. Higher = only flag obvious bias.", + "params": { "threshold": 0.97, "match_type": "full" }, + "order": 14 +}, +{ + "type": "output", + "id": "Deanonymize", + "riskLevel": "high", + "definition": "Restores anonymized information in outputs", + "threshold_desc": "Detect and reverse anonymization in outputs. Higher = only flag clear deanonymization.", + "params": { "matching_strategy": "exact" }, + "order": 15 +}, +{ + "type": "output", + "id": "FactualConsistency", + "riskLevel": "high", + "definition": "Checks factual consistency with input", + "threshold_desc": "Check facts against input. Higher = only flag clear inconsistencies.", + "params": { "minimum_score": 0.5 }, + "order": 16 +}, +{ + "type": "output", + "id": "BadURL", + "riskLevel": "high", + "definition": "Detects both URL reachability and malicious URLs in outputs", + "threshold_desc": "Detect and block bad or malicious URLs in outputs. Higher = only block obvious bad URLs.", + "params": { "threshold": 0.95 }, + "order": 17 +}, +{ + "type": "output", + "id": "Regex", + "riskLevel": "medium", + "definition": "Custom regex pattern matching in outputs", + "threshold_desc": "Custom pattern matching in outputs. Adjust based on pattern complexity.", + "params": { "patterns": ["Bearer [A-Za-z0-9-._~+/]"], "is_blocked": true, "match_type": "search", "redact": true }, + "order": 18 +}, +{ + "type": "output", + "id": "Sensitive", + "riskLevel": "high", + "definition": "Detects sensitive information in outputs", + "threshold_desc": "Detect sensitive info in outputs. Higher = only flag obvious sensitive data.", + "params": { "redact": false, "threshold": 0.50, "language": "en" }, + "order": 19 +}, +{ + "type": "output", + "id": "BanCompetitors", + "riskLevel": "low", + "definition": "Removes competitor mentions from outputs", + "threshold_desc": "Remove competitor mentions from responses. Higher = only obvious mentions.", + "params": { "competitors": ["OpenAI", "Google", "Microsoft"], "threshold": 0.5, "redact": true }, + "order": 20 +}, +{ + "type": "output", + "id": "BanSubstrings", + "riskLevel": "medium", + "definition": "Removes banned substrings from outputs", + "threshold_desc": "Remove banned phrases from outputs. Higher = exact matches only.", + "params": { "substrings": ["confidential", "internal"], "match_type": "str", "case_sensitive": false, "redact": true, "contains_all": false }, + "order": 21 +}, +{ + "type": "output", + "id": "JSON", + "riskLevel": "low", + "definition": "Validates and repairs JSON in outputs", + "threshold_desc": "Validate and repair JSON output. Ensures required elements are present.", + "params": { "required_elements": 0, "repair": true }, + "order": 22 +}, +{ + "type": "output", + "id": "Language", + "riskLevel": "low", + "definition": "Validates output language", + "threshold_desc": "Ensure output language correctness. Higher = stricter language validation.", + "params": { "valid_languages": ["en"], "threshold": 0.6, "match_type": "full" }, + "order": 23 +}, +{ + "type": "output", + "id": "LanguageSame", + "riskLevel": "medium", + "definition": "Ensures output language matches input language", + "threshold_desc": "Match output language to input. Higher = stricter language matching.", + "params": { "threshold": 0.6 }, + "order": 24 +}, +{ + "type": "output", + "id": "MaskCode", + "riskLevel": "medium", + "definition": "Blocks code snippets in model outputs", + "threshold_desc": "Code detection in outputs. Higher = stricter filtering of code responses.", + "params": { "threshold": 0.5, "mask_token": "[CODE]", "language_threshold": 0.5 }, + "order": 25 +}, +{ + "type": "output", + "id": "NoRefusal", + "riskLevel": "medium", + "definition": "Detects when model refuses to answer", + "threshold_desc": "Detect when AI refuses to answer. Higher = only flag clear refusals.", + "params": { "threshold": 0.75, "match_type": "full" }, + "order": 26 +}, +{ + "type": "output", + "id": "NoRefusalLight", + "riskLevel": "medium", + "definition": "Lightweight refusal detection; flags obvious refusals only", + "threshold_desc": "Lightweight refusal detection. Flags obvious refusals only.", + "params": {}, + "order": 27 +}, +{ + "type": "output", + "id": "Relevance", + "riskLevel": "medium", + "definition": "Ensures output relevance to input prompt", + "threshold_desc": "Ensure output relevance to input. Higher = only flag clearly irrelevant responses.", + "params": { "threshold": 0.5 }, + "order": 28 +}, +{ + "type": "output", + "id": "Sentiment", + "riskLevel": "medium", + "definition": "Analyzes sentiment in outputs", + "threshold_desc": "Sentiment threshold (-1.0 to 1.0). -1.0 = block very negative only, 0.0 = allow neutral+, 1.0 = block all negative.", + "params": { "threshold": -0.3, "lexicon": "vader_lexicon" }, + "order": 29 +} ] \ No newline at end of file diff --git a/tueri_api/docker-compose.yml b/tueri_api/docker-compose.yml index 38b7d53b..0313d8a4 100644 --- a/tueri_api/docker-compose.yml +++ b/tueri_api/docker-compose.yml @@ -19,3 +19,4 @@ services: restart: unless-stopped volumes: - ./config/app_config.yml:/home/user/app/config/app_config.yml + \ No newline at end of file