haiku.rag/tests/agents/chat/test_features.py
2026-02-10 14:38:14 +02:00

160 lines
5.4 KiB
Python

from pydantic_ai import FunctionToolset
from haiku.rag.agents.chat.agent import (
DEFAULT_FEATURES,
FEATURE_ANALYSIS,
FEATURE_DOCUMENTS,
FEATURE_QA,
FEATURE_SEARCH,
ChatDeps,
create_chat_agent,
)
from haiku.rag.agents.chat.prompts import build_chat_prompt
from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config
from haiku.rag.tools.context import ToolContext
from haiku.rag.tools.qa import QA_SESSION_NAMESPACE, QASessionState
from haiku.rag.tools.session import SESSION_NAMESPACE, SessionState
def _count_function_toolsets(agent) -> int:
"""Count FunctionToolset instances in an agent (excludes internal toolsets)."""
return sum(1 for t in agent.toolsets if type(t) is FunctionToolset)
# =============================================================================
# Feature Selection Tests
# =============================================================================
def test_default_features(temp_db_path):
"""Default features create search + document + qa toolsets and register both states."""
client = HaikuRAG(temp_db_path, create=True)
context = ToolContext()
agent = create_chat_agent(Config, client, context)
# Should have 3 toolsets (search, document, qa)
assert _count_function_toolsets(agent) == 3
# Both SessionState and QASessionState should be registered
assert context.get(SESSION_NAMESPACE, SessionState) is not None
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is not None
client.close()
def test_search_only(temp_db_path):
"""features=["search"] creates only search toolset, no QASessionState."""
client = HaikuRAG(temp_db_path, create=True)
context = ToolContext()
agent = create_chat_agent(Config, client, context, features=[FEATURE_SEARCH])
assert _count_function_toolsets(agent) == 1
# SessionState always registered, but QASessionState should NOT be
assert context.get(SESSION_NAMESPACE, SessionState) is not None
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is None
client.close()
def test_search_and_documents(temp_db_path):
"""features=["search", "documents"] creates both toolsets, no QASessionState."""
client = HaikuRAG(temp_db_path, create=True)
context = ToolContext()
agent = create_chat_agent(
Config, client, context, features=[FEATURE_SEARCH, FEATURE_DOCUMENTS]
)
assert _count_function_toolsets(agent) == 2
assert context.get(SESSION_NAMESPACE, SessionState) is not None
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is None
client.close()
def test_all_features(temp_db_path):
"""All four features create four toolsets."""
client = HaikuRAG(temp_db_path, create=True)
context = ToolContext()
agent = create_chat_agent(
Config,
client,
context,
features=[FEATURE_SEARCH, FEATURE_DOCUMENTS, FEATURE_QA, FEATURE_ANALYSIS],
)
assert _count_function_toolsets(agent) == 4
assert context.get(SESSION_NAMESPACE, SessionState) is not None
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is not None
client.close()
def test_no_qa_skips_qa_session_state(temp_db_path):
"""Without QA feature, QASessionState is not registered."""
client = HaikuRAG(temp_db_path, create=True)
context = ToolContext()
create_chat_agent(
Config, client, context, features=[FEATURE_SEARCH, FEATURE_DOCUMENTS]
)
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is None
client.close()
def test_chat_deps_state_without_qa(temp_db_path):
"""ChatDeps.state getter omits qa_history/session_context when QASessionState absent."""
client = HaikuRAG(temp_db_path, create=True)
context = ToolContext()
create_chat_agent(Config, client, context, features=[FEATURE_SEARCH])
deps = ChatDeps(config=Config, tool_context=context, session_id="test")
state = deps.state
assert "session_id" in state
# SessionState fields should be present
assert "document_filter" in state
assert "citation_registry" in state
assert "citations" in state
# QA fields should NOT be present
assert "qa_history" not in state
assert "session_context" not in state
client.close()
# =============================================================================
# Prompt Composition Tests
# =============================================================================
def test_build_chat_prompt_default():
"""Default features produce prompt mentioning all standard tools."""
prompt = build_chat_prompt(DEFAULT_FEATURES)
assert "list_documents" in prompt
assert "get_document" in prompt
assert "summarize_document" in prompt
assert "ask" in prompt
assert "search" in prompt
assert "analyze" not in prompt
def test_build_chat_prompt_search_only():
"""Search-only prompt doesn't mention ask or document tools."""
prompt = build_chat_prompt([FEATURE_SEARCH])
assert "search" in prompt
assert '"ask"' not in prompt
assert '"list_documents"' not in prompt
assert '"get_document"' not in prompt
assert '"summarize_document"' not in prompt
def test_build_chat_prompt_includes_analysis():
"""Analysis feature adds analyze guidance to prompt."""
prompt = build_chat_prompt(
[FEATURE_SEARCH, FEATURE_QA, FEATURE_DOCUMENTS, FEATURE_ANALYSIS]
)
assert "analyze" in prompt
assert "search" in prompt
assert "ask" in prompt