Spaces:
Running
Running
Download brain/tests/test_builder.py from chintakapp/prova-core: direct link, hf CLI and curl.
- Browser
- Download file 8.42 kB
-
https://huggingface.co/spaces/chintakapp/prova-core/resolve/main/brain/tests/test_builder.py
- Command line
-
hf download hf://spaces/chintakapp/prova-core/brain/tests/test_builder.py
-
curl -L -o test_builder.py https://huggingface.co/spaces/chintakapp/prova-core/resolve/main/brain/tests/test_builder.py
8.42 kB
| """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 --------------------------- | |
| 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") | |