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("", "").strip() if len(answer) < 10: return False, "I couldn't find enough context to answer that — try rephrasing." return True, answer