"""Prompt assembly — master prompt 12 (block order) and blueprint 16 (what must be tested). Block order is significant and every position here was moved because of a reported bug. The tests assert ORDER, not wording, so the persona text can be edited freely. """ from datetime import datetime import pytest from brain.persona.builder import Attachment, Channel, PromptInput, build_prompt from brain.persona.prova import HEADINGS from brain.persona.routine import WeeklyRoutine from brain.persona.types import Mode NOW = datetime(2024, 1, 1, 11, 0) # Monday, at work def make_input(**kwargs) -> PromptInput: base = { "ai_name": "Prova", "user_name": "Karnak", "mode": Mode.GF, "mood_id": "playful", "memories": ["His sister's name is Ria."], "now": NOW, "routine": WeeklyRoutine.default(), } return PromptInput(**{**base, **kwargs}) def order_of(prompt: str, *headings: str) -> list[int]: return [prompt.index(h) for h in headings] def is_sorted(values: list[int]) -> bool: return values == sorted(values) # --- GF mode --------------------------------------------------------------- def test_gf_block_order(): prompt = build_prompt(make_input()) positions = order_of( prompt, HEADINGS["persona"], HEADINGS["mode"], HEADINGS["time"], HEADINGS["routine"], HEADINGS["memories"], HEADINGS["mood"], HEADINGS["rules"], ) assert is_sorted(positions) def test_gf_mood_sits_directly_above_the_rules(): # Load-bearing: the rules block names the mood as the strongest tone signal. prompt = build_prompt(make_input()) between = prompt[prompt.index(HEADINGS["mood"]) : prompt.index(HEADINGS["rules"])] assert not any(h in between for h in HEADINGS.values() if h != HEADINGS["mood"]) def test_gf_includes_the_relationship_layer(): assert "been together a long time" in build_prompt(make_input()) def test_a_proactive_turn_replaces_the_rules_block(): prompt = build_prompt(make_input(proactive=True)) assert HEADINGS["rules"] not in prompt assert HEADINGS["proactive"] in prompt def test_a_proactive_turn_carries_the_checkin_block(): # PROACTIVE_PROMPT names this block by name, so omitting it would point at nothing. prompt = build_prompt(make_input(proactive=True)) assert HEADINGS["checkin"] in prompt def test_an_ordinary_turn_has_no_checkin_block(): assert HEADINGS["checkin"] not in build_prompt(make_input()) # --- Normal mode ----------------------------------------------------------- def test_normal_block_order(): prompt = build_prompt(make_input(mode=Mode.NORMAL)) positions = order_of( prompt, HEADINGS["persona"], HEADINGS["mode"], HEADINGS["time"], HEADINGS["memories"], HEADINGS["rules"], ) assert is_sorted(positions) def test_normal_never_sends_a_mood_block(): assert HEADINGS["mood"] not in build_prompt(make_input(mode=Mode.NORMAL, mood_id="naughty")) def test_normal_never_sends_the_relationship_layer(): prompt = build_prompt(make_input(mode=Mode.NORMAL)) assert "been together a long time" not in prompt assert "your partner" not in prompt.lower() def test_normal_uses_the_professional_mode_block(): prompt = build_prompt(make_input(mode=Mode.NORMAL)) assert "MODE: PROFESSIONAL (NORMAL)" in prompt assert "executive assistant" in prompt def test_normal_carries_hud_context_when_the_client_sends_it(): prompt = build_prompt(make_input(mode=Mode.NORMAL, hud={"city": "Kolkata", "weather": "30C"})) assert HEADINGS["hud"] in prompt assert "Kolkata" in prompt def test_normal_omits_the_hud_block_when_there_is_nothing_to_say(): assert HEADINGS["hud"] not in build_prompt(make_input(mode=Mode.NORMAL)) def test_normal_gets_a_routine_status_line_not_the_full_week(): prompt = build_prompt(make_input(mode=Mode.NORMAL)) assert "at the office, working" in prompt assert "leaves home around" not in prompt # --- Memory scope ---------------------------------------------------------- def test_normal_mode_rejects_private_memories_even_if_they_are_passed_in(): # Defence in depth: retrieval filters by scope, and so does the builder. prompt = build_prompt( make_input( mode=Mode.NORMAL, memories=["His sister's name is Ria."], private_memories=["He calls her shona."], ) ) assert "Ria" in prompt assert "shona" not in prompt def test_normal_sees_private_is_the_one_switch_that_lets_private_facts_through(): # Master prompt 4.5: off by default; Karnak can turn it on, with a warning in Settings. prompt = build_prompt( make_input( mode=Mode.NORMAL, memories=["His sister's name is Ria."], private_memories=["He calls her shona."], normal_sees_private=True, ) ) assert "shona" in prompt def test_gf_mode_sees_both_scopes(): prompt = build_prompt( make_input(memories=["His sister's name is Ria."], private_memories=["He calls her shona."]) ) assert "Ria" in prompt assert "shona" in prompt def test_the_memory_block_is_omitted_when_nothing_cleared_the_floor(): assert HEADINGS["memories"] not in build_prompt(make_input(memories=[])) # --- Style bank (master prompt 5.3) --------------------------------------- def test_style_bank_sits_after_memories_and_before_mood_in_gf(): prompt = build_prompt( make_input(style_examples=[("aaj boss ta abar...", "Abar? 😏 ki bollo ebar")]) ) positions = order_of(prompt, HEADINGS["memories"], HEADINGS["style"], HEADINGS["mood"]) assert is_sorted(positions) def test_style_bank_sits_before_the_rules_in_normal(): prompt = build_prompt( make_input(mode=Mode.NORMAL, style_examples=[("what's on my plate?", "Three items today.")]) ) positions = order_of(prompt, HEADINGS["memories"], HEADINGS["style"], HEADINGS["rules"]) assert is_sorted(positions) def test_no_style_block_when_the_bank_is_empty_or_disabled(): assert HEADINGS["style"] not in build_prompt(make_input(style_examples=[])) # --- Turn overrides, which must outrank the mood --------------------------- @pytest.mark.parametrize("attachment", [Attachment.PHOTO, Attachment.STICKER]) def test_attachment_blocks_sit_after_the_rules(attachment): # A playful mood turning a photo of breakfast into a joke is the bug this ordering fixes. prompt = build_prompt(make_input(attachment=attachment)) assert prompt.index(HEADINGS["rules"]) < prompt.index(HEADINGS[attachment.value]) def test_a_photo_in_normal_mode_is_told_to_stay_friendly_not_romantic(): prompt = build_prompt(make_input(mode=Mode.NORMAL, attachment=Attachment.PHOTO)) assert "friendly, not romantic" in prompt def test_the_ambiguity_block_sits_after_the_rules(): prompt = build_prompt(make_input(ambiguous_message=True)) assert prompt.index(HEADINGS["rules"]) < prompt.index(HEADINGS["ambiguity"]) def test_the_corrective_note_is_last(): prompt = build_prompt( make_input(attachment=Attachment.PHOTO, ambiguous_message=True, corrective_note="FIX IT") ) assert prompt.rstrip().endswith("FIX IT") def test_a_mode_switch_adds_a_marker(): prompt = build_prompt(make_input(mode=Mode.NORMAL, mode_just_changed=True)) assert HEADINGS["mode_changed"] in prompt assert "others may be around" in prompt.lower() def test_call_channel_adds_the_speaking_line(): prompt = build_prompt(make_input(channel=Channel.CALL)) assert "voice call" in prompt.lower() # --- Names ----------------------------------------------------------------- def test_names_are_substituted_and_no_placeholder_survives(): prompt = build_prompt(make_input()) assert "{{ai_name}}" not in prompt assert "{{user_name}}" not in prompt assert "Prova" in prompt assert "Karnak" in prompt def test_a_blank_user_name_falls_back_to_something_non_assistant(): prompt = build_prompt(make_input(user_name="")) assert "the user" not in prompt.lower() assert "{{user_name}}" not in prompt def test_blocks_are_separated_by_a_blank_line_and_never_start_with_one(): prompt = build_prompt(make_input()) assert "\n\n\n" not in prompt assert not prompt.startswith("\n")