import unittest from unittest.mock import MagicMock, patch import json import sys import os # Mock missing dependencies BEFORE importing app sys.modules["torch"] = MagicMock() sys.modules["transformers"] = MagicMock() sys.modules["gradio"] = MagicMock() sys.modules["numpy"] = MagicMock() sys.modules["scipy"] = MagicMock() sys.modules["scipy.io"] = MagicMock() sys.modules["scipy.io.wavfile"] = MagicMock() sys.modules["accelerate"] = MagicMock() # Add parent directory to path to import app sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from app import build_agent_prompt, chat_agent_stream class TestNameExtraction(unittest.TestCase): def setUp(self): self.mock_history = [] self.mock_vs_state = MagicMock() self.mock_mh_state = MagicMock() def test_prompt_includes_name_argument_definition(self): """Verify that the system prompt includes the 'name' argument in tool definition.""" prompt = build_agent_prompt("Hello", [], [], language="English") # Check for the specific tool definition we added expected_part = '"name": "str (Optional. Use ONLY if the user explicitly stated their name, otherwise omit)"' self.assertIn(expected_part, prompt) @patch('app.get_llm') @patch('app.TextIteratorStreamer') @patch('app.retrieve_relevant_chunks') @patch('app.detect_language') @patch('app.get_oracle_data') def test_oracle_call_with_name(self, mock_get_oracle_data, mock_detect, mock_retrieve, mock_streamer_cls, mock_get_llm): """Test that the agent calls get_oracle_data with the extracted name.""" # Setup mocks mock_detect.return_value = "English" mock_retrieve.return_value = [] mock_model = MagicMock() mock_processor = MagicMock() mock_get_llm.return_value = (mock_model, mock_processor) # Mock streamer to yield tool call tool_call_json = json.dumps({ "name": "oracle_consultation", "arguments": { "topic": "Future", "name": "Julian" } }) tool_call_text = f"{tool_call_json}" # The loop iterates over the streamer mock_inst = mock_streamer_cls.return_value mock_inst.__iter__.side_effect = [ iter([tool_call_text]), # First turn yields tool call iter(["Done"]), # Second turn (post tool) yields done iter([]) ] # Mock oracle return mock_get_oracle_data.return_value = {"wisdom_nodes": []} # Run generator # We need to list() it to exhaust the generator and force execution list(chat_agent_stream("Consult oracle for Julian", [], self.mock_vs_state, self.mock_mh_state)) # Verify get_oracle_data was called with correct name mock_get_oracle_data.assert_called() call_args = mock_get_oracle_data.call_args self.assertEqual(call_args.kwargs.get('name'), "Julian") self.assertEqual(call_args.kwargs.get('topic'), "Future") @patch('app.get_llm') @patch('app.TextIteratorStreamer') @patch('app.retrieve_relevant_chunks') @patch('app.detect_language') @patch('app.get_oracle_data') def test_oracle_call_without_name_defaults_to_seeker(self, mock_get_oracle_data, mock_detect, mock_retrieve, mock_streamer_cls, mock_get_llm): """Test that the agent defaults to 'Seeker' if no name is provided.""" # Setup mocks mock_detect.return_value = "English" mock_retrieve.return_value = [] mock_get_llm.return_value = (MagicMock(), MagicMock()) # Mock streamer to yield tool call WITHOUT name tool_call_json = json.dumps({ "name": "oracle_consultation", "arguments": { "topic": "Destiny" } }) tool_call_text = f"{tool_call_json}" mock_inst = mock_streamer_cls.return_value mock_inst.__iter__.side_effect = [ iter([tool_call_text]), iter(["Done"]), iter([]) ] mock_get_oracle_data.return_value = {"wisdom_nodes": []} list(chat_agent_stream("Consult oracle", [], self.mock_vs_state, self.mock_mh_state)) mock_get_oracle_data.assert_called() call_args = mock_get_oracle_data.call_args self.assertEqual(call_args.kwargs.get('name'), "Seeker") self.assertEqual(call_args.kwargs.get('topic'), "Destiny") if __name__ == '__main__': unittest.main()