Irminsul / guardrails.py
MukulRay's picture
feat: Groq backend, guardrails module, Sumeru UI redesign
00e4869
Raw
History Blame
2.84 kB
import os
import logging
from sentence_transformers import SentenceTransformer, util
logger = logging.getLogger(__name__)
EMBED_MODEL = os.getenv("EMBED_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
GENSHIN_ANCHORS = [
"Genshin Impact character build",
"elemental reaction damage",
"Archon quest lore",
"artifact set bonus",
"team composition synergy",
"vision and gnosis",
"Mondstadt Liyue Inazuma Sumeru Fontaine Natlan",
"polearm sword claymore bow catalyst",
"Pyro Hydro Cryo Electro Anemo Geo Dendro",
"Genshin resin domain spiral abyss",
"Hu Tao Zhongli Venti Kazuha Raiden Shogun",
"Neuvillette Furina Arlecchino Navia Wriothesley",
"who is this Genshin character",
"best build for Genshin character",
"Genshin lore story explained",
"where to find character in Genshin",
]
INJECTION_PATTERNS = [
"ignore previous instructions",
"ignore your instructions",
"you are now",
"pretend you are",
"act as",
"forget everything",
"jailbreak",
"dan mode",
"do anything now",
]
_embedder = None
_anchor_embeddings = None
def _get_embedder():
global _embedder, _anchor_embeddings
if _embedder is None:
_embedder = SentenceTransformer(EMBED_MODEL)
_anchor_embeddings = _embedder.encode(
GENSHIN_ANCHORS, convert_to_tensor=True
)
return _embedder, _anchor_embeddings
def is_in_domain(query: str, threshold: float = 0.15) -> bool:
try:
embedder, anchors = _get_embedder()
query_vec = embedder.encode(query, convert_to_tensor=True)
score = util.cos_sim(query_vec, anchors).max().item()
logger.info(f"Domain score: {score:.3f}")
return score >= threshold
except Exception as e:
logger.warning(f"Domain check failed ({e}) — passing query through")
return True
def has_injection(query: str) -> bool:
q = query.lower()
return any(p in q for p in INJECTION_PATTERNS)
def validate_input(query: str) -> tuple[bool, str]:
query = query.strip()
if not query:
return False, "Query cannot be empty."
if len(query) > 500:
return False, "Query is too long — please keep it under 500 characters."
if has_injection(query):
return False, "That type of query isn't something I can help with."
if not is_in_domain(query):
return (
False,
"I'm specialized in Genshin Impact — ask me about characters, "
"builds, lore, or elemental mechanics!",
)
return True, ""
def validate_output(answer: str) -> tuple[bool, str]:
answer = answer.strip().replace("</s>", "").strip()
if len(answer) < 10:
return False, "I couldn't find enough context to answer that — try rephrasing."
return True, answer