ai-tutor-chatbot / tests /test_prompts.py
omarsol's picture
feat(prompts): add course content reproduction policy and related tests
9e804b1
Raw History Blame
5.53 kB
from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
from app.prompts import (
build_system_prompt,
ensure_kb_agents_instructions,
load_kb_agents_instructions,
)
def test_build_system_prompt_includes_local_kb_agents_instructions(
tmp_path: Path,
monkeypatch,
) -> None:
agents_path = tmp_path / "AGENTS.md"
agents_path.write_text(
"# Test KB Instructions\n\n"
"- Read `wiki/index.md` before broad questions.\n"
"- Treat `raw/` as source authority.\n",
encoding="utf-8",
)
monkeypatch.setenv("AI_TUTOR_KB_AGENTS_PATH", str(agents_path))
prompt = build_system_prompt("google-genai:gemini-3.5-flash", ())
assert "## Local KB Instructions" in prompt
assert "loaded from `data/kb/AGENTS.md`" in prompt
assert "# Test KB Instructions" in prompt
assert "Treat `raw/` as source authority." in prompt
def test_build_system_prompt_omits_local_kb_section_when_file_missing(
tmp_path: Path,
monkeypatch,
) -> None:
monkeypatch.setenv("AI_TUTOR_KB_AGENTS_PATH", str(tmp_path / "missing.md"))
prompt = build_system_prompt("google-genai:gemini-3.5-flash", ())
assert "## Local KB Instructions" not in prompt
def test_load_kb_agents_instructions_returns_empty_for_missing_path(
tmp_path: Path,
) -> None:
assert load_kb_agents_instructions(tmp_path / "missing.md") == ""
def test_build_system_prompt_without_local_tools_describes_none_of_them(
tmp_path: Path,
monkeypatch,
) -> None:
# Explicit empty source selection: the prompt must not describe or
# instruct retrieval/KB tools the agent does not have this turn.
agents_path = tmp_path / "AGENTS.md"
agents_path.write_text("# From disk\n", encoding="utf-8")
monkeypatch.setenv("AI_TUTOR_KB_AGENTS_PATH", str(agents_path))
prompt = build_system_prompt(
"google-genai:gemini-3.5-flash",
("web_search",),
include_local_tools=False,
)
assert "retrieve_tutor_context" not in prompt
assert "run_kb_command" not in prompt
assert "## Local KB Instructions" not in prompt
assert "## Knowledge base disabled" in prompt
assert "google_search" in prompt
no_tools = build_system_prompt(
"google-genai:gemini-3.5-flash",
(),
include_local_tools=False,
)
assert "tools available" not in no_tools
assert "one tool available" not in no_tools
def test_build_system_prompt_includes_course_reproduction_policy() -> None:
# The policy must reach the model whenever it can touch the corpus: both the
# full KB-browsing path and the retrieval-only path (disable_kb).
kb_on = build_system_prompt(
"google-genai:gemini-3.5-flash", (), kb_agents_instructions=""
)
retrieval_only = build_system_prompt(
"google-genai:gemini-3.5-flash", (), disable_kb=True
)
for prompt in (kb_on, retrieval_only):
assert "## Course content reproduction policy" in prompt
assert "raw/courses/" in prompt
assert "raw/docs/" in prompt
# Affordability redirect, gated on the student raising cost.
assert "louis@towardsai.net" in prompt
assert "never offer it unprompted" in prompt
def test_course_reproduction_policy_absent_without_corpus_access() -> None:
# No sources selected: the agent has no corpus to copy from, so the policy
# (which only governs corpus text) must not appear.
prompt = build_system_prompt(
"google-genai:gemini-3.5-flash", (), include_local_tools=False
)
assert "## Course content reproduction policy" not in prompt
def test_build_system_prompt_uses_explicit_instructions_without_file_read(
tmp_path: Path,
monkeypatch,
) -> None:
# The file on disk must be ignored when instructions are passed in.
agents_path = tmp_path / "AGENTS.md"
agents_path.write_text("# From disk\n", encoding="utf-8")
monkeypatch.setenv("AI_TUTOR_KB_AGENTS_PATH", str(agents_path))
explicit = build_system_prompt(
"google-genai:gemini-3.5-flash",
(),
kb_agents_instructions="# Explicit KB rules",
)
assert "# Explicit KB rules" in explicit
assert "# From disk" not in explicit
empty = build_system_prompt(
"google-genai:gemini-3.5-flash",
(),
kb_agents_instructions="",
)
assert "## Local KB Instructions" not in empty
def test_ensure_kb_agents_instructions_materializes_from_template(
tmp_path: Path,
monkeypatch,
) -> None:
template_path = tmp_path / "kb_agents_template.md"
template_path.write_text("# Generated KB rules\n", encoding="utf-8")
agents_path = tmp_path / "kb" / "AGENTS.md"
monkeypatch.setenv("AI_TUTOR_KB_AGENTS_PATH", str(agents_path))
with (
patch("app.config.KB_AGENTS_PATH", str(agents_path)),
patch("app.config.KB_AGENTS_TEMPLATE_PATH", str(template_path)),
):
instructions = ensure_kb_agents_instructions()
assert instructions == "# Generated KB rules"
assert agents_path.is_file()
def test_ensure_kb_agents_instructions_empty_when_template_missing(
tmp_path: Path,
monkeypatch,
) -> None:
agents_path = tmp_path / "kb" / "AGENTS.md"
monkeypatch.setenv("AI_TUTOR_KB_AGENTS_PATH", str(agents_path))
with (
patch("app.config.KB_AGENTS_PATH", str(agents_path)),
patch("app.config.KB_AGENTS_TEMPLATE_PATH", str(tmp_path / "missing.md")),
):
assert ensure_kb_agents_instructions() == ""