File size: 4,220 Bytes
187966e
 
 
 
 
 
 
 
f8197b1
 
 
1316064
 
 
c26455e
1316064
 
 
f8197b1
 
 
 
 
 
187966e
 
f8197b1
 
 
187966e
 
0c3405e
 
 
 
 
 
 
 
 
 
 
187966e
 
f8197b1
 
 
 
 
 
 
 
 
 
 
 
 
 
cfcec09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47a6953
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations

from app.prompts import build_system_prompt


def test_system_prompt_includes_rules_and_brand():
    p = build_system_prompt("TOORX Assist")
    assert "TOORX Assist" in p
    # language is the prominent RULE #1
    assert "RULE #1" in p
    assert "language" in p.lower()
    # A DETECTED language is pinned explicitly (the Spanish-heavy context otherwise
    # drags the model into Spanish even for an English customer). The Hungarian bug
    # is fixed at the DETECTOR, not by refusing to pin.
    pinned = build_system_prompt("X", language="portugués de Portugal (pt-PT)")
    assert "pt-PT" in pinned
    # when detection is unsure (None) we fall back to mirroring
    assert "mirror" in build_system_prompt("X", language=None).lower()
    # anti-hallucination grounding
    assert "invent" in p.lower()
    # order verification (email + order number / postal code)
    assert "email" in p.lower() and "order number" in p.lower() and "postal code" in p.lower()
    assert "NEVER" in p  # no full address / payment
    # tools referenced by name
    assert "escalate_to_human" in p
    assert "search_knowledge" in p and "search_products" in p and "lookup_order" in p
    # no emojis + don't translate identifiers
    assert "emoji" in p.lower()
    assert "variant_ids" in p


def test_system_prompt_covers_attached_photo_product_search():
    p = build_system_prompt("X")
    # the attached-file marker is explained and tied to the product search tool
    assert "[El cliente ha adjuntado" in p
    assert "CALL search_products" in p
    # attribute extraction is spelled out so the model searches with key terms
    assert "color" in p and "brand or model" in p
    # honest fallback when nothing similar exists
    assert "say so honestly" in p


def test_system_prompt_default_brand():
    assert "Asistente" in build_system_prompt("")


def test_prompt_channel_handoff_is_mutually_exclusive():
    web = build_system_prompt("X", channel="web")
    wa = build_system_prompt("X", channel="whatsapp")
    assert "show_form" in web and "WhatsApp" not in web
    assert "WhatsApp" in wa and "show_form" not in wa


def test_brand_name_braces_are_sanitized():
    # a brand name with { } must not break .format or inject placeholders
    p = build_system_prompt("Acme {evil}")
    assert "{evil}" not in p
    assert "Acme" in p


def test_business_rules_are_top_priority_and_override_ficha():
    """The merchant's custom_instructions must land HIGH in the prompt (before the
    GROUNDING block) and be framed as overriding the product sheet — otherwise weak
    models defer to the retrieved ficha (the 28mm-rule bug)."""
    rule = "Los discos de 28mm SI son compatibles con soporte de 25mm."
    p = build_system_prompt("TOORX", extra_instructions=rule)
    assert rule in p
    assert "BUSINESS RULES (HIGHEST PRIORITY" in p
    # the business block comes BEFORE the grounding block (position = weight)
    assert p.index("BUSINESS RULES") < p.index("# GROUNDING")
    # the grounding "say only what tools return" carries the override exception
    assert "a business rule" in p.lower() and "outranks the ficha" in p.lower()


def test_no_business_block_when_no_custom_instructions():
    p = build_system_prompt("TOORX")
    # the SECTION (with merchant content) is absent; the end-reminder mention of
    # "business rules" is harmless boilerplate, so check the section header.
    assert "these OVERRIDE the grounding" not in p


def test_business_rules_truncation_raised_to_6000():
    big = "x" * 5000
    p = build_system_prompt("TOORX", extra_instructions=big)
    assert big in p  # 5000-char rule set not silently cut (old cap was 2000)


def test_shipping_cost_vs_time_routing_is_disambiguated():
    """Shipping COST/zone -> get_shipping_cost; delivery TIME -> search_knowledge
    even when a destination is named (the 'cuánto tarda a Canarias' overlap)."""
    p = build_system_prompt("X")
    assert "get_shipping_cost" in p
    # the cost tool is explicitly scoped to zones/prices, not transit times
    assert "NOT transit times" in p
    # delivery-time questions with a destination are explicitly routed away
    assert "cuánto tarda a Canarias" in p