"""CYPHER V12 M13 — French language boost. Detects French prompts and applies adjustments: - Boost generation temperature slightly (FR vocab less covered) - Inject French few-shot example (via M15 fewshot_prompter) - Post-process: detect English code-switching and flag for retry - Optional: simple FR↔EN dictionary for common terms Used by cypher_bridge_v12.py /chat pipeline. """ from __future__ import annotations import logging import re from typing import Any logger = logging.getLogger(__name__) # Quick FR detection: presence of accented characters or common FR function words _FR_ACCENT_RE = re.compile(r"[éèêëàâäîïôöùûüçÉÈÊËÀÂÄÎÏÔÖÙÛÜÇ]") _FR_FUNCTION_WORDS = { " est ", " sont ", " une ", " des ", " du ", " au ", " aux ", " pour ", " avec ", " dans ", " sur ", " sans ", " entre ", " qui ", " que ", " quoi ", " comment ", " pourquoi ", " quel ", " quelle ", " quels ", " quelles ", " ceux ", " celle ", " ces ", " mon ", " ma ", " mes ", " ton ", " ta ", " tes ", " son ", " sa ", " ses ", " notre ", " votre ", " leur ", " leurs ", } _FR_QUESTION_STARTS = ( "qui ", "que ", "quoi", "quel", "quelle", "comment", "pourquoi", "où ", "ou ", "quand ", "explique", "décris", "presente", "présente", "bonjour", "salut", "bonsoir", "merci", "aide", "donne", ) def detect_french(text: str, threshold: float = 0.5) -> dict: """Detect if input is French. Returns confidence + signals.""" if not text: return {"is_fr": False, "confidence": 0.0, "signals": []} signals: list[str] = [] score = 0.0 # 1. Accent presence n_accents = len(_FR_ACCENT_RE.findall(text)) if n_accents > 0: score += min(0.4, n_accents * 0.1) signals.append(f"accents:{n_accents}") # 2. Function words text_padded = f" {text.lower()} " fr_word_hits = sum(1 for w in _FR_FUNCTION_WORDS if w in text_padded) if fr_word_hits > 0: score += min(0.5, fr_word_hits * 0.08) signals.append(f"fr_words:{fr_word_hits}") # 3. Starts with FR question word text_lower = text.lower().strip() if any(text_lower.startswith(qs) for qs in _FR_QUESTION_STARTS): score += 0.3 signals.append("fr_question_start") # 4. langdetect fallback (if available) try: from langdetect import detect, DetectorFactory DetectorFactory.seed = 0 lang = detect(text) if lang == "fr": score += 0.3 signals.append("langdetect_fr") except Exception: pass is_fr = score >= threshold return { "is_fr": is_fr, "confidence": min(1.0, score), "signals": signals, } def detect_english_codeswitch(response: str, min_run: int = 5) -> bool: """Detect if response contains a run of English words inside FR context.""" # Heuristic: 5+ consecutive ASCII-only tokens with no FR accent in middle of response if not response: return False tokens = response.split() if len(tokens) < min_run * 2: return False has_fr_overall = bool(_FR_ACCENT_RE.search(response)) or any( w in f" {response.lower()} " for w in _FR_FUNCTION_WORDS ) if not has_fr_overall: return False # not even FR overall, no codeswitch concern # Find longest run of pure-English (ASCII no accent, no FR word) run = 0 max_run = 0 for tok in tokens: tok_clean = tok.strip(".,;:!?\"'()[]") if not tok_clean: continue is_en = (tok_clean.isascii() and len(tok_clean) >= 3 and tok_clean.lower() not in ("de", "la", "le", "les", "un", "une", "des", "du", "au", "et", "ou", "ne", "pas", "ce", "ces", "se", "ses", "ma", "ta", "sa", "mon", "ton", "son")) if is_en: run += 1 max_run = max(max_run, run) else: run = 0 return max_run >= min_run class FRLanguageBoost: """Applies FR-aware inference adjustments at the bridge level.""" def __init__( self, base_temperature: float = 0.35, fr_temperature_bump: float = 0.05, codeswitch_retry: bool = True, ): self.base_temperature = base_temperature self.fr_temperature_bump = fr_temperature_bump self.codeswitch_retry = codeswitch_retry def get_temperature(self, prompt: str) -> float: info = detect_french(prompt) if info["is_fr"]: return self.base_temperature + self.fr_temperature_bump return self.base_temperature def should_inject_fr_fewshot(self, prompt: str) -> bool: return detect_french(prompt)["is_fr"] def post_process(self, prompt: str, response: str) -> dict: """Diagnose response quality vs FR expectation.""" prompt_info = detect_french(prompt) if not prompt_info["is_fr"]: return {"ok": True, "issue": None} response_info = detect_french(response) if not response_info["is_fr"]: return {"ok": False, "issue": "fr_prompt_en_response"} if self.codeswitch_retry and detect_english_codeswitch(response): return {"ok": False, "issue": "fr_response_en_codeswitch"} return {"ok": True, "issue": None} # Optional: small bilingual cybersec/trading glossary for hint injection FR_EN_GLOSSARY = { "vulnérabilité": "vulnerability", "menace": "threat", "attaque": "attack", "chiffrement": "encryption", "déchiffrement": "decryption", "pare-feu": "firewall", "mot de passe": "password", "hameçonnage": "phishing", "rançongiciel": "ransomware", "logiciel malveillant": "malware", "renseignement sur les menaces": "threat intelligence", "réponse à incident": "incident response", "détection": "detection", "atténuation": "mitigation", "pile": "stack", "tas": "heap", "débordement": "overflow", "ordre block": "order block", "vide de juste valeur": "fair value gap", "balayage de liquidité": "liquidity sweep", "argent intelligent": "smart money", "tendance": "trend", "structure de marché": "market structure", "premium": "premium", "discount": "discount", "stop suiveur": "trailing stop", } def glossary_hint(text: str) -> str: """Returns a brief glossary hint if text contains any FR↔EN term.""" text_lower = text.lower() matches: list[str] = [] for fr_term, en_term in FR_EN_GLOSSARY.items(): if fr_term in text_lower or en_term in text_lower: matches.append(f"{fr_term}↔{en_term}") if not matches: return "" return f"[FR_GLOSSARY: {'; '.join(matches[:5])}]" __all__ = [ "detect_french", "detect_english_codeswitch", "FRLanguageBoost", "FR_EN_GLOSSARY", "glossary_hint", ] if __name__ == "__main__": logging.basicConfig(level=logging.INFO) print("=== M13 fr_language_boost SMOKE ===") # Detection tests = [ ("Bonjour, comment puis-je analyser ce CVE?", True), ("Hello, what is SQL injection?", False), ("Qui es-tu?", True), ("Tell me about Order Blocks", False), ("Explique le concept de Smart Money", True), ("Mix: explique CVE-2024-3400 mais en anglais please", True), ] for txt, expected in tests: info = detect_french(txt) mark = "✓" if info["is_fr"] == expected else "✗" print(f" {mark} '{txt[:40]}' is_fr={info['is_fr']} conf={info['confidence']:.2f} signals={info['signals']}") # Codeswitch detection fr_response_clean = "Je suis CYPHER, l'ASI cybersécurité défensive. Je travaille avec les CVE et MITRE ATT&CK." fr_response_switched = "Je suis CYPHER. The defensive cybersec ASI of the family. We work with various CVE catalogs and MITRE ATT&CK techniques for threat hunting and detection engineering across enterprise." print(f"\nCodeswitch clean: {detect_english_codeswitch(fr_response_clean)}") print(f"Codeswitch switched: {detect_english_codeswitch(fr_response_switched)}") # FRLanguageBoost boost = FRLanguageBoost() print(f"\nTemperature FR prompt: {boost.get_temperature('Qui es-tu?'):.2f}") print(f"Temperature EN prompt: {boost.get_temperature('Who are you?'):.2f}") pp = boost.post_process("Qui es-tu?", "I'm CYPHER the defensive ASI of the family.") print(f"Post-process FR→EN response: {pp}") # Glossary print(f"\nGlossary hint: {glossary_hint('Explique le rançongiciel et le phishing')}") print("=== SMOKE PASS ===")