Download python-services/greeting_handler.py from 010Abid/the-brain: direct link, hf CLI and curl.
- Browser
- Download file 11.6 kB
-
https://huggingface.co/spaces/010Abid/the-brain/resolve/main/python-services/greeting_handler.py
- Command line
-
hf download hf://spaces/010Abid/the-brain/python-services/greeting_handler.py
-
curl -L -o greeting_handler.py https://huggingface.co/spaces/010Abid/the-brain/resolve/main/python-services/greeting_handler.py
11.6 kB
| # greeting_handler.py | |
| # Classifies opening-turn messages and returns template short replies (no LLM). | |
| # Supports Latin/Roman and native Urdu (Arabic-script) greetings. | |
| # | |
| # v2 — Issue 8 fix + arabic_clarify removal | |
| # | |
| # Issue 8 (English "Yes, I can hear you." reply in Urdu-only session): | |
| # The connection_check tier in get_short_reply() had a fallback branch: | |
| # if _is_audio_connection_check(text): return "جی ہاں، آواز آ رہی ہے۔" | |
| # return "Yes, I can hear you." ← BUG: English fallback | |
| # When the connection-check pattern matched BUT _is_audio_connection_check() | |
| # returned False (e.g. a bare "ہیلو ہیلو" repetition matched the | |
| # connection_check tier via _is_connection_check but didn't contain | |
| # آواز/سن/bol keywords), the fallback English string was returned and | |
| # synthesised in an English voice — exactly the "Yes, I can hear you." | |
| # (chars=20) seen in the TTS log for session1 Turn1. | |
| # Fix: both branches of connection_check now return Urdu strings. | |
| # The English fallback is removed entirely (Urdu-only mode). | |
| # | |
| # arabic_clarify removal: | |
| # classify_opening_message() previously accepted a `language_locked` | |
| # param that was partly driven by arabic_clarify state. That param is | |
| # unchanged (it's the session language lock flag, not arabic_clarify). | |
| # No further changes needed in this file for arabic_clarify — it was | |
| # only consumed by prompt_builder.py and language_detector.py, not here. | |
| import re | |
| from typing import Literal | |
| from language_config import has_arabic_script | |
| OpeningTier = Literal["pure_greeting", "connection_check", "greeting_with_substance", "normal"] | |
| # Roman / Latin substance markers | |
| SUBSTANCE_MARKERS_LATIN = { | |
| "aap", "ap", "meri", "mera", "madad", "help", "kesse", "kaise", "kese", "kaisey", | |
| "puchna", "poochna", "admission", "course", "problem", "issue", "sawal", "sawaal", | |
| "question", "batana", "batao", "bata", "chahiye", "chahte", "chahti", "karna", | |
| "how", "what", "when", "where", "why", "can", "could", "would", "need", "want", | |
| "tell", "about", "regarding", "fee", "fees", "enroll", "register", | |
| "mujhe", "main", "mein", "hum", "humein", "kya", "kab", "kahan", "kyun", | |
| } | |
| # Urdu-script substance markers (intent beyond a bare greeting) | |
| SUBSTANCE_MARKERS_URDU = { | |
| "آپ", "اپ", "مدد", "مجھے", "میرا", "میری", "مرا", "کیا", "کہاں", "کیوں", "کیسے", | |
| "کسے", "بتائیں", "بتاؤ", "پوچھنا", "چاہیے", "ضرورت", "مسئلہ", "مسئله", "سوال", | |
| "داخلہ", "کورس", "فیس", "کرنا", "کریں", "سکتے", "سکتا", "سکتی", | |
| "بتانا", "رہنمائی", "معلومات", "جاننا", "جانکارى", | |
| } | |
| PURE_GREETING_PHRASES_LATIN = { | |
| "hi", "hello", "hey", "helo", "hii", "hiii", | |
| "salam", "adaab", | |
| "assalam u alaikum", "assalamu alaikum", "asalam u alaikum", "assalam o alaikum", | |
| "salam u alaikum", "salam alaikum", "assalam alaikum", | |
| "alaikum assalam", "walaikum assalam", "walaykum assalam", "walaikum salam", | |
| "kher he", "khair he", "khair mubarak", "kher mubarak", "mubarak", | |
| "good morning", "good evening", "good afternoon", "good night", | |
| "yes", "ok", "okay", "thanks", "thank you", "bye", "goodbye", "shukriya", | |
| "ji", "jee", "han", "haan", "nah", "na", | |
| } | |
| # Native Urdu-script greeting phrases (punctuation stripped before match) | |
| PURE_GREETING_PHRASES_URDU = { | |
| "السلام علیکم", "السلام عليكم", "سلام علیکم", "سلام عليكم", | |
| "وعلیکم السلام", "وعليكم السلام", "و علیکم السلام", "و عليكم السلام", | |
| "سلام", "السلام", "آداب", "اداب", "خیر ہے", "خیر مبارک", "خیرمبارک", "مبارک", | |
| "جی", "جی ہاں", "جی ہاں", "ہاں", "ہان", "شکریہ", "شکریا", "جزاک اللہ", | |
| "ہیلو", "ہائے", "ہیلو ہیلو", "سلام سلام", | |
| "الحمد للہ", "الحمد لله", "ماشاء اللہ", "ماشاء الله", | |
| } | |
| URDU_GREETING_TOKENS = { | |
| "السلام", "علیکم", "عليكم", "وعلیکم", "وعليكم", "سلام", "آداب", "اداب", | |
| "خیر", "مبارک", "جی", "ہاں", "ہان", "شکریہ", "شکریا", "ہیلو", "ہائے", | |
| } | |
| CONNECTION_CHECK_PATTERNS = [ | |
| # Latin / Roman | |
| re.compile(r"\bhello\s+hello\b", re.I), | |
| re.compile(r"\bhi\s+hi\b", re.I), | |
| re.compile(r"\bhey\s+hey\b", re.I), | |
| re.compile(r"awa?z\s+aara?hi", re.I), | |
| re.compile(r"awa?z\s+aa\s+rahi", re.I), | |
| re.compile(r"sun\s+pa\s+rahe", re.I), | |
| re.compile(r"sun\s+rahe", re.I), | |
| re.compile(r"koi\s+sun\s+raha", re.I), | |
| re.compile(r"sunaai\s+de", re.I), | |
| re.compile(r"sunai\s+de", re.I), | |
| re.compile(r"bol\s+rahe", re.I), | |
| # Urdu script | |
| re.compile(r"ہیلو\s+ہیلو"), | |
| re.compile(r"سلام\s+سلام"), | |
| re.compile(r"آواز\s+آ\s*رہی"), | |
| re.compile(r"آواز\s+آرہی"), | |
| re.compile(r"آواز\s+آ\s*رہی"), | |
| re.compile(r"سن\s+پا\s+رہے"), | |
| re.compile(r"سن\s+رہے"), | |
| re.compile(r"سن\s+رہی"), | |
| re.compile(r"کوئی\s+سن\s+رہا"), | |
| re.compile(r"سنائی\s+دے"), | |
| re.compile(r"سناائی\s+دے"), | |
| re.compile(r"بول\s+رہے"), | |
| re.compile(r"بول\s+رہی"), | |
| re.compile(r"آواز\s+آ\s*رہی\s+ہے"), | |
| ] | |
| _GREETING_MIRROR = [ | |
| # Latin / Roman | |
| (re.compile(r"assalam\s*u?\s*alaikum|assalamu\s*alaikum|salam\s*u?\s*alaikum", re.I), "وعلیکم السلام"), | |
| (re.compile(r"walaikum\s*assalam|walaykum\s*assalam|alaikum\s*assalam", re.I), "السلام علیکم"), | |
| (re.compile(r"khair\s*mubarak|kher\s*mubarak", re.I), "مبارک"), | |
| (re.compile(r"\bkher\s+he\b|\bkhair\s+he\b", re.I), "مبارک"), | |
| (re.compile(r"\badaab\b", re.I), "آداب"), | |
| (re.compile(r"\bhello\b", re.I), "ہیلو"), | |
| (re.compile(r"\bhi\b|\bhey\b|\bhelo\b", re.I), "ہیلو"), | |
| (re.compile(r"\bsalam\b", re.I), "وعلیکم السلام"), | |
| # Urdu script | |
| (re.compile(r"السلام\s*علیکم|السلام\s*عليكم|سلام\s*علیکم|سلام\s*عليكم"), "وعلیکم السلام"), | |
| (re.compile(r"وعلیکم\s*السلام|وعليكم\s*السلام"), "السلام علیکم"), | |
| (re.compile(r"خیر\s*مبارک|خیرمبارک"), "مبارک"), | |
| (re.compile(r"خیر\s*ہے"), "مبارک"), | |
| (re.compile(r"آداب|اداب"), "آداب"), | |
| (re.compile(r"ہیلو"), "ہیلو"), | |
| (re.compile(r"ہائے"), "ہائے"), | |
| (re.compile(r"^سلام$|^\s*سلام\s*$"), "وعلیکم السلام"), | |
| (re.compile(r"^جی\s*ہاں$|^ہاں$|^ہان$"), "جی"), | |
| (re.compile(r"شکریہ|شکریا"), "خوش آمدید"), | |
| ] | |
| def _strip_punctuation(text: str) -> str: | |
| return re.sub(r"[\s\u060C\u061B\u061F\u06D4\.,!?؛:]+", " ", text).strip() | |
| def _normalize_latin(text: str) -> str: | |
| return re.sub(r"\s+", " ", (text or "").strip().lower()) | |
| def _normalize_urdu(text: str) -> str: | |
| return re.sub(r"\s+", " ", _strip_punctuation(text)).strip() | |
| def _has_substance(text: str) -> bool: | |
| if not text: | |
| return False | |
| latin_words = set(re.findall(r"[a-zA-Z]+", text.lower())) | |
| if latin_words & SUBSTANCE_MARKERS_LATIN: | |
| return True | |
| urdu_words = set(re.findall(r"[\u0600-\u06FF]+", text)) | |
| if urdu_words & SUBSTANCE_MARKERS_URDU: | |
| return True | |
| if "?" in text or "؟" in text: | |
| lowered = text.lower() | |
| if any(m in lowered for m in SUBSTANCE_MARKERS_LATIN): | |
| return True | |
| if any(m in text for m in SUBSTANCE_MARKERS_URDU): | |
| return True | |
| return False | |
| def _is_pure_greeting_latin(text: str) -> bool: | |
| normalized = _normalize_latin(text) | |
| if not normalized: | |
| return False | |
| if normalized in PURE_GREETING_PHRASES_LATIN: | |
| return True | |
| words = normalized.split() | |
| filler = {"ji", "jee", "han", "haan"} | |
| if words and all(w in PURE_GREETING_PHRASES_LATIN or w in filler for w in words): | |
| return True | |
| return False | |
| def _is_pure_greeting_urdu(text: str) -> bool: | |
| if not has_arabic_script(text): | |
| return False | |
| normalized = _normalize_urdu(text) | |
| if not normalized: | |
| return False | |
| if normalized in PURE_GREETING_PHRASES_URDU: | |
| return True | |
| compact = normalized.replace("و ", "و") | |
| if compact in PURE_GREETING_PHRASES_URDU: | |
| return True | |
| words = re.findall(r"[\u0600-\u06FF]+", normalized) | |
| if words and all(w in URDU_GREETING_TOKENS or w == "ہے" for w in words): | |
| if set(words) <= (URDU_GREETING_TOKENS | {"ہے"}): | |
| return True | |
| return False | |
| def _is_pure_greeting(text: str) -> bool: | |
| return _is_pure_greeting_latin(text) or _is_pure_greeting_urdu(text) | |
| def _is_connection_check(text: str) -> bool: | |
| for pattern in CONNECTION_CHECK_PATTERNS: | |
| if pattern.search(text): | |
| return True | |
| normalized_latin = _normalize_latin(text) | |
| if normalized_latin.count("hello") >= 2 or normalized_latin.count("hi") >= 2: | |
| return True | |
| normalized_urdu = _normalize_urdu(text) | |
| if normalized_urdu.count("ہیلو") >= 2 or normalized_urdu.count("سلام") >= 2: | |
| return True | |
| return False | |
| def _message_length(text: str) -> int: | |
| stripped = _strip_punctuation(text) | |
| return len(stripped.replace(" ", "")) | |
| def classify_opening_message(text: str, turn_count: int, language_locked: bool) -> OpeningTier: | |
| """Classify message tier for early-turn routing.""" | |
| if turn_count > 3 or language_locked: | |
| return "normal" | |
| if _has_substance(text): | |
| return "greeting_with_substance" | |
| if _is_connection_check(text): | |
| return "connection_check" | |
| if _is_pure_greeting(text): | |
| return "pure_greeting" | |
| if _message_length(text) < 12 and not _has_substance(text): | |
| return "pure_greeting" | |
| return "normal" | |
| def _is_audio_connection_check(text: str) -> bool: | |
| latin = text.lower() | |
| if any(c in latin for c in ("awaaz", "awaz", "sun")): | |
| return True | |
| return any( | |
| marker in text | |
| for marker in ("آواز", "سن", "سنائی", "سناائی", "بول رہ") | |
| ) | |
| def get_short_reply(tier: OpeningTier, text: str, greeting_streak: int = 0) -> str: | |
| """ | |
| Deterministic short reply — no LLM. Urdu-only mode: all replies in Urdu. | |
| Issue 8 fix: the previous connection_check fallback branch returned the | |
| English string "Yes, I can hear you." when _is_audio_connection_check() | |
| was False (e.g. bare "ہیلو ہیلو" repetition). This produced an English | |
| TTS response in a Urdu-only session. Both branches now return Urdu. | |
| """ | |
| if tier == "connection_check": | |
| if _is_audio_connection_check(text): | |
| return "جی ہاں، آواز آ رہی ہے۔" | |
| # Issue 8 fix: was "Yes, I can hear you." — now Urdu | |
| return "جی، میں سن رہا ہوں۔" | |
| if tier == "pure_greeting": | |
| for pattern, reply in _GREETING_MIRROR: | |
| if pattern.search(text): | |
| return reply | |
| if greeting_streak >= 2: | |
| return "جی، میں آپ کی کیسے مدد کر سکتا ہوں؟" | |
| if has_arabic_script(text): | |
| return "وعلیکم السلام" | |
| # Issue 8 fix: was "Hello" — now Urdu | |
| return "ہیلو" | |
| # Issue 8 fix: was "Hello" — now Urdu | |
| return "جی" | |
| def should_short_circuit(tier: OpeningTier) -> bool: | |
| return tier in ("pure_greeting", "connection_check") |