# -*- coding: utf-8 -*- """OpenAI services test module.""" import unittest from unittest.mock import patch, MagicMock, mock_open import os import shutil from openai._types import NOT_GIVEN from agentscope.service.multi_modality.openai_services import ( openai_audio_to_text, openai_text_to_audio, openai_text_to_image, openai_image_to_text, openai_edit_image, openai_create_image_variation, ) from agentscope.service import ServiceExecStatus from agentscope.service.multi_modality.openai_services import _audio_filename class TestOpenAIServices(unittest.TestCase): """Test the OpenAI services functions.""" def setUp(self) -> None: """Set up the test""" self.save_dir = os.path.join(os.path.dirname(__file__), "test_dir") os.makedirs(self.save_dir, exist_ok=True) self.maxDiff = None def tearDown(self) -> None: """Tear down the test""" if os.path.exists(self.save_dir): shutil.rmtree(self.save_dir) @patch( "agentscope.service.multi_modality.openai_services.OpenAIDALLEWrapper", ) @patch("agentscope.service.multi_modality.openai_services._download_file") def test_openai_text_to_image_success( self, _: MagicMock, mock_dalle_wrapper: MagicMock, ) -> None: """Test the openai_text_to_image function with a valid prompt.""" # Mock the OpenAIDALLEWrapper response mock_response = MagicMock() mock_response.image_urls = [ "https://example.com/image1.png", "https://example.com/image2.png", ] mock_instance = MagicMock() mock_instance.return_value = mock_response mock_dalle_wrapper.return_value = mock_instance # Call the function with a valid prompt result = openai_text_to_image( prompt="A futuristic city skyline at sunset", api_key="fake_api_key", n=2, model="dall-e-2", size="256x256", quality="standard", style="vivid", save_dir=self.save_dir, ) self.assertEqual(result.status, ServiceExecStatus.SUCCESS) expected_urls = [ os.path.join(self.save_dir, "image1.png"), os.path.join(self.save_dir, "image2.png"), ] self.assertEqual(result.content, {"image_urls": expected_urls}) # Ensure the wrapper and response methods are called correctly mock_dalle_wrapper.assert_called_once_with( config_name="text_to_image_service_call", model_name="dall-e-2", api_key="fake_api_key", ) mock_instance.assert_called_once_with( prompt="A futuristic city skyline at sunset", n=2, size="256x256", quality="standard", style="vivid", ) @patch( "agentscope.service.multi_modality.openai_services.OpenAIDALLEWrapper", ) def test_openai_text_to_image_api_error( self, mock_dalle_wrapper: MagicMock, ) -> None: """Test the openai_text_to_image function with an API error.""" # Mock an API failure response mock_instance = MagicMock() mock_instance.side_effect = Exception("API Error: Invalid request") mock_dalle_wrapper.return_value = mock_instance # Call the function with a prompt that causes an API error result = openai_text_to_image( prompt="An invalid prompt", api_key="fake_api_key", n=1, model="dall-e-2", size="256x256", quality="standard", style="vivid", ) self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertIn("API Error", result.content) # Ensure the wrapper and response methods are called correctly mock_dalle_wrapper.assert_called_once_with( config_name="text_to_image_service_call", model_name="dall-e-2", api_key="fake_api_key", ) mock_instance.assert_called_once_with( prompt="An invalid prompt", n=1, size="256x256", quality="standard", style="vivid", ) @patch( "agentscope.service.multi_modality.openai_services.OpenAIDALLEWrapper", ) @patch("agentscope.service.multi_modality.openai_services._download_file") def test_openai_text_to_image_service_error( self, mock_download_file: MagicMock, mock_dalle_wrapper: MagicMock, ) -> None: """Test the openai_text_to_image function with a service error.""" mock_response = MagicMock() mock_response.image_urls = None mock_instance = MagicMock() mock_instance.return_value = mock_response mock_dalle_wrapper.return_value = mock_instance # Call the function with a valid prompt but simulate an internal error result = openai_text_to_image( prompt="A futuristic city skyline at sunset", api_key="fake_api_key", n=2, model="dall-e-2", size="256x256", quality="standard", style="vivid", save_dir=self.save_dir, ) self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertIn("Error: Failed to generate images", result.content) # Ensure the wrapper and response methods are called correctly mock_dalle_wrapper.assert_called_once_with( config_name="text_to_image_service_call", model_name="dall-e-2", api_key="fake_api_key", ) mock_instance.assert_called_once_with( prompt="A futuristic city skyline at sunset", n=2, size="256x256", quality="standard", style="vivid", ) # Ensure _download_file is not called in case of service error mock_download_file.assert_not_called() @patch("agentscope.service.multi_modality.openai_services.OpenAI") @patch( "builtins.open", new_callable=mock_open, read_data=b"mock_audio_data", ) def test_openai_audio_to_text_success( self, _: MagicMock, mock_openai: MagicMock, ) -> None: """Test the openai_audio_to_text function with a valid audio file.""" # Mocking the OpenAI API response mock_transcription = ( mock_openai.return_value.audio.transcriptions.create.return_value ) mock_transcription.text = "This is a test transcription." # Test audio file path within the save_dir audio_file_url = os.path.join(self.save_dir, "test_audio.mp3") # Create the test file in the save_dir with open(audio_file_url, "wb") as f: f.write(b"mock_audio_data") # Call the function result = openai_audio_to_text(audio_file_url, "mock_api_key") # Assertions self.assertEqual(result.status, ServiceExecStatus.SUCCESS) self.assertEqual( result.content, {"transcription": "This is a test transcription."}, ) @patch("agentscope.service.multi_modality.openai_services.OpenAI") @patch("builtins.open", new_callable=mock_open) def test_openai_audio_to_text_error( self, _: MagicMock, mock_openai: MagicMock, ) -> None: """Test the openai_audio_to_text function with an API error.""" # Mocking an OpenAI API error mock_openai.return_value.audio.transcriptions.create.side_effect = ( Exception("API Error") ) # Test audio file path within the save_dir audio_file_url = os.path.join(self.save_dir, "test_audio.mp3") # Call the function (the mock_file_open will handle the file opening) result = openai_audio_to_text(audio_file_url, "mock_api_key") # Assertions self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertIn( "Error: Failed to transcribe audio API Error", result.content, ) @patch("agentscope.service.multi_modality.openai_services.OpenAI") def test_successful_audio_generation(self, mock_openai: MagicMock) -> None: """Test the openai_text_to_audio function with a valid text.""" # Mocking the OpenAI API response mock_response = ( mock_openai.return_value.audio.speech.create.return_value ) # Sample text and expected audio path text = "Hello, this is a test." save_dir = self.save_dir expected_audio_path = os.path.join( save_dir, f"{_audio_filename(text)}.mp3", ) # Call the function result = openai_text_to_audio(text, "mock_api_key", save_dir) # Assertions self.assertEqual(result.status, ServiceExecStatus.SUCCESS) self.assertEqual(result.content["audio_path"], expected_audio_path) mock_response.stream_to_file.assert_called_once_with( expected_audio_path, ) # Check file save @patch("agentscope.service.multi_modality.openai_services.OpenAI") def test_api_error_text_to_audio(self, mock_openai: MagicMock) -> None: """Test the openai_text_to_audio function with an API error.""" # Mocking an OpenAI API error mock_openai.return_value.audio.speech.create.side_effect = Exception( "API Error", ) # Call the function result = openai_text_to_audio( "Hello, this is a test.", "mock_api_key", self.save_dir, ) # Assertions self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertIn( "Error: Failed to generate audio. API Error", result.content, ) @patch( "agentscope.service.multi_modality.openai_services.OpenAIChatWrapper", ) def test_openai_image_to_text_success( self, mock_openai_chat_wrapper: MagicMock, ) -> None: """Test the openai_image_to_text function with a valid image URL.""" # Mock the OpenAIChatWrapper mock_wrapper_instance = MagicMock() mock_openai_chat_wrapper.return_value = mock_wrapper_instance # Mock the response mock_response = MagicMock() mock_response.text = "This is a description of the image." mock_wrapper_instance.return_value = mock_response # Test with single image URL result = openai_image_to_text( "https://example.com/image.jpg", "fake_api_key", ) # Assertions self.assertEqual(result.status, ServiceExecStatus.SUCCESS) self.assertEqual(result.content, "This is a description of the image.") mock_openai_chat_wrapper.assert_called_once_with( config_name="image_to_text_service_call", model_name="gpt-4o", api_key="fake_api_key", ) mock_wrapper_instance.format.assert_called_once() formatted_message = mock_wrapper_instance.format.call_args[0][0] self.assertEqual(formatted_message.name, "service_call") self.assertEqual(formatted_message.role, "user") self.assertEqual(formatted_message.content, "Describe the image") self.assertEqual( formatted_message.url, "https://example.com/image.jpg", ) @patch( "agentscope.service.multi_modality.openai_services.OpenAIChatWrapper", ) def test_openai_image_to_text_error( self, mock_openai_chat_wrapper: MagicMock, ) -> None: """Test the openai_image_to_text function with an API error.""" # Mock the OpenAIChatWrapper to raise an exception mock_wrapper_instance = MagicMock() mock_openai_chat_wrapper.return_value = mock_wrapper_instance mock_wrapper_instance.side_effect = Exception("API Error") # Test with single image URL result = openai_image_to_text( "https://example.com/image.jpg", "fake_api_key", ) # Assertions self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertEqual(result.content, "API Error") @patch("agentscope.service.multi_modality.openai_services.OpenAI") @patch("agentscope.service.multi_modality.openai_services._parse_url") @patch( ( "agentscope.service.multi_modality.openai_services" "._handle_openai_img_response" ), ) def test_openai_edit_image_success( self, mock_handle_response: MagicMock, mock_parse_url: MagicMock, mock_openai: MagicMock, ) -> None: """Test the openai_edit_image function with a valid image URL.""" # Mock OpenAI client mock_client = MagicMock() mock_openai.return_value = mock_client mock_response = MagicMock() mock_client.images.edit.return_value = mock_response # Mock _parse_url function mock_parse_url.side_effect = lambda x: f"parsed_{x}" # Mock _handle_openai_img_response function mock_handle_response.return_value = [ "edited_image_url1", "edited_image_url2", ] # Call the function result = openai_edit_image( image_url="original_image.png", prompt="Add a sun to the sky", api_key="fake_api_key", mask_url="mask_image.png", n=2, size="512x512", save_dir=None, ) # Assertions self.assertEqual(result.status, ServiceExecStatus.SUCCESS) self.assertEqual( result.content, {"image_urls": ["edited_image_url1", "edited_image_url2"]}, ) # Check if OpenAI client was called correctly mock_client.images.edit.assert_called_once_with( model="dall-e-2", image="parsed_original_image.png", mask="parsed_mask_image.png", prompt="Add a sun to the sky", n=2, size="512x512", ) # Check if _handle_openai_img_response was called mock_handle_response.assert_called_once_with(mock_response, None) @patch("agentscope.service.multi_modality.openai_services.OpenAI") @patch("agentscope.service.multi_modality.openai_services._parse_url") def test_openai_edit_image_error( self, mock_parse_url: MagicMock, mock_openai: MagicMock, ) -> None: """Test the openai_edit_image function with an API error.""" # Mock OpenAI client to raise an exception mock_client = MagicMock() mock_openai.return_value = mock_client mock_client.images.edit.side_effect = Exception("API Error") # Mock _parse_url function mock_parse_url.side_effect = lambda x: f"parsed_{x}" # Call the function result = openai_edit_image( image_url="original_image.png", prompt="Add a sun to the sky", api_key="fake_api_key", ) # Assertions self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertEqual(result.content, "API Error") # Check if OpenAI client was called mock_client.images.edit.assert_called_once_with( model="dall-e-2", image="parsed_original_image.png", mask=NOT_GIVEN, prompt="Add a sun to the sky", n=1, size="256x256", ) @patch("agentscope.service.multi_modality.openai_services.OpenAI") @patch("agentscope.service.multi_modality.openai_services._parse_url") @patch( ( "agentscope.service.multi_modality.openai_services" "._handle_openai_img_response" ), ) def test_openai_create_image_variation_success( self, mock_handle_response: MagicMock, mock_parse_url: MagicMock, mock_openai: MagicMock, ) -> None: """Test the openai_create_image_variation swith a valid image URL.""" # Mock OpenAI client mock_client = MagicMock() mock_openai.return_value = mock_client mock_response = MagicMock() mock_client.images.create_variation.return_value = mock_response # Mock _parse_url function mock_parse_url.return_value = "parsed_image.png" # Mock _handle_openai_img_response function mock_handle_response.return_value = [ "variation_url1", "variation_url2", ] # Call the function result = openai_create_image_variation( image_url="original_image.png", api_key="fake_api_key", n=2, size="512x512", save_dir=None, ) # Assertions self.assertEqual(result.status, ServiceExecStatus.SUCCESS) self.assertEqual( result.content, {"image_urls": ["variation_url1", "variation_url2"]}, ) # Check if OpenAI client was called correctly mock_client.images.create_variation.assert_called_once_with( model="dall-e-2", image="parsed_image.png", n=2, size="512x512", ) # Check if _handle_openai_img_response was called mock_handle_response.assert_called_once_with(mock_response, None) @patch("agentscope.service.multi_modality.openai_services.OpenAI") @patch("agentscope.service.multi_modality.openai_services._parse_url") def test_openai_create_image_variation_error( self, mock_parse_url: MagicMock, mock_openai: MagicMock, ) -> None: """Test the openai_create_image_variation with an API error.""" # Mock OpenAI client to raise an exception mock_client = MagicMock() mock_openai.return_value = mock_client mock_client.images.create_variation.side_effect = Exception( "API Error", ) # Mock _parse_url function mock_parse_url.return_value = "parsed_image.png" # Call the function result = openai_create_image_variation( image_url="original_image.png", api_key="fake_api_key", ) # Assertions self.assertEqual(result.status, ServiceExecStatus.ERROR) self.assertEqual(result.content, "API Error") # Check if OpenAI client was called mock_client.images.create_variation.assert_called_once_with( model="dall-e-2", image="parsed_image.png", n=1, size="256x256", ) if __name__ == "__main__": unittest.main()