File size: 9,936 Bytes
4ebc6e6 47a6953 4ebc6e6 e7cf80b eb0b69b 47a6953 eb0b69b 4ebc6e6 5b8e6d8 d183415 60c3455 1961ced 60c3455 e15c2f9 4ebc6e6 e15c2f9 593d015 47a6953 b81fe79 593d015 b81fe79 1f7aa34 e15c2f9 d183415 1316064 47a6953 1316064 47a6953 d183415 | 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 | from __future__ import annotations
import pytest
from app.lang import detect_language
@pytest.mark.parametrize(
"text",
[
# The live regression: langdetect scores this pt at 99.9%.
"hola, ¿hacéis envíos a Portugal?",
"que productos vendeis y de que material son",
"quiero devolver mi pedido",
# langdetect scores this one as Catalan.
"envianme a Lisboa el paquete por favor",
"¿cuánto tarda el envío a Canarias?",
# live bug: this all-caps Spanish was answered in HUNGARIAN (langdetect
# misfired 'hu'); the expanded markers ("soy","mandais") now win.
"SOY MELILLA, MANDAIS A MELILLA",
"soy de melilla, mandais cosas a melilla?",
],
)
def test_spanish_detected(text):
assert detect_language(text) == "español"
@pytest.mark.parametrize(
"text",
[
"bom dia, que produtos tendes?",
"qual é o prazo de entrega para Lisboa?",
"obrigado, quero devolver o meu pedido",
"não recebi a minha encomenda",
],
)
def test_portuguese_detected(text):
assert detect_language(text) == "portugués de Portugal (pt-PT)"
def test_catalan_detected_not_portuguese():
# regression: "bon dia, què veneu?" was answered in Portuguese
assert detect_language("bon dia, què veneu?") == "català"
assert detect_language("bona tarda, voleu ajudar-me amb això?") == "català"
@pytest.mark.parametrize(
"text,expected",
[
("مرحبا، ماذا تبيعون في متجركم؟", "árabe (Arabic)"),
("你们卖什么产品?我想了解一下", "chino simplificado (Chinese)"),
("こんにちは、どんな商品がありますか?", "japonés (Japanese)"),
("안녕하세요, 어떤 제품을 판매하나요?", "coreano (Korean)"),
("Здравствуйте, какие товары у вас есть?", "ruso (Russian)"),
("नमस्ते, आप क्या बेचते हैं?", "hindi (Hindi)"),
("Merhaba, hangi ürünleri satıyorsunuz?", "turco (Turkish)"),
("Hallo, welke producten verkoopt u?", "neerlandés (Dutch)"),
# genuine Hungarian must STILL detect (the Melilla fix must not over-suppress hu)
("Szeretném tudni a szállítási költségeket Budapestre kérem", "húngaro (Hungarian)"),
],
)
def test_world_languages_detected(text, expected):
assert detect_language(text) == expected
def test_english_detected():
assert detect_language("where is my order, can you check the tracking?") == "English"
def test_english_with_stray_spanish_word_stays_english():
# regression: "mi" must NOT flip a clearly-English sentence to Spanish
assert detect_language("where is mi product please") == "English"
assert detect_language("hello, can you help me find a product") == "English"
@pytest.mark.parametrize(
"text",
[
# the real production bug: plain Spanish WITHOUT ¿¡ñ accents was pinned
# English because the Spanish preposition "a" tripped _looks_english.
"Hola, voy a comprar la Toorx msx70 y quiero saber los plazos de entrega "
"para envios a la peninsula.",
# same defect without a leading greeting (exercises the score path)
"quiero comprar la bici y saber los plazos de envios a la peninsula",
"necesito ayuda para elegir y comprar productos para mi pedido",
],
)
def test_plain_spanish_without_accents_is_spanish(text):
# regression: a strong Spanish word-score must beat the "a"/"to" English bias
assert detect_language(text) == "español"
@pytest.mark.parametrize(
"text",
[
"i want to talk to a human", # was detected Swahili
"hello can you help me", # was detected Welsh
"i need help with my order please",
"can you tell me where my product is",
],
)
def test_short_english_not_misdetected(text):
# regression: short English must NOT be pinned to an exotic language
assert detect_language(text) == "English"
@pytest.mark.parametrize(
"text",
[
# regression: langdetect guessed Iberian, no markers → was pinned español
"do you have garden hoses?",
"do you sell garden products?",
],
)
def test_english_misclassified_as_iberian_stays_english(text):
assert detect_language(text) == "English"
@pytest.mark.parametrize(
"text,expected",
[
# regression: dense non-Latin under 12 chars returned None (no pin → drift)
("你们卖什么?", "chino simplificado (Chinese)"),
("你们有花园用的水管吗?", "chino simplificado (Chinese)"),
("정원용 호스 있나요?", "coreano (Korean)"),
("ガーデンホース", "japonés (Japanese)"),
("لديكم خراطيم؟", "árabe (Arabic)"),
],
)
def test_short_nonlatin_detected_by_script(text, expected):
assert detect_language(text) == expected
def test_short_cyrillic_pins_a_cyrillic_language():
# exact Slavic language varies, but it must pin a Cyrillic language (not None)
cyrillic = {"ruso (Russian)", "ucraniano (Ukrainian)", "búlgaro (Bulgarian)",
"macedonio (Macedonian)"}
assert detect_language("у вас есть садовые шланги?") in cyrillic
def test_unsure_latin_text_not_forced():
# gibberish/ambiguous Latin → don't force a wrong language (let LLM decide)
assert detect_language("zxcv qwer asdf zxcv") is None
def test_short_greetings_pinned_by_lexicon():
# regression: "buenas" was answered in Portuguese
assert detect_language("buenas") == "español"
assert detect_language("hola") == "español"
assert detect_language("bonjour") == "français"
assert detect_language("hello") == "English"
assert detect_language("olá") == "portugués de Portugal (pt-PT)"
def test_unknown_short_text_not_pinned():
assert detect_language("xy") is None
assert detect_language("") is None
def test_language_guard_flags_drift():
from app.lang import needs_language_repair
ko = "coreano (Korean)"
zh = "chino simplificado (Chinese)"
# drift: Korean pinned but answer is Spanish → must repair
assert needs_language_repair("Sí, disponemos de mangueras para jardín.", ko) is True
assert needs_language_repair("Sí, tenemos varias opciones.", zh) is True
# correct: answer in the pinned script → no repair (Latin product names allowed)
assert needs_language_repair("네, 정원용 호스가 있습니다. Gobeflat Medium 모델입니다.", ko) is False
assert needs_language_repair("我们有花园用的水管。PVC 平面水管。", zh) is False
# Latin-script languages are NOT force-repaired by design: the model mirrors the
# customer's language (system prompt), and a heuristic Latin pin can be wrong —
# repairing toward a wrong pin is exactly what caused the Hungarian/Catalan
# misfire. So Latin always returns False (trust the model), only non-Latin repairs.
assert needs_language_repair("Sí, tenemos varias mangueras de jardín disponibles para ti.", "français") is False
assert needs_language_repair("Bonjour, nous avons plusieurs tuyaux d'arrosage disponibles.", "français") is False
assert needs_language_repair("anything", None) is False
def test_strip_emojis_removes_pictographs_keeps_text():
from app.lang import strip_emojis
assert strip_emojis("¡Hola! 😊 ¿En qué te ayudo?") == "¡Hola! ¿En qué te ayudo?"
assert strip_emojis("Tenemos mangueras 🌿✅ disponibles") == "Tenemos mangueras disponibles"
# non-Latin text must survive untouched (emoji ranges don't overlap scripts)
assert strip_emojis("정원용 호스 😀 있습니다") == "정원용 호스 있습니다"
assert strip_emojis("我们有水管 🚀") == "我们有水管"
assert strip_emojis("normal text") == "normal text"
def test_strip_links_removes_fabricated_links():
from app.lang import strip_links
# markdown link → plain text (the inline form is the real CTA, no 404 link)
out = strip_links("Rellena el [Formulario de contacto](https://flexigobe.com/contacto) abajo.")
assert "https://" not in out and "](" not in out
assert "Formulario de contacto" in out # keep the words, drop the link
# bare URL removed
assert "http" not in strip_links("Visita https://flexigobe.com/contacto para más.")
# clean text untouched
assert strip_links("Rellena el formulario que aparece abajo.") == \
"Rellena el formulario que aparece abajo."
def test_prompt_forbids_emojis():
from app.prompts import build_system_prompt
assert "emoji" in build_system_prompt("X").lower()
def test_prompt_pins_detected_language_else_mirrors():
# The store context is heavily Spanish and drags the model into Spanish even for
# an English customer (observed live), so a detected language is pinned EXPLICITLY;
# when detection is unsure (None) we mirror. The Hungarian misfire is fixed at the
# DETECTOR (see test_spanish_detected), not by refusing to pin.
from app.prompts import build_system_prompt
pinned = build_system_prompt("Tienda", "English").lower()
assert "english" in pinned and "entire reply" in pinned
unsure = build_system_prompt("Tienda", None).lower()
assert "same language" in unsure and "mirror" in unsure
def test_prompt_forbids_redirecting_customer_to_third_parties():
"""Regression: the bot once told a Spanish customer to 'visit Toorx's website
/ contact their customer service' instead of helping. The prompt must forbid
sending the customer to a manufacturer/third party and keep them with the
store's own team."""
from app.prompts import build_system_prompt
p = build_system_prompt("Tienda X").lower()
assert "third party" in p and "manufacturer" in p
assert "escalate_to_human" in p
|