File size: 11,632 Bytes
7e54ab9
 
 
a2c51f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e54ab9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2c51f3
7e54ab9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2c51f3
 
7e54ab9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2c51f3
7e54ab9
 
 
 
a2c51f3
 
 
 
 
 
 
 
7e54ab9
 
 
a2c51f3
 
7e54ab9
 
 
 
 
 
 
 
 
a2c51f3
 
7e54ab9
a2c51f3
 
7e54ab9
 
 
a2c51f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# 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")