cypher-v12-finalized / modules /fr_language_boost.py
jescy525's picture
Upload folder using huggingface_hub
076a67c verified
Raw
History Blame Contribute Delete
8.59 kB
"""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 ===")