- Create InterviewerAgent with Socratic questioning and RAG context - Build SynthesizerAgent for transcript segmentation and link generation - Integrate Google Gemini models (Flash for interviewing, Pro for synthesis) - Add structured output parsing for Zettel extraction and linking - Implement session termination detection with [END_SESSION] token - Add conversation context formatting and similarity-based neighbor filtering - Add vector service tests with mocked ChromaDB and embeddings - Test interviewer agent RAG conversations and session termination - Test synthesizer agent transcript formatting and neighbor analysis - Add prompt loader tests for external prompt system - Test all repository CRUD operations and database transactions
162 lines
7.0 KiB
Python
162 lines
7.0 KiB
Python
"""Test synthesizer agent functionality - simplified version."""
|
|
import pytest
|
|
import uuid
|
|
from unittest.mock import MagicMock, patch
|
|
from datetime import datetime
|
|
|
|
from app.services.synthesizer import SynthesizerAgent
|
|
from app.services.vector import VectorService
|
|
from app.data.models import Note
|
|
from langchain_core.documents import Document
|
|
|
|
|
|
class TestSynthesizerAgent:
|
|
"""Test SynthesizerAgent functionality with simplified mocking."""
|
|
|
|
@pytest.fixture
|
|
def mock_vector_service(self):
|
|
"""Create a mock vector service."""
|
|
return MagicMock(spec=VectorService)
|
|
|
|
@pytest.fixture
|
|
def sample_transcript(self):
|
|
"""Create a sample conversation transcript."""
|
|
return [
|
|
{"role": "user", "content": "I want to explore AI ethics"},
|
|
{"role": "assistant", "content": "What aspects of AI ethics concern you most?"},
|
|
{"role": "user", "content": "I'm worried about bias in algorithms"}
|
|
]
|
|
|
|
@pytest.fixture
|
|
def sample_note(self):
|
|
"""Create a sample note for linking."""
|
|
return Note(
|
|
id=uuid.uuid4(),
|
|
title="Algorithmic Bias",
|
|
content="AI systems can perpetuate discrimination when trained on biased datasets.",
|
|
tags=["ai", "bias", "ethics"],
|
|
session_id=uuid.uuid4(),
|
|
created_at=datetime.utcnow()
|
|
)
|
|
|
|
@pytest.fixture
|
|
def sample_neighbors(self):
|
|
"""Create sample neighboring documents."""
|
|
return [
|
|
Document(
|
|
page_content="Machine learning fairness requires careful consideration.",
|
|
metadata={"title": "ML Fairness", "tags": "ml,fairness", "similarity_score": 0.8}
|
|
),
|
|
Document(
|
|
page_content="Privacy-preserving AI techniques protect user data.",
|
|
metadata={"title": "Privacy in AI", "tags": "privacy,ai", "similarity_score": 0.6}
|
|
)
|
|
]
|
|
|
|
def test_synthesizer_initialization(self, mock_vector_service):
|
|
"""Test that SynthesizerAgent initializes correctly."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI') as mock_llm:
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
assert agent.vector_service is mock_vector_service
|
|
assert agent.llm is not None
|
|
# Verify it uses the Pro model with lower temperature
|
|
mock_llm.assert_called_once()
|
|
call_args = mock_llm.call_args
|
|
assert call_args.kwargs['temperature'] == 0.3
|
|
|
|
def test_format_transcript_for_segmentation(self, mock_vector_service, sample_transcript):
|
|
"""Test transcript formatting for segmentation."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI'):
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
result = agent._format_transcript_for_segmentation(sample_transcript)
|
|
|
|
assert "User:" in result
|
|
assert "AI Interviewer:" in result
|
|
assert "AI ethics" in result
|
|
assert "bias" in result.lower()
|
|
|
|
def test_format_transcript_for_segmentation_empty(self, mock_vector_service):
|
|
"""Test transcript formatting with empty transcript."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI'):
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
result = agent._format_transcript_for_segmentation([])
|
|
assert "Empty conversation transcript" in result
|
|
|
|
def test_format_neighbors(self, mock_vector_service, sample_neighbors):
|
|
"""Test neighbor formatting for linking."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI'):
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
result = agent._format_neighbors(sample_neighbors)
|
|
|
|
assert "Note 1:" in result
|
|
assert "Note 2:" in result
|
|
assert "ML Fairness" in result
|
|
assert "Privacy in AI" in result
|
|
assert "Similarity Score:" in result
|
|
assert "0.800" in result # Formatted similarity score
|
|
|
|
def test_format_neighbors_empty(self, mock_vector_service):
|
|
"""Test neighbor formatting with no neighbors."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI'):
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
result = agent._format_neighbors([])
|
|
assert "No existing notes found" in result
|
|
|
|
def test_format_neighbors_with_metadata(self, mock_vector_service):
|
|
"""Test neighbor formatting handles missing metadata gracefully."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI'):
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
neighbors = [
|
|
Document(
|
|
page_content="Content without much metadata",
|
|
metadata={} # Missing title, tags, similarity_score
|
|
)
|
|
]
|
|
|
|
result = agent._format_neighbors(neighbors)
|
|
assert "Note 1:" in result
|
|
assert "Content without much metadata" in result
|
|
assert "Similarity Score: 0.000" in result # Default value
|
|
|
|
def test_transcript_formatting_preserves_conversation_flow(self, mock_vector_service):
|
|
"""Test that transcript formatting preserves the conversation flow."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI'):
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
transcript = [
|
|
{"role": "user", "content": "First message"},
|
|
{"role": "assistant", "content": "First response"},
|
|
{"role": "user", "content": "Second message"},
|
|
{"role": "assistant", "content": "Second response"}
|
|
]
|
|
|
|
result = agent._format_transcript_for_segmentation(transcript)
|
|
|
|
# Check order is preserved
|
|
lines = result.split('\n\n')
|
|
assert len(lines) == 4
|
|
assert "User: First message" in lines[0]
|
|
assert "AI Interviewer: First response" in lines[1]
|
|
assert "User: Second message" in lines[2]
|
|
assert "AI Interviewer: Second response" in lines[3]
|
|
|
|
def test_agent_uses_correct_model_config(self, mock_vector_service):
|
|
"""Test that the agent is configured with the correct model settings."""
|
|
with patch('app.services.synthesizer.ChatGoogleGenerativeAI') as mock_llm_class:
|
|
with patch('app.services.synthesizer.settings') as mock_settings:
|
|
mock_settings.LLM_PRO_MODEL = "test-pro-model"
|
|
mock_settings.GOOGLE_API_KEY = "test-key"
|
|
|
|
agent = SynthesizerAgent(mock_vector_service)
|
|
|
|
# Verify the LLM was initialized with correct parameters
|
|
mock_llm_class.assert_called_once_with(
|
|
model="test-pro-model",
|
|
google_api_key="test-key",
|
|
temperature=0.3
|
|
) |