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