async def test_hybrid_search_strategy(self, mock_providers):
        """Test hybrid search combining keyword and semantic search."""
        # Mock search results
        keyword_results = {
            "chunk_search_results": [
                {
                    "chunk_id": f"keyword-chunk-{i}",
                    "document_id": f"doc-{i}",
                    "text": f"Keyword match {i} about Aristotle's ethics.",
                    "metadata": {"source": f"source-{i}"},
                    "score": 0.95 - (i * 0.05),
                }
                for i in range(3)
            ]
        }

        semantic_results = {
            "chunk_search_results": [
                {
                    "chunk_id": f"semantic-chunk-{i}",
                    "document_id": f"doc-{i+5}",
                    "text": f"Semantic match {i} about virtue ethics philosophy.",
                    "metadata": {"source": f"source-{i+5}"},
                    "score": 0.9 - (i * 0.05),
                }
                for i in range(3)
            ]
        }

        # Mock hybrid search function
        async def perform_hybrid_search(query, **kwargs):
            # Perform both search types
            # In real implementation, these would be actual search calls
            keyword_results_copy = keyword_results.copy()
            semantic_results_copy = semantic_results.copy()

            # Combine and deduplicate results
            combined_results = {
                "chunk_search_results":
                    keyword_results_copy["chunk_search_results"][:2] +
                    semantic_results_copy["chunk_search_results"][:2]
            }

            return combined_results

        # Mock RAG processor using hybrid search
        class HybridSearchRAGProcessor:
            def __init__(self, providers):
                self.providers = providers
                # Fix the prompt builder to include actual content
                self.prompt_builder = MagicMock()

                # Configure the prompt builder to actually include the search results in the prompt
                async def build_prompt_with_content(query, search_results, **kwargs):
                    context = ""
                    for result in search_results.get("chunk_search_results", []):
                        context += f"{result.get('text', '')}\n\n"

                    return [
                        {"role": "system", "content": f"System prompt with hybrid context:\n\n{context}"},
                        {"role": "user", "content": query}
                    ]

                self.prompt_builder.build_prompt = AsyncMock(side_effect=build_prompt_with_content)

                # Configure LLM to return a valid response
                self.providers.llm.aget_completion = AsyncMock(return_value={
                    "choices": [{"message": {"content": "LLM generated response"}}]
                })

            async def generate_with_hybrid_search(self, query):
                # Perform hybrid search
                search_results = await perform_hybrid_search(query)

                # Build prompt with combined results
                messages = await self.prompt_builder.build_prompt(
                    query=query,
                    search_results=search_results
                )

                # Generate response
                response = await self.providers.llm.aget_completion(messages=messages)
                return response["choices"][0]["message"]["content"]

        # Create processor and generate response
        processor = HybridSearchRAGProcessor(mock_providers)
        query = "What did Aristotle say about ethics?"

        response = await processor.generate_with_hybrid_search(query)

        # Check that the LLM was called with the hybrid search results
        call_args = mock_providers.llm.aget_completion.call_args[1]
        messages = call_args["messages"]

        # Find the system message
        system_message = next((m for m in messages if m["role"] == "system"), None)

        # Verify both result types are in the context
        assert "Keyword match" in system_message["content"], "System message should include keyword matches"
        assert "Semantic match" in system_message["content"], "System message should include semantic matches"

        # Check the final response
        assert response == "LLM generated response", "Should return the mocked LLM response"
Test hybrid search combining keyword and semantic search.
py/tests/unit/retrieval/test_rag_processing.py