feat(05-01): AgentTemplate ORM model, migration 007, and system prompt builder
- Add AgentTemplate ORM model to tenant.py (global, not tenant-scoped) - Create migration 007 with agent_templates table and 7 seed templates - Create shared/prompts/system_prompt_builder.py with build_system_prompt() - AI transparency clause always present (non-negotiable per Phase 1 decision) - Unit tests pass (17 tests, all sections verified)
This commit is contained in:
168
tests/unit/test_system_prompt_builder.py
Normal file
168
tests/unit/test_system_prompt_builder.py
Normal file
@@ -0,0 +1,168 @@
|
||||
"""
|
||||
Unit tests for shared.prompts.system_prompt_builder.
|
||||
|
||||
Tests verify:
|
||||
- Full prompt with all fields produces expected sections
|
||||
- Minimal prompt (name + role only) still includes AI transparency clause
|
||||
- Empty tools and escalation_rules omit those sections
|
||||
- AI transparency clause is always present regardless of inputs
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from shared.prompts.system_prompt_builder import build_system_prompt
|
||||
|
||||
AI_TRANSPARENCY_CLAUSE = "When directly asked if you are an AI, always disclose that you are an AI assistant."
|
||||
|
||||
|
||||
class TestBuildSystemPromptFull:
|
||||
"""Test build_system_prompt with all fields populated."""
|
||||
|
||||
def test_contains_name(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Customer Support",
|
||||
persona="Friendly and helpful",
|
||||
tool_assignments=["knowledge_base_search"],
|
||||
escalation_rules=[{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}],
|
||||
)
|
||||
assert "You are Mara" in prompt
|
||||
|
||||
def test_contains_role(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Customer Support",
|
||||
persona="Friendly and helpful",
|
||||
tool_assignments=["knowledge_base_search"],
|
||||
escalation_rules=[{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}],
|
||||
)
|
||||
assert "Customer Support" in prompt
|
||||
|
||||
def test_contains_persona(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Customer Support",
|
||||
persona="Friendly and helpful",
|
||||
tool_assignments=["knowledge_base_search"],
|
||||
escalation_rules=[{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}],
|
||||
)
|
||||
assert "Friendly and helpful" in prompt
|
||||
|
||||
def test_contains_tool(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Customer Support",
|
||||
persona="Friendly and helpful",
|
||||
tool_assignments=["knowledge_base_search"],
|
||||
escalation_rules=[{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}],
|
||||
)
|
||||
assert "knowledge_base_search" in prompt
|
||||
|
||||
def test_contains_escalation_rule(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Customer Support",
|
||||
persona="Friendly and helpful",
|
||||
tool_assignments=["knowledge_base_search"],
|
||||
escalation_rules=[{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}],
|
||||
)
|
||||
assert "billing_dispute AND attempts > 2" in prompt
|
||||
|
||||
def test_contains_ai_transparency_clause(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Customer Support",
|
||||
persona="Friendly and helpful",
|
||||
tool_assignments=["knowledge_base_search"],
|
||||
escalation_rules=[{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}],
|
||||
)
|
||||
assert AI_TRANSPARENCY_CLAUSE in prompt
|
||||
|
||||
|
||||
class TestBuildSystemPromptMinimal:
|
||||
"""Test build_system_prompt with only name and role provided."""
|
||||
|
||||
def test_minimal_contains_name(self) -> None:
|
||||
prompt = build_system_prompt(name="Alex", role="Sales Assistant")
|
||||
assert "You are Alex" in prompt
|
||||
|
||||
def test_minimal_contains_role(self) -> None:
|
||||
prompt = build_system_prompt(name="Alex", role="Sales Assistant")
|
||||
assert "Sales Assistant" in prompt
|
||||
|
||||
def test_minimal_contains_ai_transparency_clause(self) -> None:
|
||||
prompt = build_system_prompt(name="Alex", role="Sales Assistant")
|
||||
assert AI_TRANSPARENCY_CLAUSE in prompt
|
||||
|
||||
def test_minimal_is_string(self) -> None:
|
||||
prompt = build_system_prompt(name="Alex", role="Sales Assistant")
|
||||
assert isinstance(prompt, str)
|
||||
assert len(prompt) > 0
|
||||
|
||||
|
||||
class TestBuildSystemPromptEmptySections:
|
||||
"""Test that empty tools and escalation_rules omit those sections."""
|
||||
|
||||
def test_empty_tools_omits_tools_section(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Bob",
|
||||
role="Office Manager",
|
||||
persona="Organized and efficient",
|
||||
tool_assignments=[],
|
||||
escalation_rules=[],
|
||||
)
|
||||
# Should not contain a tools header/section
|
||||
assert "tools:" not in prompt.lower() or "tools" not in prompt.split("\n")[0]
|
||||
|
||||
def test_empty_escalation_omits_escalation_section(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Bob",
|
||||
role="Office Manager",
|
||||
persona="Organized and efficient",
|
||||
tool_assignments=[],
|
||||
escalation_rules=[],
|
||||
)
|
||||
# Should not contain an escalation section
|
||||
assert "Escalation" not in prompt
|
||||
|
||||
def test_none_tools_omits_tools_section(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Bob",
|
||||
role="Office Manager",
|
||||
tool_assignments=None,
|
||||
escalation_rules=None,
|
||||
)
|
||||
assert "Escalation" not in prompt
|
||||
|
||||
def test_empty_still_has_ai_transparency(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Bob",
|
||||
role="Office Manager",
|
||||
tool_assignments=[],
|
||||
escalation_rules=[],
|
||||
)
|
||||
assert AI_TRANSPARENCY_CLAUSE in prompt
|
||||
|
||||
|
||||
class TestBuildSystemPromptAIClauseAlwaysPresent:
|
||||
"""AI transparency clause must always be present — non-negotiable."""
|
||||
|
||||
def test_ai_clause_present_full_args(self) -> None:
|
||||
prompt = build_system_prompt(
|
||||
name="Mara",
|
||||
role="Support",
|
||||
persona="Helpful",
|
||||
tool_assignments=["kb_search"],
|
||||
escalation_rules=[{"condition": "x", "action": "handoff_human"}],
|
||||
)
|
||||
assert AI_TRANSPARENCY_CLAUSE in prompt
|
||||
|
||||
def test_ai_clause_present_name_role_only(self) -> None:
|
||||
prompt = build_system_prompt(name="Z", role="Y")
|
||||
assert AI_TRANSPARENCY_CLAUSE in prompt
|
||||
|
||||
def test_ai_clause_present_with_persona_only(self) -> None:
|
||||
prompt = build_system_prompt(name="Sam", role="Analyst", persona="Detail-oriented")
|
||||
assert AI_TRANSPARENCY_CLAUSE in prompt
|
||||
Reference in New Issue
Block a user