init
This commit is contained in:
0
AlgoriAgent/tests/__init__.py
Normal file
0
AlgoriAgent/tests/__init__.py
Normal file
83
AlgoriAgent/tests/agent_test.py
Normal file
83
AlgoriAgent/tests/agent_test.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for agent classes and functions
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from agentscope.agents import AgentBase
|
||||
|
||||
|
||||
class TestAgent(AgentBase):
|
||||
"""An agent for test usage"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
sys_prompt: str = None,
|
||||
**kwargs: dict,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name=name,
|
||||
sys_prompt=sys_prompt,
|
||||
model_config_name=(
|
||||
kwargs["model_config"] if "model_config" in kwargs else None
|
||||
),
|
||||
use_memory=(
|
||||
kwargs["use_memory"] if "use_memory" in kwargs else None
|
||||
),
|
||||
memory_config=(
|
||||
kwargs["memory_config"] if "memory_config" in kwargs else None
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TestAgentCopy(TestAgent):
|
||||
"""A copy of testagent"""
|
||||
|
||||
|
||||
class BasicAgentTest(unittest.TestCase):
|
||||
"""Test cases for basic agents"""
|
||||
|
||||
def test_agent_init(self) -> None:
|
||||
"""Test the init of agentbase sub-class."""
|
||||
a1 = TestAgent(
|
||||
"a",
|
||||
"Hi",
|
||||
use_memory=False, # type: ignore[arg-type]
|
||||
attribute_1="hello world", # type: ignore[arg-type]
|
||||
)
|
||||
self.assertTupleEqual(
|
||||
a1._init_settings["args"], # pylint: disable=W0212
|
||||
(
|
||||
"a",
|
||||
"Hi",
|
||||
),
|
||||
)
|
||||
self.assertDictEqual(
|
||||
a1._init_settings["kwargs"], # pylint: disable=W0212
|
||||
{"use_memory": False, "attribute_1": "hello world"},
|
||||
)
|
||||
a2 = TestAgent(
|
||||
"b",
|
||||
sys_prompt="Hello",
|
||||
attribute_2="Bye", # type: ignore[arg-type]
|
||||
)
|
||||
self.assertTupleEqual(
|
||||
a2._init_settings["args"], # pylint: disable=W0212
|
||||
("b",),
|
||||
)
|
||||
self.assertDictEqual(
|
||||
a2._init_settings["kwargs"], # pylint: disable=W0212
|
||||
{"sys_prompt": "Hello", "attribute_2": "Bye"},
|
||||
)
|
||||
self.assertNotEqual(a1.agent_id, a2.agent_id)
|
||||
self.assertTrue(a1.agent_id.startswith("TestAgent"))
|
||||
self.assertTrue(a2.agent_id.startswith("TestAgent"))
|
||||
a3 = TestAgentCopy("c")
|
||||
self.assertTrue(a3.agent_id.startswith("TestAgentCopy"))
|
||||
a4 = TestAgent(
|
||||
"d",
|
||||
)
|
||||
a4._agent_id = "agent_id_for_d" # pylint: disable=W0212
|
||||
self.assertEqual(a4.agent_id, "agent_id_for_d")
|
||||
308
AlgoriAgent/tests/dashscope_services_test.py
Normal file
308
AlgoriAgent/tests/dashscope_services_test.py
Normal file
@@ -0,0 +1,308 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Dashscope Multi-Modality Service Test."""
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock, mock_open
|
||||
|
||||
from agentscope.service import ServiceResponse, ServiceExecStatus
|
||||
from agentscope.service import (
|
||||
dashscope_image_to_text,
|
||||
dashscope_text_to_image,
|
||||
dashscope_text_to_audio,
|
||||
)
|
||||
|
||||
|
||||
class TestDashScopeServices(unittest.TestCase):
|
||||
"""Test DashScope Multi-Modality Services."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Set up the test."""
|
||||
self.save_dir = "./test_dir/"
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Tear down the test."""
|
||||
# Remove the test directory
|
||||
if os.path.exists(self.save_dir):
|
||||
shutil.rmtree(self.save_dir)
|
||||
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services.os.path.abspath",
|
||||
)
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services._download_file",
|
||||
)
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services."
|
||||
"DashScopeImageSynthesisWrapper",
|
||||
)
|
||||
def test_dashscope_text_to_image(
|
||||
self,
|
||||
mock_wrapper_cls: MagicMock,
|
||||
mock_download_file: MagicMock,
|
||||
mock_abspath: MagicMock,
|
||||
) -> None:
|
||||
"""Test DashScope Image to Text Service."""
|
||||
# Configure the mocks
|
||||
mock_instance = MagicMock()
|
||||
mock_wrapper_cls.return_value = mock_instance
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.image_urls = [
|
||||
"https://xx/RESULT_URL1.png",
|
||||
"https://xx/RESULT_URL2.png",
|
||||
"https://xx/RESULT_URL3.png",
|
||||
"https://xx/RESULT_URL4.png",
|
||||
]
|
||||
mock_instance.return_value = mock_response
|
||||
|
||||
mock_download_file.return_value = None
|
||||
|
||||
mock_abspath.side_effect = lambda x: f"/abc/{x}"
|
||||
|
||||
# Call the function under test
|
||||
prompt = "fake-query-prompt"
|
||||
api_key = "fake-api"
|
||||
number_of_images = 4
|
||||
size = "1024x1024"
|
||||
model = "fake-model"
|
||||
save_dir = "./test_dir/"
|
||||
|
||||
results = dashscope_text_to_image(
|
||||
prompt,
|
||||
api_key,
|
||||
number_of_images,
|
||||
size,
|
||||
model,
|
||||
save_dir,
|
||||
)
|
||||
|
||||
# Expected result
|
||||
expected_content = {
|
||||
"image_urls": [
|
||||
f"/abc/{self.save_dir[:-1]}/RESULT_URL1.png",
|
||||
f"/abc/{self.save_dir[:-1]}/RESULT_URL2.png",
|
||||
f"/abc/{self.save_dir[:-1]}/RESULT_URL3.png",
|
||||
f"/abc/{self.save_dir[:-1]}/RESULT_URL4.png",
|
||||
],
|
||||
}
|
||||
self.assertEqual(results.status, ServiceExecStatus.SUCCESS)
|
||||
self.assertEqual(results.content, expected_content)
|
||||
|
||||
@patch(
|
||||
(
|
||||
"agentscope.service.multi_modality.dashscope_services."
|
||||
"DashScopeImageSynthesisWrapper"
|
||||
),
|
||||
)
|
||||
@patch("agentscope.service.multi_modality.dashscope_services.os.makedirs")
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services.os.path.exists",
|
||||
)
|
||||
def test_dashscope_text_to_image_failure(
|
||||
self,
|
||||
mock_os_path_exists: MagicMock,
|
||||
mock_os_makedirs: MagicMock,
|
||||
mock_wrapper_cls: MagicMock,
|
||||
) -> None:
|
||||
"""Test DashScope Image to Text Service failure."""
|
||||
# Configure the mocks
|
||||
mock_os_path_exists.return_value = False
|
||||
|
||||
mock_instance = mock_wrapper_cls.return_value
|
||||
mock_instance.return_value.image_urls = (
|
||||
None # Simulate failure by not returning URLs
|
||||
)
|
||||
|
||||
# Call the function under test
|
||||
prompt = "fake-query-prompt"
|
||||
api_key = "fake-api"
|
||||
results = dashscope_text_to_image(prompt, api_key)
|
||||
|
||||
# Verify the wrapper call
|
||||
mock_wrapper_cls.assert_called_once_with(
|
||||
config_name="dashscope-text-to-image-service",
|
||||
model_name="wanx-v1",
|
||||
api_key=api_key,
|
||||
)
|
||||
mock_instance.assert_called_once_with(
|
||||
prompt=prompt,
|
||||
n=1,
|
||||
size="1024*1024",
|
||||
)
|
||||
|
||||
# Verify no directory creation for failure
|
||||
mock_os_makedirs.assert_not_called()
|
||||
|
||||
# Expected result
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.ERROR,
|
||||
content="Error: Failed to generate images",
|
||||
)
|
||||
|
||||
self.assertEqual(results.status, expected_result.status)
|
||||
self.assertEqual(results.content, expected_result.content)
|
||||
|
||||
@patch(
|
||||
(
|
||||
"agentscope.service.multi_modality.dashscope_services."
|
||||
"DashScopeMultiModalWrapper"
|
||||
),
|
||||
)
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services.os.path.abspath",
|
||||
)
|
||||
def test_dashscope_image_to_text_success(
|
||||
self,
|
||||
mock_abspath: MagicMock,
|
||||
mock_wrapper_cls: MagicMock,
|
||||
) -> None:
|
||||
"""Test successful image to text conversion."""
|
||||
# Configure the mocks
|
||||
mock_abspath.side_effect = lambda x: f"/abs/path/{x}"
|
||||
mock_instance = mock_wrapper_cls.return_value
|
||||
mock_response = MagicMock()
|
||||
mock_response.text = "A beautiful sunset in the mountains"
|
||||
mock_instance.return_value = mock_response
|
||||
|
||||
# Call the function under test
|
||||
image_urls = ("image1.jpg", "https://example.com/image2.jpg")
|
||||
prompt = "Describe the image"
|
||||
api_key = "fake-api"
|
||||
model = "qwen-vl-plus"
|
||||
results = dashscope_image_to_text(image_urls, api_key, prompt, model)
|
||||
|
||||
# Verify the wrapper call
|
||||
mock_wrapper_cls.assert_called_once_with(
|
||||
config_name="dashscope-image-to-text-service",
|
||||
model_name=model,
|
||||
api_key=api_key,
|
||||
)
|
||||
mock_instance.assert_called_once()
|
||||
|
||||
# Expected result
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.SUCCESS,
|
||||
content="A beautiful sunset in the mountains",
|
||||
)
|
||||
|
||||
self.assertEqual(results.status, expected_result.status)
|
||||
self.assertEqual(results.content, expected_result.content)
|
||||
|
||||
@patch(
|
||||
(
|
||||
"agentscope.service.multi_modality.dashscope_services."
|
||||
"DashScopeMultiModalWrapper"
|
||||
),
|
||||
)
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services.os.path.abspath",
|
||||
)
|
||||
def test_dashscope_image_to_text_failure(
|
||||
self,
|
||||
mock_abspath: MagicMock,
|
||||
mock_wrapper_cls: MagicMock,
|
||||
) -> None:
|
||||
"""Test failure in image to text conversion."""
|
||||
# Configure the mocks
|
||||
mock_abspath.side_effect = lambda x: f"/abs/path/{x}"
|
||||
mock_instance = mock_wrapper_cls.return_value
|
||||
mock_instance.return_value.text = (
|
||||
None # Simulate failure by returning None
|
||||
)
|
||||
|
||||
# Call the function under test
|
||||
image_urls = "image1.jpg"
|
||||
prompt = "Describe the image"
|
||||
api_key = "fake-api"
|
||||
model = "qwen-vl-plus"
|
||||
results = dashscope_image_to_text(image_urls, api_key, prompt, model)
|
||||
|
||||
# Verify the wrapper call
|
||||
mock_wrapper_cls.assert_called_once_with(
|
||||
config_name="dashscope-image-to-text-service",
|
||||
model_name=model,
|
||||
api_key=api_key,
|
||||
)
|
||||
mock_instance.assert_called_once()
|
||||
|
||||
# Expected result
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.ERROR,
|
||||
content="Error: Failed to generate text",
|
||||
)
|
||||
|
||||
self.assertEqual(results.status, expected_result.status)
|
||||
self.assertEqual(results.content, expected_result.content)
|
||||
|
||||
@patch(
|
||||
(
|
||||
"agentscope.service.multi_modality.dashscope_services."
|
||||
"SpeechSynthesizer"
|
||||
),
|
||||
)
|
||||
@patch("agentscope.service.multi_modality.dashscope_services.os.makedirs")
|
||||
@patch(
|
||||
"agentscope.service.multi_modality.dashscope_services.os.path.exists",
|
||||
)
|
||||
@patch("builtins.open", new_callable=mock_open)
|
||||
def test_dashscope_text_to_audio_success(
|
||||
self,
|
||||
mock_open_func: MagicMock,
|
||||
mock_os_path_exists: MagicMock,
|
||||
mock_os_makedirs: MagicMock,
|
||||
mock_synthesizer_cls: MagicMock,
|
||||
) -> None:
|
||||
"""Test successful text to audio conversion."""
|
||||
# Configure the mocks
|
||||
mock_os_path_exists.return_value = False
|
||||
|
||||
mock_instance = mock_synthesizer_cls.return_value
|
||||
mock_response = MagicMock()
|
||||
mock_response.get_audio_data.return_value = b"fake_audio_data"
|
||||
mock_instance.return_value = mock_response
|
||||
|
||||
# Call the function under test
|
||||
text = "hello?"
|
||||
api_key = "fake-api-key"
|
||||
model = "sambert-zhichu-v1"
|
||||
sample_rate = 48000
|
||||
saved_dir = "./audio"
|
||||
results = dashscope_text_to_audio(
|
||||
text,
|
||||
api_key,
|
||||
saved_dir,
|
||||
model,
|
||||
sample_rate,
|
||||
)
|
||||
|
||||
# Verify the wrapper call
|
||||
mock_synthesizer_cls.call.assert_called_once_with(
|
||||
model=model,
|
||||
text=text,
|
||||
sample_rate=sample_rate,
|
||||
format="wav",
|
||||
)
|
||||
|
||||
# Verify the file operations
|
||||
mock_os_makedirs.assert_called_once_with(saved_dir, exist_ok=True)
|
||||
mock_open_func.assert_called_once()
|
||||
|
||||
# Expected result
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.SUCCESS,
|
||||
content={"audio_path": f"{saved_dir}/{text}.wav"},
|
||||
)
|
||||
|
||||
self.assertEqual(results.status, expected_result.status)
|
||||
self.assertIn(
|
||||
results.content,
|
||||
[
|
||||
{"audio_path": f"{saved_dir}/{text}.wav"},
|
||||
{"audio_path": f"{saved_dir}\\{text}.wav"}, # For Windows
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
350
AlgoriAgent/tests/dashscope_test.py
Normal file
350
AlgoriAgent/tests/dashscope_test.py
Normal file
@@ -0,0 +1,350 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""dashscope test"""
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from agentscope.models import (
|
||||
ModelResponse,
|
||||
DashScopeChatWrapper,
|
||||
DashScopeImageSynthesisWrapper,
|
||||
DashScopeTextEmbeddingWrapper,
|
||||
DashScopeMultiModalWrapper,
|
||||
)
|
||||
|
||||
|
||||
class TestDashScopeChatWrapper(unittest.TestCase):
|
||||
"""Test DashScope Chat Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.config_name = "test_config"
|
||||
self.model_name = "test_model"
|
||||
self.api_key = "test_api_key"
|
||||
self.wrapper = DashScopeChatWrapper(
|
||||
config_name=self.config_name,
|
||||
model_name=self.model_name,
|
||||
api_key=self.api_key,
|
||||
)
|
||||
|
||||
@patch("agentscope.models.dashscope_model.dashscope.Generation.call")
|
||||
def test_call_success(self, mock_generation_call: MagicMock) -> None:
|
||||
"""Test call success"""
|
||||
# Set up the mock response for a successful API call
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.request_id = "test_request_id"
|
||||
mock_response.usage = {
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 5,
|
||||
"total_tokens": 8,
|
||||
}
|
||||
mock_response.output = {
|
||||
"choices": [{"message": {"content": "Hello, world!"}}],
|
||||
}
|
||||
|
||||
mock_generation_call.return_value = mock_response
|
||||
|
||||
# Define test input
|
||||
messages = [
|
||||
{"role": "user", "content": "Hi!"},
|
||||
{"role": "assistant", "content": "Hello!"},
|
||||
]
|
||||
|
||||
# Call the wrapper method
|
||||
response = self.wrapper(messages)
|
||||
|
||||
# Verify the response
|
||||
self.assertIsInstance(response, ModelResponse)
|
||||
self.assertEqual(response.text, "Hello, world!")
|
||||
self.assertEqual(response.raw, mock_response)
|
||||
|
||||
# Verify call to dashscope.Generation.call
|
||||
mock_generation_call.assert_called_once_with(
|
||||
model=self.model_name,
|
||||
messages=messages,
|
||||
result_format="message",
|
||||
)
|
||||
|
||||
@patch("agentscope.models.dashscope_model.dashscope.Generation.call")
|
||||
def test_call_failure(self, mock_generation_call: MagicMock) -> None:
|
||||
"""test call failure"""
|
||||
# Set up the mock response for a failed API call
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = "400"
|
||||
mock_response.request_id = "Test_request_id"
|
||||
mock_response.code = "Error Code"
|
||||
mock_response.message = "Error Message"
|
||||
mock_generation_call.return_value = mock_response
|
||||
|
||||
# Define test input
|
||||
messages = [
|
||||
{"role": "user", "content": "Hi!"},
|
||||
{"role": "assistant", "content": "Hello!"},
|
||||
]
|
||||
|
||||
# Call the wrapper method and expect an exception
|
||||
with self.assertRaises(RuntimeError) as context:
|
||||
self.wrapper(messages)
|
||||
|
||||
# Assert the expected exception message contains the error details
|
||||
self.assertIn("Error Code", str(context.exception))
|
||||
self.assertIn("Error Message", str(context.exception))
|
||||
self.assertIn("400", str(context.exception))
|
||||
self.assertIn("Test_request_id", str(context.exception))
|
||||
|
||||
# Verify call to dashscope.Generation.call
|
||||
mock_generation_call.assert_called_once_with(
|
||||
model=self.model_name,
|
||||
messages=messages,
|
||||
result_format="message",
|
||||
)
|
||||
|
||||
|
||||
class TestDashScopeImageSynthesisWrapper(unittest.TestCase):
|
||||
"""Test DashScope Image Synthesis Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.config_name = "config_name"
|
||||
self.model_name = "test_model"
|
||||
self.api_key = "test_api_key"
|
||||
self.wrapper = DashScopeImageSynthesisWrapper(
|
||||
config_name=self.config_name,
|
||||
model_name=self.model_name,
|
||||
api_key=self.api_key,
|
||||
)
|
||||
|
||||
@patch(
|
||||
"agentscope.file_manager.file_manager.save_image",
|
||||
side_effect=lambda x: f'/local/path/{x.split("/")[-1]}',
|
||||
)
|
||||
@patch("agentscope.models.dashscope_model.dashscope.ImageSynthesis.call")
|
||||
def test_image_synthesis_wrapper_call_success(
|
||||
self,
|
||||
mock_call: MagicMock,
|
||||
mock_save_image: MagicMock,
|
||||
) -> None:
|
||||
"""Test call success"""
|
||||
# Set up the mock response for a successful API call
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.output = {
|
||||
"results": [{"url": "http://example.com/image.jpg"}],
|
||||
}
|
||||
mock_call.return_value = mock_response
|
||||
# Call the wrapper with prompt
|
||||
prompt = "Generate an image of a sunset"
|
||||
response = self.wrapper(prompt, save_local=False)
|
||||
# Check if response is an instance of ModelResponse and contains
|
||||
# expected data
|
||||
self.assertIsInstance(response, ModelResponse)
|
||||
self.assertEqual(
|
||||
response.image_urls,
|
||||
["http://example.com/image.jpg"],
|
||||
)
|
||||
|
||||
# Call the wrapper method with save_local set to True
|
||||
response_with_save = self.wrapper(prompt, save_local=True)
|
||||
|
||||
# Verify save_image call and local image url in response
|
||||
mock_save_image.assert_called_with("http://example.com/image.jpg")
|
||||
self.assertEqual(
|
||||
response_with_save.image_urls,
|
||||
["/local/path/image.jpg"],
|
||||
)
|
||||
|
||||
@patch("agentscope.models.dashscope_model.dashscope.ImageSynthesis.call")
|
||||
def test_image_synthesis_wrapper_call_failure(
|
||||
self,
|
||||
mock_call: MagicMock,
|
||||
) -> None:
|
||||
"""Test call failure"""
|
||||
# Set up the mock response for a failed API call
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 400
|
||||
mock_response.request_id = "Test_request_id"
|
||||
mock_response.code = "Error Code"
|
||||
mock_response.message = "Error Message"
|
||||
mock_call.return_value = mock_response
|
||||
|
||||
# Call the wrapper with prompt and expect a RuntimeError
|
||||
prompt = "Generate an image of a sunset"
|
||||
with self.assertRaises(RuntimeError) as context:
|
||||
self.wrapper(prompt, save_local=False, n=1)
|
||||
|
||||
# Assert the expected exception message
|
||||
self.assertIn("Error Code", str(context.exception))
|
||||
self.assertIn("Error Message", str(context.exception))
|
||||
self.assertIn("Test_request_id", str(context.exception))
|
||||
self.assertIn("400", str(context.exception))
|
||||
|
||||
# Verify call to dashscope.ImageSynthesis.call
|
||||
mock_call.assert_called_once_with(
|
||||
model=self.model_name,
|
||||
prompt=prompt,
|
||||
n=1, # Assuming this is a default value used to call the API
|
||||
)
|
||||
|
||||
|
||||
class TestDashScopeTextEmbeddingWrapper(unittest.TestCase):
|
||||
"""Test DashScope Text Embedding Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
# Initialize DashScopeTextEmbeddingWrapper instance
|
||||
self.wrapper = DashScopeTextEmbeddingWrapper(
|
||||
config_name="test_config",
|
||||
model_name="test_model",
|
||||
api_key="test_key",
|
||||
)
|
||||
|
||||
@patch("agentscope.models.dashscope_model.dashscope.TextEmbedding.call")
|
||||
def test_call_success(self, mock_call: MagicMock) -> None:
|
||||
"""Test call success"""
|
||||
# Mock a successful API response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.usage = {
|
||||
"total_tokens": 8,
|
||||
}
|
||||
mock_response.output = {
|
||||
"embeddings": [{"embedding": [0.1, 0.2, 0.3]}],
|
||||
}
|
||||
mock_call.return_value = mock_response
|
||||
|
||||
# Call the wrapper with a mock API
|
||||
texts = ["Hello, world!"]
|
||||
response = self.wrapper(texts)
|
||||
|
||||
# Assert the API was called correctly
|
||||
mock_call.assert_called_once_with(
|
||||
input=texts,
|
||||
model=self.wrapper.model_name,
|
||||
**self.wrapper.generate_args,
|
||||
)
|
||||
|
||||
# Assert the response is as expected
|
||||
self.assertEqual(response.embedding, [[0.1, 0.2, 0.3]])
|
||||
|
||||
@patch("agentscope.models.dashscope_model.dashscope.TextEmbedding.call")
|
||||
def test_call_failure(self, mock_call: MagicMock) -> None:
|
||||
"""Test call failure"""
|
||||
# Mock a failed API response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 400
|
||||
mock_response.request_id = "Test_request_id"
|
||||
mock_response.code = "Error Code"
|
||||
mock_response.message = "Error Message"
|
||||
mock_call.return_value = mock_response
|
||||
|
||||
# Call the wrapper with a mock API and expect an exception
|
||||
texts = ["Hello, world!"]
|
||||
with self.assertRaises(RuntimeError) as context:
|
||||
self.wrapper(texts)
|
||||
|
||||
# Assert the expected exception message
|
||||
self.assertIn("Error Code", str(context.exception))
|
||||
self.assertIn("Error Message", str(context.exception))
|
||||
self.assertIn("Test_request_id", str(context.exception))
|
||||
self.assertIn("400", str(context.exception))
|
||||
|
||||
# Verify call to dashscope.TextEmbedding.call
|
||||
mock_call.assert_called_once_with(
|
||||
input=texts,
|
||||
model=self.wrapper.model_name,
|
||||
**self.wrapper.generate_args,
|
||||
)
|
||||
|
||||
|
||||
class TestDashScopeMultiModalWrapper(unittest.TestCase):
|
||||
"""Test DashScope MultiModal Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
# Initialize DashScopeMultiModalWrapper instance
|
||||
self.wrapper = DashScopeMultiModalWrapper(
|
||||
config_name="test_config",
|
||||
model_name="test_model",
|
||||
api_key="test_key",
|
||||
)
|
||||
|
||||
@patch(
|
||||
"agentscope.models.dashscope_model."
|
||||
"dashscope.MultiModalConversation.call",
|
||||
)
|
||||
def test_call_success(self, mock_call: MagicMock) -> None:
|
||||
"""Test call success"""
|
||||
# Mocking the response from the API
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.output = {
|
||||
"choices": [
|
||||
{"message": {"content": [{"text": "This is the result."}]}},
|
||||
],
|
||||
}
|
||||
mock_response.usage = {
|
||||
"input_tokens": 23,
|
||||
"output_tokens": 5,
|
||||
"image_tokens": 17,
|
||||
}
|
||||
mock_call.return_value = mock_response
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"text": "What does this picture depict?"},
|
||||
{"image": "http://example.com/image.jpg"},
|
||||
],
|
||||
},
|
||||
]
|
||||
# Calling the wrapper and validating the response
|
||||
response = self.wrapper(messages=messages)
|
||||
self.assertIsInstance(response, ModelResponse)
|
||||
self.assertEqual(response.text, "This is the result.")
|
||||
self.assertEqual(response.raw, mock_response)
|
||||
|
||||
# Verify call to dashscope.MultiModalConversation.call
|
||||
mock_call.assert_called_once_with(
|
||||
model=self.wrapper.model_name,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
@patch(
|
||||
"agentscope.models.dashscope_model."
|
||||
"dashscope.MultiModalConversation.call",
|
||||
)
|
||||
def test_call_failure(self, mock_call: MagicMock) -> None:
|
||||
"""Test call failure"""
|
||||
# Simulating a failed API call
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 400
|
||||
mock_response.request_id = "Test_request_id"
|
||||
mock_response.code = "Error Code"
|
||||
mock_response.message = "Error Message"
|
||||
mock_call.return_value = mock_response
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"text": "What does this picture depict?"},
|
||||
{"image": "http://example.com/image.jpg"},
|
||||
],
|
||||
},
|
||||
]
|
||||
# Expecting a RuntimeError to be raised
|
||||
with self.assertRaises(RuntimeError) as context:
|
||||
self.wrapper(messages=messages)
|
||||
|
||||
# Assert the expected exception message
|
||||
self.assertIn("Error Code", str(context.exception))
|
||||
self.assertIn("Error Message", str(context.exception))
|
||||
self.assertIn("Test_request_id", str(context.exception))
|
||||
self.assertIn("400", str(context.exception))
|
||||
|
||||
# Verify call to dashscope.MultiModalConversation.call
|
||||
mock_call.assert_called_once_with(
|
||||
model=self.wrapper.model_name,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
22
AlgoriAgent/tests/example_test.py
Normal file
22
AlgoriAgent/tests/example_test.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Example for a unit test."""
|
||||
import unittest
|
||||
|
||||
|
||||
class ExampleTest(unittest.TestCase):
|
||||
"""
|
||||
ExampleTest for a unit test.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExampleTest."""
|
||||
self.num_a = 1
|
||||
self.num_b = 0
|
||||
|
||||
def test_dummy(self) -> None:
|
||||
"""Dummy test."""
|
||||
self.assertGreater(self.num_a, self.num_b)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
90
AlgoriAgent/tests/execute_python_code_test.py
Normal file
90
AlgoriAgent/tests/execute_python_code_test.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Python code execution test."""
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from agentscope.service import execute_python_code
|
||||
|
||||
|
||||
class ExecutePythonCodeTest(unittest.TestCase):
|
||||
"""
|
||||
Python code execution test.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExecutePythonCodeTest."""
|
||||
|
||||
# Basic expression
|
||||
self.arg0 = {"code": "print('Hello World')", "timeout": 5}
|
||||
|
||||
# Raising Exceptions
|
||||
self.arg1 = {
|
||||
"code": "print('Hello World')\nraise ValueError('An intentional "
|
||||
"error')",
|
||||
"timeout": 5,
|
||||
}
|
||||
|
||||
# Using External Libraries
|
||||
self.arg2 = {"code": "import math\nprint(math.sqrt(16))", "timeout": 5}
|
||||
|
||||
# Timeout
|
||||
self.arg3 = {
|
||||
"code": "import time\nprint('Hello World')\ntime.sleep("
|
||||
"10)\nprint('This will not print')",
|
||||
"timeout": 1,
|
||||
}
|
||||
|
||||
# No input code
|
||||
self.arg4 = {"code": "", "timeout": 5}
|
||||
|
||||
def run_test(
|
||||
self,
|
||||
args: dict,
|
||||
expected_output: str,
|
||||
expected_error_substr: str,
|
||||
) -> None:
|
||||
"""A helper function to avoid code repetition"""
|
||||
response = execute_python_code(
|
||||
use_docker=False,
|
||||
**args,
|
||||
)
|
||||
self.assertIn(expected_output, response.content)
|
||||
self.assertIn(expected_error_substr, response.content)
|
||||
|
||||
# Uncomment it when test in local
|
||||
# response = execute_python_code(
|
||||
# use_docker=True,
|
||||
# **args,
|
||||
# )
|
||||
# self.assertIn(expected_output, response.content)
|
||||
# self.assertIn(expected_error_substr, response.content)
|
||||
|
||||
def test_basic_expression(self) -> None:
|
||||
"""Execute basic expression test."""
|
||||
self.run_test(self.arg0, "Hello World\n", "")
|
||||
|
||||
def test_raising_exceptions(self) -> None:
|
||||
"""Execute raising exceptions test."""
|
||||
self.run_test(
|
||||
self.arg1,
|
||||
"Hello World\n",
|
||||
"ValueError: An intentional error\n",
|
||||
)
|
||||
|
||||
def test_using_external_libs(self) -> None:
|
||||
"""Execute using external libs test."""
|
||||
self.run_test(self.arg2, "4.0\n", "")
|
||||
|
||||
def test_timeout(self) -> None:
|
||||
"""Execute timeout test (NOT available in WinOS.)"""
|
||||
if sys.platform == "win32":
|
||||
return
|
||||
self.run_test(self.arg3, "Hello World\n", "timed out\n")
|
||||
|
||||
def test_no_input_code(self) -> None:
|
||||
"""Execute no input code test."""
|
||||
self.run_test(self.arg4, "", "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
62
AlgoriAgent/tests/execute_shell_command_test.py
Normal file
62
AlgoriAgent/tests/execute_shell_command_test.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Python code execution test."""
|
||||
import unittest
|
||||
import platform
|
||||
|
||||
from agentscope.service import execute_shell_command
|
||||
from agentscope.service import ServiceExecStatus
|
||||
|
||||
|
||||
class ExecuteShellCommandTest(unittest.TestCase):
|
||||
"""
|
||||
Python code execution test.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExecuteShellCommandTest."""
|
||||
|
||||
# Basic expression
|
||||
self.arg0 = "touch tmp_a.text"
|
||||
|
||||
self.arg1 = "echo 'Helloworld' >> tmp_a.txt"
|
||||
|
||||
self.arg2 = "cat tmp_a.txt"
|
||||
|
||||
self.arg3 = "rm tmp_a.txt"
|
||||
|
||||
def test(self) -> None:
|
||||
"""test command, skip on windows"""
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
result = execute_shell_command(
|
||||
command=self.arg0,
|
||||
)
|
||||
assert result.status == ServiceExecStatus.SUCCESS
|
||||
assert result.content == "Success."
|
||||
|
||||
result = execute_shell_command(
|
||||
command=self.arg1,
|
||||
)
|
||||
assert result.status == ServiceExecStatus.SUCCESS
|
||||
assert result.content == "Success."
|
||||
|
||||
result = execute_shell_command(
|
||||
command=self.arg2,
|
||||
)
|
||||
assert result.status == ServiceExecStatus.SUCCESS
|
||||
assert result.content == "Helloworld"
|
||||
|
||||
result = execute_shell_command(
|
||||
command=self.arg3,
|
||||
)
|
||||
assert result.status == ServiceExecStatus.SUCCESS
|
||||
assert result.content == "Success."
|
||||
|
||||
result = execute_shell_command(
|
||||
command=self.arg3,
|
||||
)
|
||||
assert result.status == ServiceExecStatus.ERROR
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
503
AlgoriAgent/tests/format_test.py
Normal file
503
AlgoriAgent/tests/format_test.py
Normal file
@@ -0,0 +1,503 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for prompt engineering strategies in format function."""
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from agentscope.message import Msg
|
||||
from agentscope.models import (
|
||||
OpenAIChatWrapper,
|
||||
OllamaChatWrapper,
|
||||
OllamaGenerationWrapper,
|
||||
GeminiChatWrapper,
|
||||
ZhipuAIChatWrapper,
|
||||
DashScopeChatWrapper,
|
||||
DashScopeMultiModalWrapper,
|
||||
LiteLLMChatWrapper,
|
||||
)
|
||||
|
||||
|
||||
class ExampleTest(unittest.TestCase):
|
||||
"""
|
||||
ExampleTest for a unit test.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExampleTest."""
|
||||
self.inputs = [
|
||||
Msg("system", "You are a helpful assistant", role="system"),
|
||||
[
|
||||
Msg("user", "What is the weather today?", role="user"),
|
||||
Msg("assistant", "It is sunny today", role="assistant"),
|
||||
],
|
||||
]
|
||||
|
||||
self.inputs_vision = [
|
||||
Msg("system", "You are a helpful assistant", role="system"),
|
||||
[
|
||||
Msg(
|
||||
"user",
|
||||
"Describe the images",
|
||||
role="user",
|
||||
url="https://fakeweb/test.jpg",
|
||||
),
|
||||
Msg(
|
||||
"user",
|
||||
"And this images",
|
||||
"user",
|
||||
url=[
|
||||
"/Users/xxx/abc.png",
|
||||
"/Users/xxx/def.mp3",
|
||||
],
|
||||
),
|
||||
],
|
||||
]
|
||||
|
||||
self.wrong_inputs = [
|
||||
Msg("system", "You are a helpful assistant", role="system"),
|
||||
[
|
||||
"What is the weather today?",
|
||||
Msg("assistant", "It is sunny today", role="assistant"),
|
||||
],
|
||||
]
|
||||
|
||||
@patch("builtins.open", mock.mock_open(read_data=b"abcdef"))
|
||||
@patch("os.path.isfile")
|
||||
@patch("os.path.exists")
|
||||
@patch("openai.OpenAI")
|
||||
def test_openai_chat_vision_with_wrong_model(
|
||||
self,
|
||||
mock_client: MagicMock,
|
||||
mock_exists: MagicMock,
|
||||
mock_isfile: MagicMock,
|
||||
) -> None:
|
||||
"""Unit test for the format function in openai chat api wrapper with
|
||||
vision models"""
|
||||
mock_exists.side_effect = lambda url: url == "/Users/xxx/abc.png"
|
||||
mock_isfile.side_effect = lambda url: url == "/Users/xxx/abc.png"
|
||||
|
||||
# Prepare the mock client
|
||||
mock_client.return_value = "client_dummy"
|
||||
|
||||
model = OpenAIChatWrapper(
|
||||
config_name="",
|
||||
model_name="gpt-4",
|
||||
)
|
||||
|
||||
# correct format
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant",
|
||||
"name": "system",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"name": "user",
|
||||
"content": "Describe the images",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"name": "user",
|
||||
"content": "And this images",
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs_vision)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
@patch("builtins.open", mock.mock_open(read_data=b"abcdef"))
|
||||
@patch("os.path.isfile")
|
||||
@patch("os.path.exists")
|
||||
@patch("openai.OpenAI")
|
||||
def test_openai_chat_vision(
|
||||
self,
|
||||
mock_client: MagicMock,
|
||||
mock_exists: MagicMock,
|
||||
mock_isfile: MagicMock,
|
||||
) -> None:
|
||||
"""Unit test for the format function in openai chat api wrapper with
|
||||
vision models"""
|
||||
mock_exists.side_effect = lambda url: url == "/Users/xxx/abc.png"
|
||||
mock_isfile.side_effect = lambda url: url == "/Users/xxx/abc.png"
|
||||
|
||||
# Prepare the mock client
|
||||
mock_client.return_value = "client_dummy"
|
||||
|
||||
model = OpenAIChatWrapper(
|
||||
config_name="",
|
||||
model_name="gpt-4o",
|
||||
)
|
||||
|
||||
# correct format
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant",
|
||||
"name": "system",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"name": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Describe the images",
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://fakeweb/test.jpg",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"name": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "And this images",
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "data:image/png;base64,YWJjZGVm",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs_vision)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
@patch("openai.OpenAI")
|
||||
def test_openai_chat(self, mock_client: MagicMock) -> None:
|
||||
"""Unit test for the format function in openai chat api wrapper."""
|
||||
# Prepare the mock client
|
||||
mock_client.return_value = "client_dummy"
|
||||
|
||||
model = OpenAIChatWrapper(
|
||||
config_name="",
|
||||
model_name="gpt-4",
|
||||
)
|
||||
|
||||
# correct format
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant",
|
||||
"name": "system",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is the weather today?",
|
||||
"name": "user",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "It is sunny today",
|
||||
"name": "assistant",
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs) # type: ignore[arg-type]
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
def test_ollama_chat(self) -> None:
|
||||
"""Unit test for the format function in ollama chat api wrapper."""
|
||||
model = OllamaChatWrapper(
|
||||
config_name="",
|
||||
model_name="llama2",
|
||||
)
|
||||
|
||||
# correct format
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
"You are a helpful assistant\n"
|
||||
"\n"
|
||||
"## Dialogue History\n"
|
||||
"user: What is the weather today?\n"
|
||||
"assistant: It is sunny today"
|
||||
),
|
||||
},
|
||||
]
|
||||
prompt = model.format(*self.inputs) # type: ignore[arg-type]
|
||||
self.assertEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
def test_ollama_generation(self) -> None:
|
||||
"""Unit test for the generation function in ollama chat api wrapper."""
|
||||
model = OllamaGenerationWrapper(
|
||||
config_name="",
|
||||
model_name="llama2",
|
||||
)
|
||||
|
||||
# correct format
|
||||
ground_truth = (
|
||||
"You are a helpful assistant\n\n## Dialogue History\nuser: "
|
||||
"What is the weather today?\nassistant: It is sunny today"
|
||||
)
|
||||
prompt = model.format(*self.inputs) # type: ignore[arg-type]
|
||||
self.assertEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
@patch("google.generativeai.configure")
|
||||
def test_gemini_chat(self, mock_configure: MagicMock) -> None:
|
||||
"""Unit test for the format function in gemini chat api wrapper."""
|
||||
mock_configure.return_value = "client_dummy"
|
||||
|
||||
model = GeminiChatWrapper(
|
||||
config_name="",
|
||||
model_name="gemini-pro",
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
# correct format
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "user",
|
||||
"parts": [
|
||||
"You are a helpful assistant\n\n## Dialogue History\n"
|
||||
"user: What is the weather today?\nassistant: It is "
|
||||
"sunny today",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs) # type: ignore[arg-type]
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
def test_dashscope_chat(self) -> None:
|
||||
"""Unit test for the format function in dashscope chat api wrapper."""
|
||||
model = DashScopeChatWrapper(
|
||||
config_name="",
|
||||
model_name="qwen-max",
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
ground_truth = [
|
||||
{
|
||||
"content": "You are a helpful assistant",
|
||||
"role": "system",
|
||||
},
|
||||
{
|
||||
"content": (
|
||||
"## Dialogue History\n"
|
||||
"user: What is the weather today?\n"
|
||||
"assistant: It is sunny today"
|
||||
),
|
||||
"role": "user",
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
def test_zhipuai_chat(self) -> None:
|
||||
"""Unit test for the format function in zhipu chat api wrapper."""
|
||||
model = ZhipuAIChatWrapper(
|
||||
config_name="",
|
||||
model_name="glm-4",
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
ground_truth = [
|
||||
{
|
||||
"content": "You are a helpful assistant",
|
||||
"role": "system",
|
||||
},
|
||||
{
|
||||
"content": (
|
||||
"## Dialogue History\n"
|
||||
"user: What is the weather today?\n"
|
||||
"assistant: It is sunny today"
|
||||
),
|
||||
"role": "user",
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
def test_litellm_chat(self) -> None:
|
||||
"""Unit test for the format function in litellm chat api wrapper."""
|
||||
model = LiteLLMChatWrapper(
|
||||
config_name="",
|
||||
model_name="gpt-3.5-turbo",
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
"You are a helpful assistant\n\n"
|
||||
"## Dialogue History\nuser: What is the weather today?\n"
|
||||
"assistant: It is sunny today"
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*self.inputs)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs) # type: ignore[arg-type]
|
||||
|
||||
def test_dashscope_multimodal_image(self) -> None:
|
||||
"""Unit test for the format function in dashscope multimodal
|
||||
conversation api wrapper for image."""
|
||||
model = DashScopeMultiModalWrapper(
|
||||
config_name="",
|
||||
model_name="qwen-vl-plus",
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
multimodal_input = [
|
||||
Msg(
|
||||
"system",
|
||||
"You are a helpful assistant",
|
||||
role="system",
|
||||
url="url1.png",
|
||||
),
|
||||
[
|
||||
Msg(
|
||||
"user",
|
||||
"What is the weather today?",
|
||||
role="user",
|
||||
url="url2.png",
|
||||
),
|
||||
Msg(
|
||||
"assistant",
|
||||
"It is sunny today",
|
||||
role="assistant",
|
||||
url="url3.png",
|
||||
),
|
||||
],
|
||||
]
|
||||
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": [
|
||||
{"image": "url1.png"},
|
||||
{"text": "You are a helpful assistant"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"image": "url2.png"},
|
||||
{"image": "url3.png"},
|
||||
{
|
||||
"text": (
|
||||
"## Dialogue History\n"
|
||||
"user: What is the weather today?\n"
|
||||
"assistant: It is sunny today"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*multimodal_input)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs)
|
||||
|
||||
def test_dashscope_multimodal_audio(self) -> None:
|
||||
"""Unit test for the format function in dashscope multimodal
|
||||
conversation api wrapper for audio."""
|
||||
model = DashScopeMultiModalWrapper(
|
||||
config_name="",
|
||||
model_name="qwen-audio-turbo",
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
multimodal_input = [
|
||||
Msg(
|
||||
"system",
|
||||
"You are a helpful assistant",
|
||||
role="system",
|
||||
url="url1.mp3",
|
||||
),
|
||||
[
|
||||
Msg(
|
||||
"user",
|
||||
"What is the weather today?",
|
||||
role="user",
|
||||
url="url2.mp3",
|
||||
),
|
||||
Msg(
|
||||
"assistant",
|
||||
"It is sunny today",
|
||||
role="assistant",
|
||||
url="url3.mp3",
|
||||
),
|
||||
],
|
||||
]
|
||||
|
||||
ground_truth = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": [
|
||||
{"audio": "url1.mp3"},
|
||||
{"text": "You are a helpful assistant"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"audio": "url2.mp3"},
|
||||
{"audio": "url3.mp3"},
|
||||
{
|
||||
"text": (
|
||||
"## Dialogue History\n"
|
||||
"user: What is the weather today?\n"
|
||||
"assistant: It is sunny today"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
prompt = model.format(*multimodal_input)
|
||||
self.assertListEqual(prompt, ground_truth)
|
||||
|
||||
# wrong format
|
||||
with self.assertRaises(TypeError):
|
||||
model.format(*self.wrong_inputs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
118
AlgoriAgent/tests/gemini_test.py
Normal file
118
AlgoriAgent/tests/gemini_test.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for gemini model wrapper."""
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import agentscope
|
||||
from agentscope.models import load_model_by_config_name
|
||||
from agentscope._runtime import _Runtime
|
||||
from agentscope.file_manager import _FileManager
|
||||
from agentscope.utils.monitor import MonitorFactory
|
||||
|
||||
|
||||
def flush() -> None:
|
||||
"""
|
||||
** Only for unittest usage. Don't use this function in your code. **
|
||||
Clear the runtime dir and destroy all singletons.
|
||||
"""
|
||||
_Runtime._flush() # pylint: disable=W0212
|
||||
_FileManager._flush() # pylint: disable=W0212
|
||||
MonitorFactory.flush()
|
||||
|
||||
|
||||
class DummyPart:
|
||||
"""Dummy part for testing."""
|
||||
|
||||
text = "Hello! How can I help you?"
|
||||
|
||||
|
||||
class DummyContent:
|
||||
"""Dummy content for testing."""
|
||||
|
||||
parts = [DummyPart()]
|
||||
|
||||
|
||||
class DummyCandidate:
|
||||
"""Dummy candidate for testing."""
|
||||
|
||||
content = DummyContent()
|
||||
|
||||
|
||||
class DummyResponse:
|
||||
"""Dummy response for testing."""
|
||||
|
||||
text = "Hello! How can I help you?"
|
||||
|
||||
candidates = [DummyCandidate]
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Return string representation."""
|
||||
return str({"text": self.text})
|
||||
|
||||
|
||||
class GeminiModelWrapperTest(unittest.TestCase):
|
||||
"""Unit test for gemini model wrapper."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Set up for GeminiModelWrapperTest."""
|
||||
flush()
|
||||
|
||||
@patch("google.generativeai.GenerativeModel")
|
||||
def test_gemini_chat(self, mock_model: MagicMock) -> None:
|
||||
"""Test for chat API."""
|
||||
# prepare mock response
|
||||
mock_counter = MagicMock()
|
||||
mock_counter.total_tokens = 20
|
||||
|
||||
# connect
|
||||
mock_model.return_value.model_name = "gemini-pro"
|
||||
mock_model.return_value.generate_content.return_value = DummyResponse()
|
||||
mock_model.return_value.count_tokens.return_value = mock_counter
|
||||
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "my_gemini_chat",
|
||||
"model_type": "gemini_chat",
|
||||
"model_name": "gemini-pro",
|
||||
"api_key": "xxx",
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("my_gemini_chat")
|
||||
response = model(contents="Hi!")
|
||||
|
||||
self.assertEqual(str(response.raw), str(DummyResponse()))
|
||||
|
||||
@patch("google.generativeai.embed_content")
|
||||
def test_gemini_embedding(self, mock_model: MagicMock) -> None:
|
||||
"""Test gemini embedding API"""
|
||||
mock_model.return_value = {
|
||||
"embedding": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
|
||||
}
|
||||
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "my_gemini_embedding",
|
||||
"model_type": "gemini_embedding",
|
||||
"model_name": "models/embedding-001",
|
||||
"api_key": "xxx",
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("my_gemini_embedding")
|
||||
response = model(content="Hi!")
|
||||
|
||||
self.assertDictEqual(
|
||||
response.raw,
|
||||
{
|
||||
"embedding": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
|
||||
},
|
||||
)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Clean up after each test."""
|
||||
flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
93
AlgoriAgent/tests/knowledge_test.py
Normal file
93
AlgoriAgent/tests/knowledge_test.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for knowledge (RAG module in AgentScope)
|
||||
"""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from typing import Any
|
||||
import shutil
|
||||
|
||||
from agentscope.rag import LlamaIndexKnowledge
|
||||
from agentscope.models import OpenAIEmbeddingWrapper, ModelResponse
|
||||
|
||||
|
||||
class DummyModel(OpenAIEmbeddingWrapper):
|
||||
"""
|
||||
Dummy model wrapper for testing
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""dummy init"""
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> ModelResponse:
|
||||
"""dummy call"""
|
||||
return ModelResponse(embedding=[[1.0, 2.0]])
|
||||
|
||||
|
||||
class KnowledgeTest(unittest.TestCase):
|
||||
"""
|
||||
Test cases for TemporaryMemory
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""set up test data"""
|
||||
self.data_dir = "tmp_data_dir"
|
||||
if not os.path.exists(self.data_dir):
|
||||
os.mkdir(self.data_dir)
|
||||
self.file_name_1 = "tmp_data_dir/file1.txt"
|
||||
self.content = "testing file"
|
||||
with open(self.file_name_1, "w", encoding="utf-8") as f:
|
||||
f.write(self.content)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Clean up before & after tests."""
|
||||
try:
|
||||
if os.path.exists(self.data_dir):
|
||||
shutil.rmtree(self.data_dir)
|
||||
if os.path.exists("./runs"):
|
||||
shutil.rmtree("./runs")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_llamaindexknowledge(self) -> None:
|
||||
"""test llamaindexknowledge"""
|
||||
dummy_model = DummyModel()
|
||||
|
||||
knowledge_config = {
|
||||
"knowledge_id": "",
|
||||
"data_processing": [],
|
||||
}
|
||||
loader_config = {
|
||||
"load_data": {
|
||||
"loader": {
|
||||
"create_object": True,
|
||||
"module": "llama_index.core",
|
||||
"class": "SimpleDirectoryReader",
|
||||
"init_args": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
loader_init = {"input_dir": self.data_dir, "required_exts": ".txt"}
|
||||
|
||||
loader_config["load_data"]["loader"]["init_args"] = loader_init
|
||||
knowledge_config["data_processing"].append(loader_config)
|
||||
|
||||
knowledge = LlamaIndexKnowledge(
|
||||
knowledge_id="test_knowledge",
|
||||
emb_model=dummy_model,
|
||||
knowledge_config=knowledge_config,
|
||||
)
|
||||
retrieved = knowledge.retrieve(
|
||||
query="testing",
|
||||
similarity_top_k=2,
|
||||
to_list_strs=True,
|
||||
)
|
||||
self.assertEqual(
|
||||
retrieved,
|
||||
[self.content],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
61
AlgoriAgent/tests/litellm_test.py
Normal file
61
AlgoriAgent/tests/litellm_test.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""litellm test"""
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
import agentscope
|
||||
from agentscope.models import load_model_by_config_name
|
||||
|
||||
|
||||
class TestLiteLLMChatWrapper(unittest.TestCase):
|
||||
"""Test LiteLLM Chat Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.api_key = "test_api_key.secret_key"
|
||||
self.messages = [
|
||||
{"role": "user", "content": "Hello, litellm!"},
|
||||
{"role": "assistant", "content": "How can I assist you?"},
|
||||
]
|
||||
|
||||
@patch("agentscope.models.litellm_model.litellm")
|
||||
def test_chat(self, mock_litellm: MagicMock) -> None:
|
||||
"""
|
||||
Test chat"""
|
||||
mock_response = MagicMock()
|
||||
mock_response.model_dump.return_value = {
|
||||
"choices": [
|
||||
{"message": {"content": "Hello, this is a mocked response!"}},
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 100,
|
||||
"completion_tokens": 5,
|
||||
"total_tokens": 105,
|
||||
},
|
||||
}
|
||||
mock_response.choices[
|
||||
0
|
||||
].message.content = "Hello, this is a mocked response!"
|
||||
|
||||
mock_litellm.completion.return_value = mock_response
|
||||
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "test_config",
|
||||
"model_type": "litellm_chat",
|
||||
"model_name": "ollama/llama3:8b",
|
||||
"api_key": self.api_key,
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("test_config")
|
||||
|
||||
response = model(
|
||||
messages=self.messages,
|
||||
api_base="http://localhost:11434",
|
||||
)
|
||||
|
||||
self.assertEqual(response.text, "Hello, this is a mocked response!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
77
AlgoriAgent/tests/logger_test.py
Normal file
77
AlgoriAgent/tests/logger_test.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Unit test for logger chat"""
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from agentscope.logging import setup_logger
|
||||
|
||||
|
||||
class LoggerTest(unittest.TestCase):
|
||||
"""
|
||||
Unit test for logger.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Setup for unit test."""
|
||||
self.run_dir = "./logger_runs/"
|
||||
|
||||
def test_logger_chat(self) -> None:
|
||||
"""Logger chat."""
|
||||
|
||||
setup_logger(self.run_dir, level="INFO")
|
||||
|
||||
# str with "\n"
|
||||
logger.chat("Test\nChat\n\nMessage\n\n")
|
||||
|
||||
# dict with "\n"
|
||||
logger.chat(
|
||||
{
|
||||
"name": "Alice",
|
||||
"content": "Hi!\n",
|
||||
"url": "https://xxx.png",
|
||||
},
|
||||
)
|
||||
|
||||
# dict without content
|
||||
logger.chat({"name": "Alice", "url": "https://xxx.png"})
|
||||
|
||||
# dict
|
||||
logger.chat({"abc": 1})
|
||||
|
||||
# html labels
|
||||
logger.chat({"name": "Bob", "content": "<div>abc</div"})
|
||||
|
||||
# To avoid that logging is not finished before the file is read
|
||||
time.sleep(3)
|
||||
|
||||
with open(
|
||||
os.path.join(self.run_dir, "logging.chat"),
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
) as file:
|
||||
lines = file.readlines()
|
||||
|
||||
ground_truth = [
|
||||
'"Test\\nChat\\n\\nMessage\\n\\n"\n',
|
||||
'{"name": "Alice", "content": "Hi!\\n", "url": "https://xxx.png'
|
||||
'"}\n',
|
||||
'{"name": "Alice", "url": "https://xxx.png"}\n',
|
||||
'{"abc": 1}\n',
|
||||
'{"name": "Bob", "content": "<div>abc</div"}\n',
|
||||
]
|
||||
|
||||
self.assertListEqual(lines, ground_truth)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Tear down for LoggerTest."""
|
||||
logger.remove()
|
||||
if os.path.exists(self.run_dir):
|
||||
shutil.rmtree(self.run_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
117
AlgoriAgent/tests/memory_test.py
Normal file
117
AlgoriAgent/tests/memory_test.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for memory classes and functions
|
||||
"""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from agentscope.message import Msg
|
||||
from agentscope.memory import TemporaryMemory
|
||||
|
||||
|
||||
class TemporaryMemoryTest(unittest.TestCase):
|
||||
"""
|
||||
Test cases for TemporaryMemory
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.memory = TemporaryMemory()
|
||||
self.file_name_1 = "tmp_mem_file1.txt"
|
||||
self.file_name_2 = "tmp_mem_file2.txt"
|
||||
self.msg_1 = Msg("user", "Hello", role="user")
|
||||
self.msg_2 = Msg(
|
||||
"agent",
|
||||
"Hello! How can I help you?",
|
||||
role="assistant",
|
||||
)
|
||||
self.msg_3 = Msg(
|
||||
"user",
|
||||
"Translate the following sentence",
|
||||
role="assistant",
|
||||
)
|
||||
|
||||
self.invalid = {"invalid_key": "invalid_value"}
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Clean up before & after tests."""
|
||||
if os.path.exists(self.file_name_1):
|
||||
os.remove(self.file_name_1)
|
||||
if os.path.exists(self.file_name_2):
|
||||
os.remove(self.file_name_2)
|
||||
|
||||
def test_add(self) -> None:
|
||||
"""Test add different types of object"""
|
||||
# add msg
|
||||
self.memory.add(self.msg_1)
|
||||
self.assertEqual(
|
||||
self.memory.get_memory(),
|
||||
[self.msg_1],
|
||||
)
|
||||
|
||||
# add list
|
||||
self.memory.add([self.msg_2, self.msg_3])
|
||||
self.assertEqual(
|
||||
self.memory.get_memory(),
|
||||
[self.msg_1, self.msg_2, self.msg_3],
|
||||
)
|
||||
|
||||
@patch("loguru.logger.warning")
|
||||
def test_delete(self, mock_logging: MagicMock) -> None:
|
||||
"""Test delete operations"""
|
||||
self.memory.add([self.msg_1, self.msg_2, self.msg_3])
|
||||
|
||||
self.memory.delete(index=0)
|
||||
self.assertEqual(
|
||||
self.memory.get_memory(),
|
||||
[self.msg_2, self.msg_3],
|
||||
)
|
||||
|
||||
# test invalid
|
||||
self.memory.delete(index=100)
|
||||
mock_logging.assert_called_once_with(
|
||||
"Skip delete operation for the invalid index [100]",
|
||||
)
|
||||
|
||||
def test_invalid(self) -> None:
|
||||
"""Test invalid operations for memory"""
|
||||
# test invalid add
|
||||
with self.assertRaises(Exception) as context:
|
||||
self.memory.add(self.invalid)
|
||||
self.assertTrue(
|
||||
f"Cannot add {self.invalid} to memory" in str(context.exception),
|
||||
)
|
||||
|
||||
def test_load_export(self) -> None:
|
||||
"""
|
||||
Test load and export function of TemporaryMemory
|
||||
"""
|
||||
memory = TemporaryMemory()
|
||||
user_input = Msg(name="user", content="Hello")
|
||||
agent_input = Msg(
|
||||
name="agent",
|
||||
content="Hello! How can I help you?",
|
||||
)
|
||||
memory.load([user_input, agent_input])
|
||||
retrieved_mem = memory.export(to_mem=True)
|
||||
self.assertEqual(
|
||||
retrieved_mem,
|
||||
[user_input, agent_input],
|
||||
)
|
||||
|
||||
memory.export(file_path=self.file_name_1)
|
||||
memory.clear()
|
||||
self.assertEqual(
|
||||
memory.get_memory(),
|
||||
[],
|
||||
)
|
||||
memory.load(self.file_name_1)
|
||||
self.assertEqual(
|
||||
memory.get_memory(),
|
||||
[user_input, agent_input],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
116
AlgoriAgent/tests/model_test.py
Normal file
116
AlgoriAgent/tests/model_test.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for model wrapper classes and functions
|
||||
"""
|
||||
|
||||
from typing import Any, Union, List, Sequence
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from agentscope.message import Msg
|
||||
from agentscope.models import (
|
||||
ModelResponse,
|
||||
ModelWrapperBase,
|
||||
OpenAIChatWrapper,
|
||||
PostAPIModelWrapperBase,
|
||||
_get_model_wrapper,
|
||||
read_model_configs,
|
||||
load_model_by_config_name,
|
||||
clear_model_configs,
|
||||
)
|
||||
|
||||
|
||||
class TestModelWrapperSimple(ModelWrapperBase):
|
||||
"""A simple model wrapper class for test usage"""
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> ModelResponse:
|
||||
return ModelResponse(text=self.config_name)
|
||||
|
||||
def format(
|
||||
self,
|
||||
*args: Union[Msg, Sequence[Msg]],
|
||||
) -> Union[List[dict], str]:
|
||||
return ""
|
||||
|
||||
|
||||
class BasicModelTest(unittest.TestCase):
|
||||
"""Test cases for basic model wrappers"""
|
||||
|
||||
def test_model_registry(self) -> None:
|
||||
"""Test the automatic registration mechanism of model wrapper."""
|
||||
# get model wrapper class by class name
|
||||
self.assertEqual(
|
||||
_get_model_wrapper(model_type="TestModelWrapperSimple"),
|
||||
TestModelWrapperSimple,
|
||||
)
|
||||
# get model wrapper class by model type
|
||||
self.assertEqual(
|
||||
_get_model_wrapper(model_type="openai_chat"),
|
||||
OpenAIChatWrapper,
|
||||
)
|
||||
# return PostAPIModelWrapperBase if model_type is not supported
|
||||
self.assertEqual(
|
||||
_get_model_wrapper(model_type="unknown_model_wrapper"),
|
||||
PostAPIModelWrapperBase,
|
||||
)
|
||||
|
||||
@patch("loguru.logger.warning")
|
||||
def test_load_model_configs(self, mock_logging: MagicMock) -> None:
|
||||
"""Test to load model configs"""
|
||||
configs = [
|
||||
{
|
||||
"model_type": "openai_chat",
|
||||
"config_name": "gpt-4",
|
||||
"model_name": "gpt-4",
|
||||
"api_key": "xxx",
|
||||
"organization": "xxx",
|
||||
"generate_args": {"temperature": 0.5},
|
||||
},
|
||||
{
|
||||
"model_type": "post_api",
|
||||
"config_name": "my_post_api",
|
||||
"api_url": "https://xxx",
|
||||
"headers": {},
|
||||
"json_args": {},
|
||||
},
|
||||
]
|
||||
# load a list of configs
|
||||
read_model_configs(configs=configs, clear_existing=True)
|
||||
model = load_model_by_config_name("gpt-4")
|
||||
self.assertEqual(model.config_name, "gpt-4")
|
||||
model = load_model_by_config_name("my_post_api")
|
||||
self.assertEqual(model.config_name, "my_post_api")
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
load_model_by_config_name,
|
||||
"non_existent_id",
|
||||
)
|
||||
|
||||
# load a single config
|
||||
read_model_configs(configs=configs[0], clear_existing=True)
|
||||
model = load_model_by_config_name("gpt-4")
|
||||
self.assertEqual(model.config_name, "gpt-4")
|
||||
self.assertRaises(ValueError, load_model_by_config_name, "my_post_api")
|
||||
|
||||
# load model with the same id
|
||||
read_model_configs(configs=configs[0], clear_existing=False)
|
||||
mock_logging.assert_called_once_with(
|
||||
"config_name [gpt-4] already exists.",
|
||||
)
|
||||
|
||||
read_model_configs(
|
||||
configs={
|
||||
"model_type": "TestModelWrapperSimple",
|
||||
"config_name": "test_model_wrapper",
|
||||
"args": {},
|
||||
},
|
||||
)
|
||||
test_model = load_model_by_config_name("test_model_wrapper")
|
||||
response = test_model()
|
||||
self.assertEqual(response.text, "test_model_wrapper")
|
||||
clear_model_configs()
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
load_model_by_config_name,
|
||||
"test_model_wrapper",
|
||||
)
|
||||
285
AlgoriAgent/tests/monitor_test.py
Normal file
285
AlgoriAgent/tests/monitor_test.py
Normal file
@@ -0,0 +1,285 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for Monitor classes
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import uuid
|
||||
import os
|
||||
import shutil
|
||||
from loguru import logger
|
||||
|
||||
import agentscope
|
||||
from agentscope.utils import MonitorBase, QuotaExceededError, MonitorFactory
|
||||
from agentscope.utils.monitor import SqliteMonitor, DummyMonitor
|
||||
|
||||
|
||||
class MonitorFactoryTest(unittest.TestCase):
|
||||
"Test class for MonitorFactory"
|
||||
|
||||
def setUp(self) -> None:
|
||||
MonitorFactory._instance = None # pylint: disable=W0212
|
||||
self.db_path = f"test-{uuid.uuid4()}.db"
|
||||
|
||||
def test_get_monitor(self) -> None:
|
||||
"""Test get monitor method of MonitorFactory."""
|
||||
_ = MonitorFactory.get_monitor(db_path=self.db_path)
|
||||
monitor1 = MonitorFactory.get_monitor()
|
||||
monitor2 = MonitorFactory.get_monitor()
|
||||
self.assertEqual(monitor1, monitor2)
|
||||
self.assertTrue(
|
||||
monitor1.register("token_num", metric_unit="token", quota=200),
|
||||
)
|
||||
self.assertTrue(monitor2.exists("token_num"))
|
||||
self.assertTrue(monitor2.remove("token_num"))
|
||||
self.assertFalse(monitor1.exists("token_num"))
|
||||
|
||||
def test_monitor_type(self) -> None:
|
||||
"""Test get different type of monitor"""
|
||||
monitor = MonitorFactory.get_monitor(impl_type="dummy")
|
||||
self.assertTrue(isinstance(monitor, DummyMonitor))
|
||||
MonitorFactory._instance = None # pylint: disable=W0212
|
||||
monitor = MonitorFactory.get_monitor(
|
||||
impl_type="sqlite",
|
||||
db_path=self.db_path,
|
||||
)
|
||||
self.assertTrue(isinstance(monitor, SqliteMonitor))
|
||||
|
||||
def tearDown(self) -> None:
|
||||
MonitorFactory._instance = None # pylint: disable=W0212
|
||||
os.remove(self.db_path)
|
||||
|
||||
|
||||
class DummyMonitorTest(unittest.TestCase):
|
||||
"""Test class for DummyMonitor"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
MonitorFactory._instance = None # pylint: disable=W0212
|
||||
agentscope.init(
|
||||
project="test",
|
||||
name="monitor",
|
||||
save_dir="./test_runs",
|
||||
save_log=True,
|
||||
use_monitor=False,
|
||||
)
|
||||
|
||||
def test_dummy_monitor(self) -> None:
|
||||
"""test dummy monitor"""
|
||||
monitor = MonitorFactory.get_monitor()
|
||||
self.assertTrue(
|
||||
monitor.register_budget(
|
||||
model_name="qwen",
|
||||
value=100.0,
|
||||
prefix="xxx",
|
||||
),
|
||||
)
|
||||
self.assertTrue(
|
||||
monitor.register(
|
||||
"prompt_tokens",
|
||||
metric_unit="token",
|
||||
),
|
||||
)
|
||||
monitor.update({"call_counter": 1})
|
||||
|
||||
def tearDown(self) -> None:
|
||||
MonitorFactory._instance = None # pylint: disable=W0212
|
||||
logger.remove()
|
||||
shutil.rmtree("./test_runs")
|
||||
|
||||
|
||||
class MonitorTestBase(unittest.TestCase):
|
||||
"""An abstract test class for MonitorBase interface"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
if self.__class__ is MonitorTestBase:
|
||||
raise unittest.SkipTest("Base test class")
|
||||
self.monitor = self.get_monitor_instance()
|
||||
|
||||
def get_monitor_instance(self) -> MonitorBase:
|
||||
"""Implement this method for your Monitorbase implementation."""
|
||||
raise NotImplementedError
|
||||
|
||||
def test_register_exists_remove(self) -> None:
|
||||
"""Test register and remove of monitor"""
|
||||
# register token_num
|
||||
self.assertTrue(
|
||||
self.monitor.register(
|
||||
"token_num",
|
||||
metric_unit="token",
|
||||
quota=1000,
|
||||
),
|
||||
)
|
||||
# register an existing metric (ignore this operation and return false)
|
||||
self.assertFalse(
|
||||
self.monitor.register(
|
||||
"token_num",
|
||||
metric_unit="token",
|
||||
quota=2000,
|
||||
),
|
||||
)
|
||||
# exists
|
||||
self.assertTrue(self.monitor.exists("token_num"))
|
||||
# not exists
|
||||
self.assertFalse(self.monitor.exists("communication"))
|
||||
# metric content
|
||||
metric = self.monitor.get_metric("token_num")
|
||||
self.assertIsNotNone(metric)
|
||||
self.assertEqual(metric["unit"], "token") # type: ignore[index]
|
||||
self.assertEqual(metric["quota"], 1000) # type:ignore[index]
|
||||
# remove a registered metric
|
||||
self.assertTrue(self.monitor.remove("token_num"))
|
||||
self.assertFalse(self.monitor.exists("token_num"))
|
||||
# remove an not existed metric
|
||||
self.assertFalse(self.monitor.remove("cost"))
|
||||
|
||||
def test_add_clear_set_quota(self) -> None:
|
||||
"""Test add and clear of monitor"""
|
||||
self.monitor.register("token_num", metric_unit="token", quota=100)
|
||||
# add to an existing metric
|
||||
self.assertTrue(self.monitor.add("token_num", 10))
|
||||
# add to a not existing metric
|
||||
self.assertFalse(self.monitor.add("communication", 20))
|
||||
# add and exceed quota
|
||||
self.assertRaises(
|
||||
QuotaExceededError,
|
||||
self.monitor.add,
|
||||
"token_num",
|
||||
91,
|
||||
)
|
||||
# set quota of not exists metric
|
||||
self.assertFalse(self.monitor.set_quota("communication", 200))
|
||||
# update quota
|
||||
self.assertTrue(self.monitor.set_quota("token_num", 200))
|
||||
# add success and check new value
|
||||
self.assertTrue(self.monitor.add("token_num", 10))
|
||||
self.assertEqual(self.monitor.get_value("token_num"), 20)
|
||||
# clear an existing metric
|
||||
self.assertTrue(self.monitor.clear("token_num"))
|
||||
# clear an not existing metric
|
||||
self.assertFalse(self.monitor.clear("communication"))
|
||||
self.assertTrue(self.monitor.remove("token_num"))
|
||||
|
||||
def test_get(self) -> None:
|
||||
"""Test get method of monitor"""
|
||||
self.assertTrue(
|
||||
self.monitor.register(
|
||||
"agentA.token_num",
|
||||
metric_unit="token",
|
||||
quota=200,
|
||||
),
|
||||
)
|
||||
self.assertTrue(
|
||||
self.monitor.register(
|
||||
"agentB.token_num",
|
||||
metric_unit="token",
|
||||
quota=100,
|
||||
),
|
||||
)
|
||||
self.assertTrue(
|
||||
self.monitor.register(
|
||||
"agentA.communication",
|
||||
metric_unit="KB",
|
||||
quota=500,
|
||||
),
|
||||
)
|
||||
self.assertTrue(
|
||||
self.monitor.register(
|
||||
"agentB.communication",
|
||||
metric_unit="KB",
|
||||
quota=600,
|
||||
),
|
||||
)
|
||||
self.assertTrue(
|
||||
self.monitor.register(
|
||||
"agentC.token_num",
|
||||
metric_unit="token",
|
||||
quota=600,
|
||||
),
|
||||
)
|
||||
self.monitor.add("agentA.token_num", 10)
|
||||
self.assertEqual(self.monitor.get_value("agentA.token_num"), 10)
|
||||
self.assertEqual(self.monitor.get_unit("agentA.token_num"), "token")
|
||||
self.assertEqual(self.monitor.get_quota("agentA.token_num"), 200)
|
||||
self.assertIsNone(self.monitor.get_value("token_num"))
|
||||
self.assertIsNone(self.monitor.get_unit("token_num"))
|
||||
self.assertIsNone(self.monitor.get_quota("token_num"))
|
||||
self.assertIsNone(self.monitor.get_metric("token_num"))
|
||||
metric = self.monitor.get_metric("agentB.token_num")
|
||||
self.assertIsNotNone(metric)
|
||||
self.assertEqual(metric["value"], 0) # type: ignore[index]
|
||||
self.assertEqual(metric["unit"], "token") # type: ignore[index]
|
||||
self.assertEqual(metric["quota"], 100) # type: ignore[index]
|
||||
self.assertEqual(self.monitor.get_metrics(r"cost"), {})
|
||||
agenta_metrics = self.monitor.get_metrics("agentA")
|
||||
self.assertEqual(len(agenta_metrics.keys()), 2)
|
||||
comm_metrics = self.monitor.get_metrics("communication")
|
||||
self.assertEqual(len(comm_metrics.keys()), 2)
|
||||
agentc_metrics = self.monitor.get_metrics("agentC")
|
||||
self.assertEqual(len(agentc_metrics.keys()), 1)
|
||||
self.assertEqual(
|
||||
agenta_metrics["agentA.token_num"],
|
||||
{"value": 10.0, "unit": "token", "quota": 200},
|
||||
)
|
||||
|
||||
|
||||
class SqliteMonitorTest(MonitorTestBase):
|
||||
"""Test class for SqliteMonitor"""
|
||||
|
||||
def get_monitor_instance(self) -> MonitorBase:
|
||||
self.db_path = f"test-{uuid.uuid4()}.db"
|
||||
return SqliteMonitor(self.db_path)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
os.remove(self.db_path)
|
||||
|
||||
def test_register_budget(self) -> None:
|
||||
"""Test register_budget method of monitor"""
|
||||
self.assertTrue(
|
||||
self.monitor.register_budget(
|
||||
model_name="gpt-4",
|
||||
value=5,
|
||||
prefix="agent_A.gpt-4",
|
||||
),
|
||||
)
|
||||
# register an existing model with different prefix is ok
|
||||
self.assertTrue(
|
||||
self.monitor.register_budget(
|
||||
model_name="gpt-4",
|
||||
value=15,
|
||||
prefix="agent_B.gpt-4",
|
||||
),
|
||||
)
|
||||
gpt_4_3d = {
|
||||
"prompt_tokens": 50000,
|
||||
"completion_tokens": 25000,
|
||||
"total_tokens": 750000,
|
||||
}
|
||||
# agentA uses 3 dollors
|
||||
self.monitor.update(gpt_4_3d, prefix="agent_A.gpt-4")
|
||||
# agentA uses another 3 dollors and exceeds quota
|
||||
self.assertRaises(
|
||||
QuotaExceededError,
|
||||
self.monitor.update,
|
||||
gpt_4_3d,
|
||||
"agent_A.gpt-4",
|
||||
)
|
||||
self.assertLess(
|
||||
self.monitor.get_value( # type: ignore[arg-type]
|
||||
"agent_A.gpt-4.cost",
|
||||
),
|
||||
5,
|
||||
)
|
||||
# register an existing model with existing prefix is wrong
|
||||
self.assertFalse(
|
||||
self.monitor.register_budget(
|
||||
model_name="gpt-4",
|
||||
value=5,
|
||||
prefix="agent_A.gpt-4",
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
self.monitor.get_value( # type: ignore[arg-type]
|
||||
"agent_A.gpt-4.cost",
|
||||
),
|
||||
3,
|
||||
)
|
||||
102
AlgoriAgent/tests/msghub_test.py
Normal file
102
AlgoriAgent/tests/msghub_test.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Unit test for msghub."""
|
||||
import unittest
|
||||
from typing import Optional, Union, Sequence
|
||||
|
||||
from agentscope.agents import AgentBase
|
||||
from agentscope import msghub
|
||||
from agentscope.message import Msg
|
||||
|
||||
|
||||
class TestAgent(AgentBase):
|
||||
"""Test agent class for msghub."""
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
"""Reply function for agent."""
|
||||
if x is not None:
|
||||
self.memory.add(x)
|
||||
return x
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
class MsgHubTest(unittest.TestCase):
|
||||
"""
|
||||
Test for MsgHub
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExampleTest."""
|
||||
self.wisper = TestAgent("wisper")
|
||||
self.agent1 = TestAgent("agent1")
|
||||
self.agent2 = TestAgent("agent2")
|
||||
self.agent3 = TestAgent("agent3")
|
||||
|
||||
def test_msghub_operation(self) -> None:
|
||||
"""Test add, delete and broadcast operations"""
|
||||
msg1 = Msg(name="a1", content="msg1")
|
||||
msg2 = Msg(name="a2", content="msg2")
|
||||
msg3 = Msg(name="a3", content="msg3")
|
||||
msg4 = Msg(name="a4", content="msg4")
|
||||
|
||||
with msghub(participants=[self.agent1, self.agent2]) as hub:
|
||||
self.agent1(msg1)
|
||||
self.agent2(msg2)
|
||||
|
||||
hub.delete(self.agent1)
|
||||
|
||||
hub.add(self.agent3)
|
||||
|
||||
self.agent3(msg3)
|
||||
|
||||
hub.broadcast(msg4)
|
||||
|
||||
self.assertListEqual(
|
||||
self.agent2.memory.get_memory(),
|
||||
[
|
||||
msg1,
|
||||
msg2,
|
||||
msg3,
|
||||
msg4,
|
||||
],
|
||||
)
|
||||
|
||||
self.assertListEqual(self.agent1.memory.get_memory(), [msg1, msg2])
|
||||
|
||||
self.assertListEqual(self.agent3.memory.get_memory(), [msg3, msg4])
|
||||
|
||||
def test_msghub(self) -> None:
|
||||
"""msghub test."""
|
||||
|
||||
ground_truth = [
|
||||
Msg(
|
||||
name="w1",
|
||||
content="This secret that my password is 123456 can't be"
|
||||
" leaked!",
|
||||
role="wisper",
|
||||
),
|
||||
]
|
||||
|
||||
with msghub(participants=[self.wisper, self.agent1, self.agent2]):
|
||||
self.wisper(ground_truth)
|
||||
|
||||
# agent1 and agent2 heard wisper's secret!
|
||||
self.assertListEqual(
|
||||
self.agent1.memory.get_memory(),
|
||||
ground_truth,
|
||||
)
|
||||
|
||||
self.assertListEqual(
|
||||
self.agent2.memory.get_memory(),
|
||||
ground_truth,
|
||||
)
|
||||
|
||||
# agent3 didn't hear wisper's secret!
|
||||
self.assertListEqual(
|
||||
self.agent3.memory.get_memory(),
|
||||
[],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
173
AlgoriAgent/tests/ollama_test.py
Normal file
173
AlgoriAgent/tests/ollama_test.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for Ollama model APIs."""
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
import agentscope
|
||||
from agentscope.models import load_model_by_config_name
|
||||
from agentscope._runtime import _Runtime
|
||||
from agentscope.file_manager import _FileManager
|
||||
from agentscope.utils.monitor import MonitorFactory
|
||||
|
||||
|
||||
def flush() -> None:
|
||||
"""
|
||||
** Only for unittest usage. Don't use this function in your code. **
|
||||
Clear the runtime dir and destroy all singletons.
|
||||
"""
|
||||
_Runtime._flush() # pylint: disable=W0212
|
||||
_FileManager._flush() # pylint: disable=W0212
|
||||
MonitorFactory.flush()
|
||||
|
||||
|
||||
class OllamaModelWrapperTest(unittest.TestCase):
|
||||
"""Unit test for Ollama model APIs."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for OllamaModelWrapperTest."""
|
||||
self.dummy_response = {
|
||||
"model": "llama2",
|
||||
"created_at": "2024-03-12T04:16:48.911377Z",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": (
|
||||
"Hello! It's nice to meet you. Is there something I can "
|
||||
"help you with or would you like to chat?",
|
||||
),
|
||||
},
|
||||
"done": True,
|
||||
"total_duration": 20892900042,
|
||||
"load_duration": 20019679292,
|
||||
"prompt_eval_count": 22,
|
||||
"prompt_eval_duration": 149094000,
|
||||
"eval_count": 26,
|
||||
"eval_duration": 721982000,
|
||||
}
|
||||
|
||||
self.dummy_embedding = {
|
||||
"embedding": [1.0, 2.0, 3.0],
|
||||
}
|
||||
|
||||
self.dummy_generate = {
|
||||
"model": "llama2",
|
||||
"created_at": "2024-03-12T03:42:19.621919Z",
|
||||
"response": "\n1 + 1 = 2",
|
||||
"done": True,
|
||||
"context": [
|
||||
518,
|
||||
25580,
|
||||
29962,
|
||||
3532,
|
||||
14816,
|
||||
29903,
|
||||
29958,
|
||||
5299,
|
||||
829,
|
||||
14816,
|
||||
29903,
|
||||
6778,
|
||||
13,
|
||||
13,
|
||||
29896,
|
||||
29974,
|
||||
29896,
|
||||
29922,
|
||||
518,
|
||||
29914,
|
||||
25580,
|
||||
29962,
|
||||
13,
|
||||
13,
|
||||
29896,
|
||||
718,
|
||||
29871,
|
||||
29896,
|
||||
353,
|
||||
29871,
|
||||
29906,
|
||||
],
|
||||
"total_duration": 6146120041,
|
||||
"load_duration": 6677375,
|
||||
"prompt_eval_count": 9,
|
||||
"prompt_eval_duration": 5913554000,
|
||||
"eval_count": 9,
|
||||
"eval_duration": 223689000,
|
||||
}
|
||||
flush()
|
||||
|
||||
@patch("ollama.chat")
|
||||
def test_ollama_chat(self, mock_chat: MagicMock) -> None:
|
||||
"""Unit test for ollama chat API."""
|
||||
# prepare the mock
|
||||
mock_chat.return_value = self.dummy_response
|
||||
|
||||
# run test
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "my_ollama_chat",
|
||||
"model_type": "ollama_chat",
|
||||
"model_name": "llama2",
|
||||
"options": {
|
||||
"temperature": 0.5,
|
||||
},
|
||||
"keep_alive": "5m",
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("my_ollama_chat")
|
||||
response = model(messages=[{"role": "user", "content": "Hi!"}])
|
||||
|
||||
self.assertEqual(response.raw, self.dummy_response)
|
||||
|
||||
@patch("ollama.embeddings")
|
||||
def test_ollama_embedding(self, mock_embeddings: MagicMock) -> None:
|
||||
"""Unit test for ollama embeddings API."""
|
||||
# prepare the mock
|
||||
mock_embeddings.return_value = self.dummy_embedding
|
||||
|
||||
# run test
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "my_ollama_embedding",
|
||||
"model_type": "ollama_embedding",
|
||||
"model_name": "llama2",
|
||||
"options": {
|
||||
"temperature": 0.5,
|
||||
},
|
||||
"keep_alive": "5m",
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("my_ollama_embedding")
|
||||
response = model(prompt="Hi!")
|
||||
|
||||
self.assertEqual(response.raw, self.dummy_embedding)
|
||||
|
||||
@patch("ollama.generate")
|
||||
def test_ollama_generate(self, mock_generate: MagicMock) -> None:
|
||||
"""Unit test for ollama generate API."""
|
||||
# prepare the mock
|
||||
mock_generate.return_value = self.dummy_generate
|
||||
|
||||
# run test
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "my_ollama_generate",
|
||||
"model_type": "ollama_generate",
|
||||
"model_name": "llama2",
|
||||
"options": None,
|
||||
"keep_alive": "5m",
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("my_ollama_generate")
|
||||
response = model(prompt="1+1=")
|
||||
|
||||
self.assertEqual(response.raw, self.dummy_generate)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Clean up after each test."""
|
||||
flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
547
AlgoriAgent/tests/openai_services_test.py
Normal file
547
AlgoriAgent/tests/openai_services_test.py
Normal file
@@ -0,0 +1,547 @@
|
||||
# -*- 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()
|
||||
106
AlgoriAgent/tests/operate_file_test.py
Normal file
106
AlgoriAgent/tests/operate_file_test.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Operate file test."""
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
from agentscope.service import (
|
||||
create_file,
|
||||
delete_file,
|
||||
move_file,
|
||||
create_directory,
|
||||
delete_directory,
|
||||
move_directory,
|
||||
read_text_file,
|
||||
write_text_file,
|
||||
read_json_file,
|
||||
write_json_file,
|
||||
list_directory_content,
|
||||
get_current_directory,
|
||||
)
|
||||
from agentscope.service.service_status import ServiceExecStatus
|
||||
|
||||
|
||||
class OperateFileTest(unittest.TestCase):
|
||||
"""
|
||||
Operate file test.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Set up test variables."""
|
||||
self.file_name = "tmp_file.txt"
|
||||
self.moved_file_name = "moved_tmp_file.txt"
|
||||
self.dir_name = "tmp_dir"
|
||||
self.moved_dir_name = "moved_tmp_dir"
|
||||
|
||||
self.txt_file_name = "tmp_txt_file.txt"
|
||||
self.json_file_name = "tmp_json_file.json"
|
||||
|
||||
self.tearDown()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Clean up before & after tests."""
|
||||
if os.path.exists(self.file_name):
|
||||
os.remove(self.file_name)
|
||||
if os.path.exists(self.moved_file_name):
|
||||
os.remove(self.moved_file_name)
|
||||
if os.path.exists(self.dir_name):
|
||||
shutil.rmtree(self.dir_name)
|
||||
if os.path.exists(self.moved_dir_name):
|
||||
shutil.rmtree(self.moved_dir_name)
|
||||
if os.path.exists(self.txt_file_name):
|
||||
os.remove(self.txt_file_name)
|
||||
|
||||
def test_file(self) -> None:
|
||||
"""Execute file test."""
|
||||
is_success = create_file(self.file_name, "This is a test").status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
is_success = move_file(self.file_name, self.moved_file_name).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
is_success = delete_file(self.moved_file_name).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
def test_dir(self) -> None:
|
||||
"""Execute dir test."""
|
||||
|
||||
is_success = get_current_directory().status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
is_success = create_directory(self.dir_name).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
is_success = move_directory(self.dir_name, self.moved_dir_name).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
is_success = list_directory_content(self.moved_dir_name).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
is_success = delete_directory(self.moved_dir_name).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
def test_txt(self) -> None:
|
||||
"""Execute txt test."""
|
||||
is_success = write_text_file(
|
||||
self.txt_file_name,
|
||||
"This is a test",
|
||||
True,
|
||||
).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
info = read_text_file(self.txt_file_name).content
|
||||
self.assertEqual(info, "This is a test")
|
||||
|
||||
def test_json(self) -> None:
|
||||
"""Execute json test."""
|
||||
data = {"test": "This is a test"}
|
||||
is_success = write_json_file(self.json_file_name, data, True).status
|
||||
self.assertEqual(is_success, ServiceExecStatus.SUCCESS)
|
||||
|
||||
info = read_json_file(self.json_file_name).content
|
||||
self.assertEqual(info, f"{data}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
288
AlgoriAgent/tests/parser_test.py
Normal file
288
AlgoriAgent/tests/parser_test.py
Normal file
@@ -0,0 +1,288 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for model response parser."""
|
||||
import unittest
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from agentscope.models import ModelResponse
|
||||
from agentscope.parsers import (
|
||||
MarkdownJsonDictParser,
|
||||
MarkdownJsonObjectParser,
|
||||
MarkdownCodeBlockParser,
|
||||
MultiTaggedContentParser,
|
||||
TaggedContent,
|
||||
)
|
||||
from agentscope.parsers.parser_base import DictFilterMixin
|
||||
|
||||
|
||||
class ModelResponseParserTest(unittest.TestCase):
|
||||
"""Unit test for model response parser."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExampleTest."""
|
||||
self.res_dict_1 = ModelResponse(
|
||||
text=(
|
||||
"```json\n"
|
||||
'{"speak": "Hello, world!", '
|
||||
'"thought": "xxx", '
|
||||
'"end_discussion": true}\n```'
|
||||
),
|
||||
)
|
||||
self.instruction_dict_1 = (
|
||||
"Respond a JSON dictionary in a markdown's fenced code block "
|
||||
"as follows:\n"
|
||||
"```json\n"
|
||||
'{"speak": "what you speak", '
|
||||
'"thought": "what you thought", '
|
||||
'"end_discussion": true/false}\n'
|
||||
"```"
|
||||
)
|
||||
self.res_dict_2 = ModelResponse(
|
||||
text="[SPEAK]Hello, world![/SPEAK]\n"
|
||||
"[THOUGHT]xxx[/THOUGHT]\n"
|
||||
"[END_DISCUSSION]true[/END_DISCUSSION]",
|
||||
)
|
||||
self.instruction_dict_2 = (
|
||||
"Respond with specific tags as outlined below, and the content "
|
||||
"between [END_DISCUSSION] and [/END_DISCUSSION] MUST be a JSON "
|
||||
"object:\n"
|
||||
"[SPEAK]what you speak[/SPEAK]\n"
|
||||
"[THOUGHT]what you thought[/THOUGHT]\n"
|
||||
"[END_DISCUSSION]true/false[/END_DISCUSSION]"
|
||||
)
|
||||
self.gt_dict = {
|
||||
"speak": "Hello, world!",
|
||||
"thought": "xxx",
|
||||
"end_discussion": True,
|
||||
}
|
||||
self.hint_dict = (
|
||||
'{"speak": "what you speak", '
|
||||
'"thought": "what you thought", '
|
||||
'"end_discussion": true/false}'
|
||||
)
|
||||
|
||||
self.instruction_dict_3 = (
|
||||
"Respond a JSON dictionary in a markdown's fenced code block as "
|
||||
"follows:\n"
|
||||
"```json\n"
|
||||
"{a_JSON_dictionary}\n"
|
||||
"```\n"
|
||||
"The generated JSON dictionary MUST follow this schema: \n"
|
||||
"{'properties': {'speak': {'description': 'what you speak', "
|
||||
"'title': 'Speak', 'type': 'string'}, 'thought': {'description': "
|
||||
"'what you thought', 'title': 'Thought', 'type': 'string'}, "
|
||||
"'end_discussion': {'description': 'whether the discussion "
|
||||
"reached an agreement or not', 'title': 'End Discussion', "
|
||||
"'type': 'boolean'}}, 'required': ['speak', 'thought', "
|
||||
"'end_discussion'], 'title': 'Schema', 'type': 'object'}"
|
||||
)
|
||||
|
||||
self.gt_to_memory = {"speak": "Hello, world!", "thought": "xxx"}
|
||||
self.gt_to_content = "Hello, world!"
|
||||
self.gt_to_metadata = {"end_discussion": True}
|
||||
|
||||
self.res_list = ModelResponse(text="""```json\n[1,2,3]\n```""")
|
||||
self.instruction_list = (
|
||||
"You should respond a json object in a json fenced code block as "
|
||||
"follows:\n"
|
||||
"```json\n"
|
||||
"{Your generated list of numbers}\n"
|
||||
"```"
|
||||
)
|
||||
self.gt_list = [1, 2, 3]
|
||||
self.hint_list = "{Your generated list of numbers}"
|
||||
|
||||
self.res_float = ModelResponse(text="""```json\n3.14\n```""")
|
||||
self.instruction_float = (
|
||||
"You should respond a json object in a json fenced code block as "
|
||||
"follows:\n"
|
||||
"```json\n"
|
||||
"{Your generated float number}\n"
|
||||
"```"
|
||||
)
|
||||
self.gt_float = 3.14
|
||||
self.hint_float = "{Your generated float number}"
|
||||
|
||||
self.res_code = ModelResponse(
|
||||
text="""```python\nprint("Hello, world!")\n```""",
|
||||
)
|
||||
self.instruction_code = (
|
||||
"You should generate python code in a python fenced code block as "
|
||||
"follows: \n"
|
||||
"```python\n"
|
||||
"${your_python_code}\n"
|
||||
"```"
|
||||
)
|
||||
self.instruction_code_with_hint = (
|
||||
"You should generate python code in a python fenced code block as "
|
||||
"follows: \n"
|
||||
"```python\n"
|
||||
"abc\n"
|
||||
"```"
|
||||
)
|
||||
self.gt_code = """\nprint("Hello, world!")\n"""
|
||||
|
||||
def test_markdownjsondictparser_with_schema(self) -> None:
|
||||
"""Test for MarkdownJsonDictParser with schema"""
|
||||
|
||||
class Schema(BaseModel): # pylint: disable=missing-class-docstring
|
||||
speak: str = Field(description="what you speak")
|
||||
thought: str = Field(description="what you thought")
|
||||
end_discussion: bool = Field(
|
||||
description="whether the discussion reached an agreement or "
|
||||
"not",
|
||||
)
|
||||
|
||||
parser = MarkdownJsonDictParser(
|
||||
content_hint=Schema,
|
||||
keys_to_memory=["speak", "thought"],
|
||||
keys_to_content="speak",
|
||||
keys_to_metadata=["end_discussion"],
|
||||
)
|
||||
|
||||
self.assertEqual(parser.format_instruction, self.instruction_dict_3)
|
||||
|
||||
res = parser.parse(self.res_dict_1)
|
||||
|
||||
self.assertDictEqual(res.parsed, self.gt_dict)
|
||||
|
||||
res = parser.parse(
|
||||
ModelResponse(
|
||||
text="""```json
|
||||
{
|
||||
"speak" : "Hello, world!",
|
||||
"thought" : "xxx",
|
||||
"end_discussion" : "true"
|
||||
}
|
||||
```""",
|
||||
),
|
||||
)
|
||||
|
||||
self.assertDictEqual(res.parsed, self.gt_dict)
|
||||
|
||||
def test_markdownjsondictparser(self) -> None:
|
||||
"""Test for MarkdownJsonDictParser"""
|
||||
parser = MarkdownJsonDictParser(
|
||||
content_hint=self.hint_dict,
|
||||
keys_to_memory=["speak", "thought"],
|
||||
keys_to_content="speak",
|
||||
keys_to_metadata=["end_discussion"],
|
||||
)
|
||||
|
||||
self.assertEqual(parser.format_instruction, self.instruction_dict_1)
|
||||
|
||||
res = parser.parse(self.res_dict_1)
|
||||
|
||||
self.assertDictEqual(res.parsed, self.gt_dict)
|
||||
|
||||
# test filter functions
|
||||
self.assertDictEqual(parser.to_memory(res.parsed), self.gt_to_memory)
|
||||
self.assertEqual(parser.to_content(res.parsed), self.gt_to_content)
|
||||
self.assertDictEqual(
|
||||
parser.to_metadata(res.parsed),
|
||||
self.gt_to_metadata,
|
||||
)
|
||||
|
||||
def test_markdownjsonobjectparser(self) -> None:
|
||||
"""Test for MarkdownJsonObjectParser"""
|
||||
# list
|
||||
parser_list = MarkdownJsonObjectParser(content_hint=self.hint_list)
|
||||
|
||||
self.assertEqual(parser_list.format_instruction, self.instruction_list)
|
||||
|
||||
res_list = parser_list.parse(self.res_list)
|
||||
self.assertListEqual(res_list.parsed, self.gt_list)
|
||||
|
||||
# float
|
||||
parser_float = MarkdownJsonObjectParser(content_hint=self.hint_float)
|
||||
|
||||
self.assertEqual(
|
||||
parser_float.format_instruction,
|
||||
self.instruction_float,
|
||||
)
|
||||
|
||||
res_float = parser_float.parse(self.res_float)
|
||||
self.assertEqual(res_float.parsed, self.gt_float)
|
||||
|
||||
def test_markdowncodeblockparser(self) -> None:
|
||||
"""Test for MarkdownCodeBlockParser"""
|
||||
parser = MarkdownCodeBlockParser(language_name="python")
|
||||
|
||||
self.assertEqual(parser.format_instruction, self.instruction_code)
|
||||
|
||||
res = parser.parse(self.res_code)
|
||||
|
||||
self.assertEqual(res.parsed, self.gt_code)
|
||||
|
||||
def test_markdowncodeblockparser_with_hint(self) -> None:
|
||||
"""Test for MarkdownCodeBlockParser"""
|
||||
parser = MarkdownCodeBlockParser(
|
||||
language_name="python",
|
||||
content_hint="abc",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
parser.format_instruction,
|
||||
self.instruction_code_with_hint,
|
||||
)
|
||||
|
||||
res = parser.parse(self.res_code)
|
||||
|
||||
self.assertEqual(res.parsed, self.gt_code)
|
||||
|
||||
def test_multitaggedcontentparser(self) -> None:
|
||||
"""Test for MultiTaggedContentParser"""
|
||||
parser = MultiTaggedContentParser(
|
||||
TaggedContent(
|
||||
"speak",
|
||||
tag_begin="[SPEAK]",
|
||||
content_hint="what you speak",
|
||||
tag_end="[/SPEAK]",
|
||||
),
|
||||
TaggedContent(
|
||||
"thought",
|
||||
tag_begin="[THOUGHT]",
|
||||
content_hint="what you thought",
|
||||
tag_end="[/THOUGHT]",
|
||||
),
|
||||
TaggedContent(
|
||||
"end_discussion",
|
||||
tag_begin="[END_DISCUSSION]",
|
||||
content_hint="true/false",
|
||||
tag_end="[/END_DISCUSSION]",
|
||||
parse_json=True,
|
||||
),
|
||||
keys_to_memory=["speak", "thought"],
|
||||
keys_to_content="speak",
|
||||
keys_to_metadata=["end_discussion"],
|
||||
)
|
||||
|
||||
self.assertEqual(parser.format_instruction, self.instruction_dict_2)
|
||||
|
||||
res = parser.parse(self.res_dict_2)
|
||||
|
||||
self.assertDictEqual(res.parsed, self.gt_dict)
|
||||
|
||||
# test filter functions
|
||||
self.assertDictEqual(parser.to_memory(res.parsed), self.gt_to_memory)
|
||||
self.assertEqual(parser.to_content(res.parsed), self.gt_to_content)
|
||||
self.assertDictEqual(
|
||||
parser.to_metadata(res.parsed),
|
||||
self.gt_to_metadata,
|
||||
)
|
||||
|
||||
def test_DictFilterMixin_default_value(self) -> None:
|
||||
"""Test the default value of the DictFilterMixin class"""
|
||||
mixin = DictFilterMixin(
|
||||
keys_to_memory=True,
|
||||
keys_to_content=True,
|
||||
keys_to_metadata=False,
|
||||
)
|
||||
|
||||
self.assertDictEqual(mixin.to_memory(self.gt_dict), self.gt_dict)
|
||||
self.assertDictEqual(mixin.to_content(self.gt_dict), self.gt_dict)
|
||||
self.assertEqual(mixin.to_metadata(self.gt_dict), None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
259
AlgoriAgent/tests/pipeline_test.py
Normal file
259
AlgoriAgent/tests/pipeline_test.py
Normal file
@@ -0,0 +1,259 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for pipeline classes and functions
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import random
|
||||
|
||||
from agentscope.pipelines import (
|
||||
SequentialPipeline,
|
||||
IfElsePipeline,
|
||||
SwitchPipeline,
|
||||
ForLoopPipeline,
|
||||
WhileLoopPipeline,
|
||||
sequentialpipeline,
|
||||
ifelsepipeline,
|
||||
)
|
||||
|
||||
from agentscope.agents import AgentBase
|
||||
|
||||
|
||||
class Add(AgentBase):
|
||||
"""Operator for adding a value"""
|
||||
|
||||
def __init__(self, name: str, value: int) -> None:
|
||||
self.name = name
|
||||
self.value = value
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, x: dict = None) -> dict:
|
||||
x["value"] += self.value
|
||||
return x
|
||||
|
||||
|
||||
class Mult(AgentBase):
|
||||
"""Operator for multiplying a value"""
|
||||
|
||||
def __init__(self, name: str, value: int) -> None:
|
||||
self.name = name
|
||||
self.value = value
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, x: dict = None) -> dict:
|
||||
x["value"] *= self.value
|
||||
return x
|
||||
|
||||
|
||||
class If_agent(AgentBase):
|
||||
"""Operator for If condition"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, _: dict = None) -> dict:
|
||||
return {"operation": "A"}
|
||||
|
||||
|
||||
class Else_agent(AgentBase):
|
||||
"""Operator for Else condition"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, _: dict = None) -> dict:
|
||||
return {"operation": "B"}
|
||||
|
||||
|
||||
class Case_agent(AgentBase):
|
||||
"""Operator for Switch condition"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, x: dict = None) -> dict:
|
||||
return {"operation": x["text"].strip()}
|
||||
|
||||
|
||||
class Default_agent(AgentBase):
|
||||
"""Operator for Switch default condition"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, x: dict = None) -> dict:
|
||||
return {"operation": "IDLE"}
|
||||
|
||||
|
||||
class Loop_for_agent(AgentBase):
|
||||
"""Operator for Loop condition"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, x: dict = None) -> dict:
|
||||
x["value"] += 1
|
||||
return x
|
||||
|
||||
|
||||
class Loop_while_agent(AgentBase):
|
||||
"""Operator for Loop condition"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
super().__init__(name=name)
|
||||
|
||||
def __call__(self, x: dict = None) -> dict:
|
||||
x["token_num"] += random.randint(1, 100)
|
||||
x["round"] += 1
|
||||
return x
|
||||
|
||||
|
||||
class BasicPipelineTest(unittest.TestCase):
|
||||
"""Test cases for Basic Pipelines"""
|
||||
|
||||
def test_sequential_pipeline(self) -> None:
|
||||
"""Test SequentialPipeline executes agents sequentially"""
|
||||
|
||||
add1 = Add("add1", 1)
|
||||
add2 = Add("add2", 2)
|
||||
mult3 = Mult("mult3", 3)
|
||||
|
||||
x = {"value": 0}
|
||||
pipeline = SequentialPipeline([add1, add2, mult3])
|
||||
self.assertEqual(pipeline(x)["value"], 9)
|
||||
x = {"value": 0}
|
||||
pipeline = SequentialPipeline([add1, mult3, add2])
|
||||
self.assertEqual(pipeline(x)["value"], 5)
|
||||
x = {"value": 0}
|
||||
pipeline = SequentialPipeline([mult3, add1, add2])
|
||||
self.assertEqual(pipeline(x)["value"], 3)
|
||||
|
||||
def test_if_else_pipeline(self) -> None:
|
||||
"""Test IfElsePipeline executes if or else agent based on condition"""
|
||||
if_x = {"text": "xxxx [PASS]"}
|
||||
else_x = {"text": "xxxx"}
|
||||
|
||||
if_agent = If_agent("if_agent")
|
||||
else_agent = Else_agent("else_agent")
|
||||
|
||||
p = IfElsePipeline(
|
||||
condition_func=lambda x: "[PASS]" in x["text"],
|
||||
if_body_operators=if_agent,
|
||||
else_body_operators=else_agent,
|
||||
)
|
||||
x = p(if_x)
|
||||
self.assertEqual(x["operation"], "A")
|
||||
x = p(else_x)
|
||||
self.assertEqual(x["operation"], "B")
|
||||
|
||||
def test_switch_pipeline(self) -> None:
|
||||
"""Test SwitchPipeline executes one of the case agents or the default
|
||||
agent
|
||||
"""
|
||||
tool_types = ["A", "B", "C", "D"]
|
||||
|
||||
case_agent = Case_agent(name="case_agent")
|
||||
|
||||
default_agent = Default_agent("default_agent")
|
||||
|
||||
case_agents = {k: case_agent for k in tool_types}
|
||||
|
||||
p = SwitchPipeline(
|
||||
condition_func=lambda x: x["text"].strip(),
|
||||
case_operators=case_agents,
|
||||
default_operators=default_agent,
|
||||
)
|
||||
for tool in tool_types:
|
||||
x = {"text": f"\n\n{tool}\n\n"}
|
||||
x = p(x)
|
||||
self.assertEqual(x["operation"], tool)
|
||||
x = p({"text": "hello"})
|
||||
self.assertEqual(x["operation"], "IDLE")
|
||||
|
||||
def test_for_pipeline(self) -> None:
|
||||
"""Test ForLoopPipeline"""
|
||||
|
||||
loop_agent = Loop_for_agent("loop_agent")
|
||||
|
||||
# test max loop
|
||||
x = {"value": 0}
|
||||
p = ForLoopPipeline(loop_body_operators=loop_agent, max_loop=10)
|
||||
x = p(x)
|
||||
self.assertEqual(x["value"], 10)
|
||||
|
||||
x = {"value": 0}
|
||||
p = ForLoopPipeline(
|
||||
loop_body_operators=loop_agent,
|
||||
max_loop=10,
|
||||
break_func=lambda x: x["value"] > 5,
|
||||
)
|
||||
x = p(x)
|
||||
self.assertEqual(x["value"], 6)
|
||||
|
||||
def test_while_pipeline(self) -> None:
|
||||
"""Test WhileLoopPipeline"""
|
||||
|
||||
loop_agent = Loop_while_agent("loop_agent")
|
||||
|
||||
p = WhileLoopPipeline(
|
||||
loop_body_operators=loop_agent,
|
||||
condition_func=lambda i, x: i < 10 and not x["token_num"] > 500,
|
||||
)
|
||||
for _ in range(50):
|
||||
x = {"token_num": 0, "round": 0}
|
||||
x = p(x)
|
||||
self.assertTrue(x["round"] >= 10 or x["token_num"] > 500)
|
||||
|
||||
|
||||
class FunctionalPipelineTest(unittest.TestCase):
|
||||
"""Test cases for Functional Pipelines"""
|
||||
|
||||
def test_sequential_pipeline(self) -> None:
|
||||
"""Test SequentialPipeline executes agents sequentially"""
|
||||
|
||||
add1 = Add("add1", 1)
|
||||
add2 = Add("add2", 2)
|
||||
mult3 = Mult("mult3", 3)
|
||||
|
||||
x = {"value": 0}
|
||||
x = sequentialpipeline(x=x, operators=[add1, add2, mult3])
|
||||
self.assertEqual(x["value"], 9)
|
||||
x = {"value": 0}
|
||||
x = sequentialpipeline(x=x, operators=[add1, mult3, add2])
|
||||
self.assertEqual(x["value"], 5)
|
||||
x = {"value": 0}
|
||||
x = sequentialpipeline(x=x, operators=[mult3, add1, add2])
|
||||
self.assertEqual(x["value"], 3)
|
||||
|
||||
def test_if_else_pipeline(self) -> None:
|
||||
"""Test ifelsepipeline executes if or else agent based on condition"""
|
||||
if_x = {"text": "xxxx [PASS]"}
|
||||
else_x = {"text": "xxxx"}
|
||||
|
||||
if_agent = If_agent("if_agent")
|
||||
else_agent = Else_agent("else_agent")
|
||||
|
||||
if_x = ifelsepipeline(
|
||||
x=if_x,
|
||||
condition_func=lambda x: "[PASS]" in x["text"],
|
||||
if_body_operators=if_agent,
|
||||
else_body_operators=else_agent,
|
||||
)
|
||||
else_x = ifelsepipeline(
|
||||
x=else_x,
|
||||
condition_func=lambda x: "[PASS]" in x["text"],
|
||||
if_body_operators=if_agent,
|
||||
else_body_operators=else_agent,
|
||||
)
|
||||
self.assertEqual(if_x["operation"], "A")
|
||||
self.assertEqual(else_x["operation"], "B")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
135
AlgoriAgent/tests/prompt_engine_test.py
Normal file
135
AlgoriAgent/tests/prompt_engine_test.py
Normal file
@@ -0,0 +1,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for prompt engine."""
|
||||
import unittest
|
||||
from typing import Any
|
||||
|
||||
from agentscope.models import read_model_configs, ModelResponse
|
||||
from agentscope.models import load_model_by_config_name
|
||||
from agentscope.models import OpenAIWrapperBase
|
||||
from agentscope.prompt import PromptEngine
|
||||
|
||||
|
||||
class PromptEngineTest(unittest.TestCase):
|
||||
"""Unit test for prompt engine."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for PromptEngineTest."""
|
||||
self.name = "white"
|
||||
self.sys_prompt = (
|
||||
"You're a player in a chess game, and you are playing {name}."
|
||||
)
|
||||
self.dialog_history = [
|
||||
{"name": "white player", "content": "Move to E4."},
|
||||
{"name": "black player", "content": "Okay, I moved to F4."},
|
||||
{"name": "white player", "content": "Move to F5."},
|
||||
]
|
||||
self.hint = "Now decide your next move."
|
||||
self.prefix = "{name} player: "
|
||||
|
||||
read_model_configs(
|
||||
[
|
||||
{
|
||||
"model_type": "post_api",
|
||||
"config_name": "open-source",
|
||||
"api_url": "http://xxx",
|
||||
"headers": {"Autherization": "Bearer {API_TOKEN}"},
|
||||
"parameters": {
|
||||
"temperature": 0.5,
|
||||
},
|
||||
},
|
||||
{
|
||||
"model_type": "openai_chat",
|
||||
"config_name": "gpt-4",
|
||||
"model_name": "gpt-4",
|
||||
"api_key": "xxx",
|
||||
"organization": "xxx",
|
||||
},
|
||||
],
|
||||
clear_existing=True,
|
||||
)
|
||||
|
||||
def test_list_prompt(self) -> None:
|
||||
"""Test for list prompt."""
|
||||
|
||||
class TestModelWrapperBase(OpenAIWrapperBase):
|
||||
"""Test model wrapper."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.max_length = 1000
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> ModelResponse:
|
||||
return ModelResponse(text="")
|
||||
|
||||
def _register_default_metrics(self) -> None:
|
||||
pass
|
||||
|
||||
model = TestModelWrapperBase()
|
||||
engine = PromptEngine(model)
|
||||
|
||||
prompt = engine.join(
|
||||
self.sys_prompt,
|
||||
self.dialog_history,
|
||||
self.hint,
|
||||
format_map={"name": self.name},
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "You're a player in a chess game, and you are "
|
||||
"playing white.",
|
||||
},
|
||||
{
|
||||
"name": "white player",
|
||||
"role": "assistant",
|
||||
"content": "Move to E4.",
|
||||
},
|
||||
{
|
||||
"name": "black player",
|
||||
"role": "assistant",
|
||||
"content": "Okay, I moved to F4.",
|
||||
},
|
||||
{
|
||||
"name": "white player",
|
||||
"role": "assistant",
|
||||
"content": "Move to F5.",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Now decide your next move.",
|
||||
},
|
||||
],
|
||||
prompt,
|
||||
)
|
||||
|
||||
def test_str_prompt(self) -> None:
|
||||
"""Test for string prompt."""
|
||||
model = load_model_by_config_name("open-source")
|
||||
engine = PromptEngine(model)
|
||||
|
||||
prompt = engine.join(
|
||||
self.sys_prompt,
|
||||
self.dialog_history,
|
||||
self.hint,
|
||||
self.prefix,
|
||||
format_map={"name": self.name},
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
"""You're a player in a chess game, and you are playing white.
|
||||
white player: Move to E4.
|
||||
black player: Okay, I moved to F4.
|
||||
white player: Move to F5.
|
||||
Now decide your next move.
|
||||
white player: """,
|
||||
prompt,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
107
AlgoriAgent/tests/record_api_invocation_test.py
Normal file
107
AlgoriAgent/tests/record_api_invocation_test.py
Normal file
@@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Test for record api invocation."""
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
import agentscope
|
||||
from agentscope.models import OpenAIChatWrapper
|
||||
from agentscope._runtime import _Runtime
|
||||
from agentscope.file_manager import _FileManager, file_manager
|
||||
from agentscope.utils.monitor import MonitorFactory
|
||||
|
||||
|
||||
def flush() -> None:
|
||||
"""
|
||||
** Only for unittest usage. Don't use this function in your code. **
|
||||
Clear the runtime dir and destroy all singletons.
|
||||
"""
|
||||
_Runtime._flush() # pylint: disable=W0212
|
||||
_FileManager._flush() # pylint: disable=W0212
|
||||
MonitorFactory.flush()
|
||||
|
||||
|
||||
class RecordApiInvocation(unittest.TestCase):
|
||||
"""
|
||||
Test for record api invocation.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for RecordApiInvocation."""
|
||||
|
||||
self.dummy_response = {"content": "dummy_response"}
|
||||
|
||||
flush()
|
||||
|
||||
@patch("openai.OpenAI")
|
||||
def test_record_model_invocation_with_init(
|
||||
self,
|
||||
mock_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test record model invocation with calling init function."""
|
||||
# prepare mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.model_dump.return_value = self.dummy_response
|
||||
mock_response.usage.model_dump.return_value = {}
|
||||
|
||||
# link mock response to mock client
|
||||
mock_openai_instance = mock_client.return_value
|
||||
mock_openai_instance.chat.completions.create.return_value = (
|
||||
mock_response
|
||||
)
|
||||
|
||||
# test
|
||||
agentscope.init(save_api_invoke=True)
|
||||
model = OpenAIChatWrapper(
|
||||
config_name="gpt-4",
|
||||
api_key="xxx",
|
||||
organization="xxx",
|
||||
)
|
||||
|
||||
_ = model(messages=[])
|
||||
|
||||
# assert
|
||||
self.assert_invocation_record()
|
||||
|
||||
def assert_invocation_record(self) -> None:
|
||||
"""Assert invocation record."""
|
||||
run_dir = file_manager.dir_root
|
||||
records = [
|
||||
_
|
||||
for _ in os.listdir(
|
||||
os.path.join(run_dir, "invoke"),
|
||||
)
|
||||
if _.startswith("model_OpenAIChatWrapper_")
|
||||
]
|
||||
|
||||
# only one record is here
|
||||
self.assertEqual(len(records), 1)
|
||||
|
||||
filename = records[0]
|
||||
timestamp = filename.split("_")[2]
|
||||
|
||||
with open(
|
||||
os.path.join(run_dir, "invoke", filename),
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
) as file:
|
||||
self.assertEqual(
|
||||
json.load(file),
|
||||
{
|
||||
"model_class": "OpenAIChatWrapper",
|
||||
"timestamp": timestamp,
|
||||
"arguments": {
|
||||
"model": "gpt-4",
|
||||
"messages": [],
|
||||
},
|
||||
"response": {
|
||||
"content": "dummy_response",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
"""Tear down for RecordApiInvocation."""
|
||||
flush()
|
||||
81
AlgoriAgent/tests/retrieval_from_list_test.py
Normal file
81
AlgoriAgent/tests/retrieval_from_list_test.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Python web search test."""
|
||||
from datetime import datetime
|
||||
import unittest
|
||||
from typing import Any
|
||||
|
||||
from agentscope.service import retrieve_from_list, cos_sim
|
||||
from agentscope.service.service_status import ServiceExecStatus
|
||||
from agentscope.message import MessageBase, Msg
|
||||
from agentscope.memory.temporary_memory import TemporaryMemory
|
||||
from agentscope.models import OpenAIEmbeddingWrapper, ModelResponse
|
||||
|
||||
|
||||
class TestRetrieval(unittest.TestCase):
|
||||
"""TestMemRetrieval for memory retrieval unit test."""
|
||||
|
||||
def test_retrieval_from_list(self) -> None:
|
||||
"""test memory retrieval"""
|
||||
|
||||
class DummyModel(OpenAIEmbeddingWrapper):
|
||||
"""
|
||||
Dummy model wrapper for testing
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> ModelResponse:
|
||||
print(*args, **kwargs)
|
||||
return ModelResponse(raw={})
|
||||
|
||||
dummy_model = DummyModel()
|
||||
|
||||
query = Msg(name="Lora", content="test query", role="assistant")
|
||||
query.embedding = [0, 1]
|
||||
query.timestamp = "2023-12-18 21:40:59"
|
||||
m1 = Msg(name="env", content="test", role="assistant")
|
||||
m1.embedding = [1, 0]
|
||||
m1.timestamp = "2023-12-18 21:45:59"
|
||||
m2 = Msg(name="env", content="test2", role="assistant")
|
||||
m2.embedding = [0.5, 0.5]
|
||||
m2.timestamp = "2023-12-18 21:50:59"
|
||||
memory = TemporaryMemory(config={}, embedding_model=dummy_model)
|
||||
memory.add(m1)
|
||||
memory.add(m2)
|
||||
|
||||
def score_func(m1: MessageBase, m2: MessageBase) -> float:
|
||||
relevance = cos_sim(m1.embedding, m2.embedding).content
|
||||
time_gap = (
|
||||
datetime.strptime(m1.timestamp, "%Y-%m-%d %H:%M:%S")
|
||||
- datetime.strptime(m2.timestamp, "%Y-%m-%d %H:%M:%S")
|
||||
).total_seconds() / 60
|
||||
recency = 0.99**time_gap
|
||||
return recency + relevance
|
||||
|
||||
retrieved = retrieve_from_list(
|
||||
query,
|
||||
list(memory.get_memory()),
|
||||
score_func,
|
||||
embedding_model=dummy_model,
|
||||
preserve_order=False,
|
||||
)
|
||||
self.assertEqual(retrieved.status, ServiceExecStatus.SUCCESS)
|
||||
self.assertEqual(retrieved.content[0][2], m2)
|
||||
self.assertEqual(retrieved.content[1][2], m1)
|
||||
|
||||
retrieved = retrieve_from_list(
|
||||
query,
|
||||
list(memory.get_memory(recent_n=2)),
|
||||
score_func,
|
||||
top_k=2,
|
||||
embedding_model=None,
|
||||
preserve_order=True,
|
||||
)
|
||||
self.assertEqual(retrieved.status, ServiceExecStatus.SUCCESS)
|
||||
self.assertEqual(retrieved.content[0][2], m1)
|
||||
|
||||
|
||||
# This allows the tests to be run from the command line
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
610
AlgoriAgent/tests/rpc_agent_test.py
Normal file
610
AlgoriAgent/tests/rpc_agent_test.py
Normal file
@@ -0,0 +1,610 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for rpc agent classes
|
||||
"""
|
||||
import unittest
|
||||
import time
|
||||
import os
|
||||
import shutil
|
||||
from typing import Optional, Union, Sequence
|
||||
|
||||
from loguru import logger
|
||||
|
||||
import agentscope
|
||||
from agentscope.agents import AgentBase, DistConf
|
||||
from agentscope.server import RpcAgentServerLauncher
|
||||
from agentscope.message import Msg
|
||||
from agentscope.message import PlaceholderMessage
|
||||
from agentscope.message import deserialize
|
||||
from agentscope.msghub import msghub
|
||||
from agentscope.pipelines import sequentialpipeline
|
||||
from agentscope.utils import MonitorFactory, QuotaExceededError
|
||||
|
||||
|
||||
class DemoRpcAgent(AgentBase):
|
||||
"""A demo Rpc agent for test usage."""
|
||||
|
||||
def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__(**kwargs)
|
||||
self.id = 0
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
"""Response after 2s"""
|
||||
x.id = self.id
|
||||
self.id += 1
|
||||
time.sleep(2)
|
||||
return x
|
||||
|
||||
|
||||
class DemoRpcAgentAdd(AgentBase):
|
||||
"""A demo Rpc agent for test usage"""
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
"""add the value, wait 1s"""
|
||||
x.content["value"] += 1
|
||||
time.sleep(1)
|
||||
return x
|
||||
|
||||
|
||||
class DemoLocalAgentAdd(AgentBase):
|
||||
"""A demo local agent for test usage"""
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
"""add the value, wait 1s"""
|
||||
x.content["value"] += 1
|
||||
time.sleep(1)
|
||||
return x
|
||||
|
||||
|
||||
class DemoRpcAgentWithMemory(AgentBase):
|
||||
"""A demo Rpc agent that count its memory"""
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
self.memory.add(x)
|
||||
msg = Msg(
|
||||
name=self.name,
|
||||
content={"mem_size": self.memory.size()},
|
||||
role="assistant",
|
||||
)
|
||||
self.memory.add(msg)
|
||||
time.sleep(1)
|
||||
return msg
|
||||
|
||||
|
||||
class DemoRpcAgentWithMonitor(AgentBase):
|
||||
"""A demo Rpc agent that use monitor"""
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
monitor = MonitorFactory.get_monitor()
|
||||
try:
|
||||
monitor.update({"msg_num": 1})
|
||||
except QuotaExceededError:
|
||||
x.content["quota_exceeded"] = True
|
||||
logger.chat(
|
||||
{
|
||||
"name": self.name,
|
||||
"content": "quota_exceeded",
|
||||
},
|
||||
)
|
||||
return x
|
||||
x.content["msg_num"] = monitor.get_value("msg_num")
|
||||
logger.chat(
|
||||
{
|
||||
"name": self.name,
|
||||
"content": f"msg_num {x.content['msg_num']}",
|
||||
},
|
||||
)
|
||||
time.sleep(0.2)
|
||||
return x
|
||||
|
||||
|
||||
class DemoGeneratorAgent(AgentBase):
|
||||
"""A demo agent to generate a number"""
|
||||
|
||||
def __init__(self, name: str, value: int) -> None:
|
||||
super().__init__(name)
|
||||
self.value = value
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
time.sleep(1)
|
||||
return Msg(
|
||||
name=self.name,
|
||||
role="assistant",
|
||||
content={
|
||||
"value": self.value,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class DemoGatherAgent(AgentBase):
|
||||
"""A demo agent to gather value"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
agents: list[DemoGeneratorAgent],
|
||||
to_dist: dict = None,
|
||||
) -> None:
|
||||
super().__init__(name, to_dist=to_dist)
|
||||
self.agents = agents
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
result = []
|
||||
stime = time.time()
|
||||
for agent in self.agents:
|
||||
result.append(agent())
|
||||
value = 0
|
||||
for r in result:
|
||||
value += r.content["value"]
|
||||
etime = time.time()
|
||||
return Msg(
|
||||
name=self.name,
|
||||
role="assistant",
|
||||
content={
|
||||
"value": value,
|
||||
"time": etime - stime,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class DemoErrorAgent(AgentBase):
|
||||
"""A demo Rpc agent that raise Error"""
|
||||
|
||||
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None) -> Msg:
|
||||
raise RuntimeError("Demo Error")
|
||||
|
||||
|
||||
class BasicRpcAgentTest(unittest.TestCase):
|
||||
"Test cases for Rpc Agent"
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for Rpc Agent Test"""
|
||||
agentscope.init(
|
||||
project="test",
|
||||
name="rpc_agent",
|
||||
save_dir="./.unittest_runs",
|
||||
save_log=True,
|
||||
)
|
||||
self.assertTrue(os.path.exists("./.unittest_runs"))
|
||||
|
||||
def tearDown(self) -> None:
|
||||
MonitorFactory._instance = None # pylint: disable=W0212
|
||||
logger.remove()
|
||||
shutil.rmtree("./.unittest_runs")
|
||||
|
||||
def test_single_rpc_agent_server(self) -> None:
|
||||
"""test setup a single rpc agent"""
|
||||
agent_a = DemoRpcAgent(
|
||||
name="a",
|
||||
to_dist=True,
|
||||
)
|
||||
self.assertIsNotNone(agent_a)
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"text": "hello world"},
|
||||
role="system",
|
||||
)
|
||||
result = agent_a(msg)
|
||||
# get name without waiting for the server
|
||||
self.assertEqual(result.name, "a")
|
||||
self.assertEqual(result["name"], "a")
|
||||
js_placeholder_result = result.serialize()
|
||||
self.assertTrue(result._is_placeholder) # pylint: disable=W0212
|
||||
placeholder_result = deserialize(js_placeholder_result)
|
||||
self.assertTrue(isinstance(placeholder_result, PlaceholderMessage))
|
||||
self.assertEqual(placeholder_result.name, "a")
|
||||
self.assertEqual(
|
||||
placeholder_result["name"], # type: ignore[call-overload]
|
||||
"a",
|
||||
)
|
||||
self.assertTrue(
|
||||
placeholder_result._is_placeholder, # pylint: disable=W0212
|
||||
)
|
||||
# wait to get content
|
||||
self.assertEqual(result.content, msg.content)
|
||||
self.assertFalse(result._is_placeholder) # pylint: disable=W0212
|
||||
self.assertEqual(result.id, 0)
|
||||
self.assertTrue(
|
||||
placeholder_result._is_placeholder, # pylint: disable=W0212
|
||||
)
|
||||
self.assertEqual(placeholder_result.content, msg.content)
|
||||
self.assertFalse(
|
||||
placeholder_result._is_placeholder, # pylint: disable=W0212
|
||||
)
|
||||
self.assertEqual(placeholder_result.id, 0)
|
||||
# check msg
|
||||
js_msg_result = result.serialize()
|
||||
msg_result = deserialize(js_msg_result)
|
||||
self.assertTrue(isinstance(msg_result, Msg))
|
||||
self.assertEqual(msg_result.content, msg.content)
|
||||
self.assertEqual(msg_result.id, 0)
|
||||
# check id increase
|
||||
msg = agent_a(msg_result) # type: ignore[arg-type]
|
||||
self.assertEqual(msg.id, 1)
|
||||
|
||||
def test_connect_to_an_existing_rpc_server(self) -> None:
|
||||
"""test connecting to an existing server"""
|
||||
launcher = RpcAgentServerLauncher(
|
||||
# choose port automatically
|
||||
host="127.0.0.1",
|
||||
port=12010,
|
||||
local_mode=False,
|
||||
custom_agents=[DemoRpcAgent],
|
||||
)
|
||||
launcher.launch()
|
||||
agent_a = DemoRpcAgent(
|
||||
name="a",
|
||||
).to_dist(
|
||||
host="127.0.0.1",
|
||||
port=launcher.port,
|
||||
)
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"text": "hello world"},
|
||||
role="system",
|
||||
)
|
||||
result = agent_a(msg)
|
||||
# get name without waiting for the server
|
||||
self.assertEqual(result.name, "a")
|
||||
# waiting for server
|
||||
self.assertEqual(result.content, msg.content)
|
||||
# test dict usage
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"text": "hi world"},
|
||||
role="system",
|
||||
)
|
||||
result = agent_a(msg)
|
||||
# get name without waiting for the server
|
||||
self.assertEqual(result["name"], "a")
|
||||
# waiting for server
|
||||
self.assertEqual(result["content"], msg.content)
|
||||
# test to_str
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"text": "test"},
|
||||
role="system",
|
||||
)
|
||||
result = agent_a(msg)
|
||||
self.assertEqual(result.to_str(), "a: {'text': 'test'}")
|
||||
launcher.shutdown()
|
||||
|
||||
def test_multi_rpc_agent(self) -> None:
|
||||
"""test setup multi rpc agent"""
|
||||
agent_a = DemoRpcAgentAdd(
|
||||
name="a",
|
||||
).to_dist(
|
||||
lazy_launch=False,
|
||||
)
|
||||
agent_b = DemoRpcAgentAdd(
|
||||
name="b",
|
||||
).to_dist(
|
||||
lazy_launch=False,
|
||||
)
|
||||
agent_c = DemoRpcAgentAdd(
|
||||
name="c",
|
||||
).to_dist(
|
||||
lazy_launch=False,
|
||||
)
|
||||
|
||||
# test sequential
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"value": 0},
|
||||
role="system",
|
||||
)
|
||||
start_time = time.time()
|
||||
msg = agent_a(msg)
|
||||
self.assertTrue(isinstance(msg, PlaceholderMessage))
|
||||
msg = agent_b(msg)
|
||||
self.assertTrue(isinstance(msg, PlaceholderMessage))
|
||||
msg = agent_c(msg)
|
||||
self.assertTrue(isinstance(msg, PlaceholderMessage))
|
||||
return_time = time.time()
|
||||
# should return directly
|
||||
self.assertTrue((return_time - start_time) < 1)
|
||||
self.assertEqual(msg.content["value"], 3)
|
||||
end_time = time.time()
|
||||
# need at least 3s to finish
|
||||
self.assertTrue((end_time - start_time) >= 3)
|
||||
|
||||
# test parallel
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"value": -1},
|
||||
role="system",
|
||||
)
|
||||
start_time = time.time()
|
||||
msg_a = agent_a(msg)
|
||||
msg_b = agent_b(msg)
|
||||
msg_c = agent_c(msg)
|
||||
self.assertEqual(msg_a.content["value"], 0)
|
||||
self.assertEqual(msg_b.content["value"], 0)
|
||||
self.assertEqual(msg_c.content["value"], 0)
|
||||
end_time = time.time()
|
||||
# need 1s to finish
|
||||
self.assertTrue((end_time - start_time) < 1.5)
|
||||
|
||||
def test_mix_rpc_agent_and_local_agent(self) -> None:
|
||||
"""test to use local and rpc agent simultaneously"""
|
||||
agent_a = DemoRpcAgentAdd(
|
||||
name="a",
|
||||
).to_dist(
|
||||
lazy_launch=False,
|
||||
)
|
||||
# local agent b
|
||||
agent_b = DemoLocalAgentAdd(
|
||||
name="b",
|
||||
)
|
||||
# rpc agent c
|
||||
agent_c = DemoRpcAgentAdd( # pylint: disable=E1123
|
||||
name="c",
|
||||
to_dist=DistConf(
|
||||
lazy_launch=False,
|
||||
),
|
||||
)
|
||||
msg = Msg(
|
||||
name="System",
|
||||
content={"value": 0},
|
||||
role="system",
|
||||
)
|
||||
|
||||
while msg.content["value"] < 4:
|
||||
msg = agent_a(msg)
|
||||
msg = agent_b(msg)
|
||||
msg = agent_c(msg)
|
||||
self.assertEqual(msg.content["value"], 6)
|
||||
|
||||
def test_msghub_compatibility(self) -> None:
|
||||
"""test compatibility with msghub"""
|
||||
agent_a = DemoRpcAgentWithMemory(
|
||||
name="a",
|
||||
).to_dist()
|
||||
agent_b = DemoRpcAgentWithMemory(
|
||||
name="b",
|
||||
).to_dist()
|
||||
agent_c = DemoRpcAgentWithMemory(
|
||||
name="c",
|
||||
to_dist=True,
|
||||
)
|
||||
participants = [agent_a, agent_b, agent_c]
|
||||
annonuncement_msgs = [
|
||||
Msg(name="System", content="Announcement 1", role="system"),
|
||||
Msg(name="System", content="Announcement 2", role="system"),
|
||||
]
|
||||
with msghub(
|
||||
participants=participants,
|
||||
announcement=annonuncement_msgs,
|
||||
):
|
||||
x_a = agent_a()
|
||||
x_b = agent_b(x_a)
|
||||
x_c = agent_c(x_b)
|
||||
self.assertEqual(x_a.content["mem_size"], 2)
|
||||
self.assertEqual(x_b.content["mem_size"], 3)
|
||||
self.assertEqual(x_c.content["mem_size"], 4)
|
||||
x_a = agent_a(x_c)
|
||||
self.assertEqual(x_a.content["mem_size"], 5)
|
||||
x_b = agent_b(x_a)
|
||||
self.assertEqual(x_b.content["mem_size"], 6)
|
||||
x_c = agent_c(x_b)
|
||||
self.assertEqual(x_c.content["mem_size"], 7)
|
||||
x_c = sequentialpipeline(participants, x_c)
|
||||
self.assertEqual(x_c.content["mem_size"], 10)
|
||||
|
||||
def test_standalone_multiprocess_init(self) -> None:
|
||||
"""test compatibility with agentscope.init"""
|
||||
monitor = MonitorFactory.get_monitor()
|
||||
monitor.register("msg_num", quota=10)
|
||||
# rpc agent a
|
||||
agent_a = DemoRpcAgentWithMonitor(
|
||||
name="a",
|
||||
).to_dist(
|
||||
lazy_launch=False,
|
||||
)
|
||||
# local agent b
|
||||
agent_b = DemoRpcAgentWithMonitor(
|
||||
name="b",
|
||||
).to_dist(
|
||||
lazy_launch=False,
|
||||
)
|
||||
msg = Msg(name="System", content={"msg_num": 0}, role="system")
|
||||
j = 0
|
||||
for _ in range(5):
|
||||
msg = agent_a(msg)
|
||||
self.assertEqual(msg["content"]["msg_num"], j + 1)
|
||||
msg = agent_b(msg)
|
||||
self.assertEqual(msg["content"]["msg_num"], j + 2)
|
||||
j += 2
|
||||
msg = agent_a(msg)
|
||||
logger.chat(msg)
|
||||
self.assertTrue(msg["content"]["quota_exceeded"])
|
||||
msg = agent_b(msg)
|
||||
logger.chat(msg)
|
||||
self.assertTrue(msg["content"]["quota_exceeded"])
|
||||
|
||||
def test_multi_agent_in_same_server(self) -> None:
|
||||
"""test agent server with multi agent"""
|
||||
launcher = RpcAgentServerLauncher(
|
||||
host="127.0.0.1",
|
||||
port=12010,
|
||||
local_mode=False,
|
||||
custom_agents=[DemoRpcAgentWithMemory],
|
||||
)
|
||||
launcher.launch()
|
||||
# although agent1 and agent2 connect to the same server
|
||||
# they are different instances with different memories
|
||||
agent1 = DemoRpcAgentWithMemory(
|
||||
name="a",
|
||||
)
|
||||
oid = agent1.agent_id
|
||||
agent1 = agent1.to_dist(
|
||||
host="127.0.0.1",
|
||||
port=launcher.port,
|
||||
)
|
||||
self.assertEqual(oid, agent1.agent_id)
|
||||
self.assertEqual(oid, agent1.client.agent_id)
|
||||
agent2 = DemoRpcAgentWithMemory( # pylint: disable=E1123
|
||||
name="a",
|
||||
to_dist={
|
||||
"host": "127.0.0.1",
|
||||
"port": launcher.port,
|
||||
},
|
||||
)
|
||||
# agent3 has the same agent id as agent1
|
||||
# so it share the same memory with agent1
|
||||
agent3 = DemoRpcAgentWithMemory(
|
||||
name="a",
|
||||
).to_dist(
|
||||
host="127.0.0.1",
|
||||
port=launcher.port,
|
||||
)
|
||||
agent3._agent_id = agent1.agent_id # pylint: disable=W0212
|
||||
agent3.client.agent_id = agent1.client.agent_id
|
||||
msg1 = Msg(name="System", content="First Msg for agent1")
|
||||
res1 = agent1(msg1)
|
||||
self.assertEqual(res1.content["mem_size"], 1)
|
||||
msg2 = Msg(name="System", content="First Msg for agent2")
|
||||
res2 = agent2(msg2)
|
||||
self.assertEqual(res2.content["mem_size"], 1)
|
||||
msg3 = Msg(name="System", content="First Msg for agent3")
|
||||
res3 = agent3(msg3)
|
||||
self.assertEqual(res3.content["mem_size"], 3)
|
||||
msg4 = Msg(name="System", content="Second Msg for agent2")
|
||||
res4 = agent2(msg4)
|
||||
self.assertEqual(res4.content["mem_size"], 3)
|
||||
# delete existing agent
|
||||
agent2.client.delete_agent()
|
||||
msg2 = Msg(name="System", content="First Msg for agent2")
|
||||
res2 = agent2(msg2)
|
||||
self.assertRaises(ValueError, res2.__getattr__, "content")
|
||||
|
||||
# should override remote default parameter(e.g. name field)
|
||||
agent4 = DemoRpcAgentWithMemory(
|
||||
name="b",
|
||||
).to_dist(
|
||||
host="127.0.0.1",
|
||||
port=launcher.port,
|
||||
)
|
||||
msg5 = Msg(name="System", content="Second Msg for agent4")
|
||||
res5 = agent4(msg5)
|
||||
self.assertEqual(res5.name, "b")
|
||||
self.assertEqual(res5.content["mem_size"], 1)
|
||||
launcher.shutdown()
|
||||
|
||||
def test_clone_instances(self) -> None:
|
||||
"""Test the clone_instances method of RpcAgent"""
|
||||
agent = DemoRpcAgentWithMemory(
|
||||
name="a",
|
||||
).to_dist()
|
||||
# lazy launch will not init client
|
||||
self.assertIsNone(agent.client)
|
||||
# generate two agents (the first is it self)
|
||||
agents = agent.clone_instances(2)
|
||||
self.assertEqual(len(agents), 2)
|
||||
agent1 = agents[0]
|
||||
agent2 = agents[1]
|
||||
self.assertTrue(agent1.agent_id.startswith("DemoRpcAgentWithMemory"))
|
||||
self.assertTrue(agent2.agent_id.startswith("DemoRpcAgentWithMemory"))
|
||||
self.assertTrue(
|
||||
agent1.client.agent_id.startswith("DemoRpcAgentWithMemory"),
|
||||
)
|
||||
self.assertTrue(
|
||||
agent2.client.agent_id.startswith("DemoRpcAgentWithMemory"),
|
||||
)
|
||||
self.assertNotEqual(agent1.agent_id, agent2.agent_id)
|
||||
self.assertEqual(agent1.agent_id, agent1.client.agent_id)
|
||||
self.assertEqual(agent2.agent_id, agent2.client.agent_id)
|
||||
# clone instance will init client
|
||||
self.assertIsNotNone(agent.client)
|
||||
self.assertEqual(agent.agent_id, agent1.agent_id)
|
||||
self.assertNotEqual(agent1.agent_id, agent2.agent_id)
|
||||
self.assertIsNotNone(agent.server_launcher)
|
||||
self.assertIsNotNone(agent1.server_launcher)
|
||||
self.assertIsNone(agent2.server_launcher)
|
||||
msg1 = Msg(name="System", content="First Msg for agent1")
|
||||
res1 = agent1(msg1)
|
||||
self.assertEqual(res1.content["mem_size"], 1)
|
||||
msg2 = Msg(name="System", content="First Msg for agent2")
|
||||
res2 = agent2(msg2)
|
||||
self.assertEqual(res2.content["mem_size"], 1)
|
||||
new_agents = agent.clone_instances(2, including_self=False)
|
||||
agent3 = new_agents[0]
|
||||
agent4 = new_agents[1]
|
||||
self.assertEqual(len(new_agents), 2)
|
||||
self.assertNotEqual(agent3.agent_id, agent.agent_id)
|
||||
self.assertNotEqual(agent4.agent_id, agent.agent_id)
|
||||
self.assertIsNone(agent3.server_launcher)
|
||||
self.assertIsNone(agent4.server_launcher)
|
||||
msg3 = Msg(name="System", content="First Msg for agent3")
|
||||
res3 = agent3(msg3)
|
||||
self.assertEqual(res1.content["mem_size"], 1)
|
||||
msg4 = Msg(name="System", content="First Msg for agent4")
|
||||
res4 = agent4(msg4)
|
||||
self.assertEqual(res3.content["mem_size"], 1)
|
||||
self.assertEqual(res4.content["mem_size"], 1)
|
||||
|
||||
def test_error_handling(self) -> None:
|
||||
"""Test error handling"""
|
||||
agent = DemoErrorAgent(name="a").to_dist()
|
||||
x = agent()
|
||||
self.assertRaises(RuntimeError, x.__getattr__, "content")
|
||||
|
||||
def test_agent_nesting(self) -> None:
|
||||
"""Test agent nesting"""
|
||||
host = "localhost"
|
||||
launcher1 = RpcAgentServerLauncher(
|
||||
# choose port automatically
|
||||
host=host,
|
||||
port=12010,
|
||||
local_mode=False,
|
||||
custom_agents=[DemoGatherAgent, DemoGeneratorAgent],
|
||||
)
|
||||
launcher2 = RpcAgentServerLauncher(
|
||||
# choose port automatically
|
||||
host=host,
|
||||
port=12011,
|
||||
local_mode=False,
|
||||
custom_agents=[DemoGatherAgent, DemoGeneratorAgent],
|
||||
)
|
||||
launcher1.launch()
|
||||
launcher2.launch()
|
||||
agents = []
|
||||
for i in range(8):
|
||||
if i % 2:
|
||||
agents.append(
|
||||
DemoGeneratorAgent(name=f"a_{i}", value=i).to_dist(
|
||||
host=host,
|
||||
port=launcher1.port,
|
||||
),
|
||||
)
|
||||
else:
|
||||
agents.append(
|
||||
DemoGeneratorAgent(name=f"a_{i}", value=i).to_dist(
|
||||
host=host,
|
||||
port=launcher2.port,
|
||||
),
|
||||
)
|
||||
gather1 = DemoGatherAgent( # pylint: disable=E1123
|
||||
name="g1",
|
||||
agents=agents[:4],
|
||||
to_dist=DistConf(
|
||||
host=host,
|
||||
port=launcher1.port,
|
||||
),
|
||||
)
|
||||
gather2 = DemoGatherAgent( # pylint: disable=E1123
|
||||
name="g2",
|
||||
agents=agents[4:],
|
||||
to_dist={
|
||||
"host": host,
|
||||
"port": launcher2.port,
|
||||
},
|
||||
)
|
||||
r1 = gather1()
|
||||
r2 = gather2()
|
||||
self.assertEqual(r1.content["value"], 6)
|
||||
self.assertEqual(r2.content["value"], 22)
|
||||
self.assertTrue(0.5 < r1.content["time"] < 2)
|
||||
self.assertTrue(0.5 < r2.content["time"] < 2)
|
||||
launcher1.shutdown()
|
||||
launcher2.shutdown()
|
||||
69
AlgoriAgent/tests/run.py
Normal file
69
AlgoriAgent/tests/run.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alibaba, Inc. and its affiliates.
|
||||
# This file is licensed under the terms of the Apache 2.0 license. Some
|
||||
# codes of this file is modified from
|
||||
# https://github.com/alibaba/FederatedScope/blob/master/tests/run.py, which
|
||||
# is also licensed under the terms of the Apache 2.0.
|
||||
""" This module provides a test runner for unittest cases."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
file_dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
sys.path.append(file_dir)
|
||||
|
||||
parser = argparse.ArgumentParser("test runner")
|
||||
parser.add_argument("--list_tests", action="store_true", help="list all tests")
|
||||
parser.add_argument("--pattern", default="*_test.py", help="test file pattern")
|
||||
parser.add_argument(
|
||||
"--test_dir",
|
||||
default="tests",
|
||||
help="directory to be tested",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
def gather_test_cases(
|
||||
test_dir: str,
|
||||
pattern: str,
|
||||
list_tests: bool,
|
||||
) -> unittest.TestSuite:
|
||||
"""Gathers all the test cases that match the given pattern and
|
||||
directory."""
|
||||
test_suite = unittest.TestSuite()
|
||||
discover = unittest.defaultTestLoader.discover(
|
||||
test_dir,
|
||||
pattern=pattern,
|
||||
top_level_dir=None,
|
||||
)
|
||||
for suite_discovered in discover:
|
||||
for test_case in suite_discovered:
|
||||
test_suite.addTest(test_case)
|
||||
if hasattr(test_case, "__iter__"):
|
||||
for subcase in test_case:
|
||||
if list_tests:
|
||||
print(subcase)
|
||||
else:
|
||||
if list_tests:
|
||||
print(test_case)
|
||||
return test_suite
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main func for the runner for unittest cases."""
|
||||
runner = unittest.TextTestRunner()
|
||||
test_suite = gather_test_cases(
|
||||
os.path.abspath(args.test_dir),
|
||||
args.pattern,
|
||||
args.list_tests,
|
||||
)
|
||||
if not args.list_tests:
|
||||
res = runner.run(test_suite)
|
||||
if not res.wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
380
AlgoriAgent/tests/service_toolkit_test.py
Normal file
380
AlgoriAgent/tests/service_toolkit_test.py
Normal file
@@ -0,0 +1,380 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Unit test for service toolkit. """
|
||||
import json
|
||||
import unittest
|
||||
from typing import Literal
|
||||
|
||||
from agentscope.models import ModelWrapperBase, ModelResponse
|
||||
from agentscope.parsers import MultiTaggedContentParser, TaggedContent
|
||||
from agentscope.service import (
|
||||
bing_search,
|
||||
execute_python_code,
|
||||
retrieve_from_list,
|
||||
query_mysql,
|
||||
summarization,
|
||||
)
|
||||
from agentscope.service import ServiceToolkit
|
||||
|
||||
|
||||
class ServiceToolkitTest(unittest.TestCase):
|
||||
"""
|
||||
Unit test for service toolkit.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Init for ExampleTest."""
|
||||
self.json_schema_bing_search1 = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "bing_search",
|
||||
"description": (
|
||||
"Search question in Bing Search API and "
|
||||
"return the searching results"
|
||||
),
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"num_results": {
|
||||
"type": "number",
|
||||
"description": (
|
||||
"The number of search " "results to return."
|
||||
),
|
||||
"default": 10,
|
||||
},
|
||||
"question": {
|
||||
"type": "string",
|
||||
"description": "The search query string.",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"question",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.json_schema_bing_search2 = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "bing_search",
|
||||
"description": (
|
||||
"Search question in Bing Search API and "
|
||||
"return the searching results"
|
||||
),
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {
|
||||
"type": "string",
|
||||
"description": "The search query string.",
|
||||
},
|
||||
},
|
||||
"required": ["question"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.json_schema_func = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "func",
|
||||
"description": None,
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {"default": "test"},
|
||||
"d": {
|
||||
"type": "typing.Literal",
|
||||
"enum": [1, "abc", "d"],
|
||||
"default": 1,
|
||||
},
|
||||
"b": {},
|
||||
"a": {"type": "string"},
|
||||
},
|
||||
"required": ["a", "b"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.json_schema_execute_python_code = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "execute_python_code",
|
||||
"description": "Execute a piece of python code.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"description": (
|
||||
"The Python code to be " "executed."
|
||||
),
|
||||
},
|
||||
},
|
||||
"required": ["code"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.json_schema_retrieve_from_list = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "retrieve_from_list",
|
||||
"description": "Retrieve data in a list.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "Any",
|
||||
"description": "A message to be retrieved.",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"query",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.json_schema_query_mysql = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "query_mysql",
|
||||
"description": "Execute query within MySQL database.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "SQL query to execute.",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"query",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.json_schema_summarization = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "summarization",
|
||||
"description": "Summarize the input text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": (
|
||||
"Text to be summarized by " "the model."
|
||||
),
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"text",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def test_bing_search(self) -> None:
|
||||
"""Test bing_search."""
|
||||
# api_key is specified by developer, while question and num_results
|
||||
# are specified by model
|
||||
_, doc_dict = ServiceToolkit.get(bing_search, api_key="xxx")
|
||||
print(json.dumps(doc_dict, indent=4))
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_bing_search1,
|
||||
)
|
||||
|
||||
# Set num_results by developer rather than model
|
||||
_, doc_dict = ServiceToolkit.get(
|
||||
bing_search,
|
||||
num_results=3,
|
||||
api_key="xxx",
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_bing_search2,
|
||||
)
|
||||
|
||||
def test_enum(self) -> None:
|
||||
"""Test enum in service toolkit."""
|
||||
|
||||
def func( # type: ignore
|
||||
a: str,
|
||||
b,
|
||||
c="test",
|
||||
d: Literal[1, "abc", "d"] = 1,
|
||||
) -> None:
|
||||
print(a, b, c, d)
|
||||
|
||||
_, doc_dict = ServiceToolkit.get(func)
|
||||
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_func,
|
||||
)
|
||||
|
||||
def test_exec_python_code(self) -> None:
|
||||
"""Test execute_python_code in service toolkit."""
|
||||
_, doc_dict = ServiceToolkit.get(
|
||||
execute_python_code,
|
||||
timeout=300,
|
||||
use_docker=True,
|
||||
maximum_memory_bytes=None,
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_execute_python_code,
|
||||
)
|
||||
|
||||
def test_retrieval(self) -> None:
|
||||
"""Test retrieval in service toolkit."""
|
||||
_, doc_dict = ServiceToolkit.get(
|
||||
retrieve_from_list,
|
||||
knowledge=[1, 2, 3],
|
||||
score_func=lambda x, y: 1.0,
|
||||
top_k=10,
|
||||
embedding_model=10,
|
||||
preserve_order=True,
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_retrieve_from_list,
|
||||
)
|
||||
|
||||
def test_sql_query(self) -> None:
|
||||
"""Test sql_query in service toolkit."""
|
||||
_, doc_dict = ServiceToolkit.get(
|
||||
query_mysql,
|
||||
database="test",
|
||||
host="localhost",
|
||||
user="root",
|
||||
password="xxx",
|
||||
port=3306,
|
||||
allow_change_data=False,
|
||||
maxcount_results=None,
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_query_mysql,
|
||||
)
|
||||
|
||||
def test_summary(self) -> None:
|
||||
"""Test summarization in service toolkit."""
|
||||
_, doc_dict = ServiceToolkit.get(
|
||||
summarization,
|
||||
model=ModelWrapperBase("abc"),
|
||||
system_prompt="",
|
||||
summarization_prompt="",
|
||||
max_return_token=-1,
|
||||
token_limit_prompt="",
|
||||
)
|
||||
|
||||
print(json.dumps(doc_dict, indent=4))
|
||||
|
||||
self.assertDictEqual(
|
||||
doc_dict,
|
||||
self.json_schema_summarization,
|
||||
)
|
||||
|
||||
def test_service_toolkit(self) -> None:
|
||||
"""Test the object of ServiceToolkit."""
|
||||
service_toolkit = ServiceToolkit()
|
||||
|
||||
service_toolkit.add(bing_search, api_key="xxx", num_results=3)
|
||||
service_toolkit.add(
|
||||
execute_python_code,
|
||||
timeout=300,
|
||||
use_docker=True,
|
||||
maximum_memory_bytes=None,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
service_toolkit.tools_instruction,
|
||||
"""## Tool Functions:
|
||||
The following tool functions are available in the format of
|
||||
```
|
||||
{index}. {function name}: {function description}
|
||||
{argument1 name} ({argument type}): {argument description}
|
||||
{argument2 name} ({argument type}): {argument description}
|
||||
...
|
||||
```
|
||||
|
||||
1. bing_search: Search question in Bing Search API and return the searching results
|
||||
question (string): The search query string.
|
||||
2. execute_python_code: Execute a piece of python code.
|
||||
code (string): The Python code to be executed.
|
||||
""", # noqa
|
||||
)
|
||||
self.assertDictEqual(
|
||||
service_toolkit.json_schemas,
|
||||
{
|
||||
"bing_search": self.json_schema_bing_search2,
|
||||
"execute_python_code": self.json_schema_execute_python_code,
|
||||
},
|
||||
)
|
||||
|
||||
def test_multi_tagged_content(self) -> None:
|
||||
"""Test multi tagged content"""
|
||||
|
||||
parser = MultiTaggedContentParser(
|
||||
TaggedContent(
|
||||
"thought",
|
||||
"[THOUGHT]",
|
||||
"what you think",
|
||||
"[/THOUGHT]",
|
||||
),
|
||||
TaggedContent("speak", "[SPEAK]", "what you speak", "[/SPEAK]"),
|
||||
TaggedContent(
|
||||
"function",
|
||||
"[FUNCTION]",
|
||||
'{"function name": "xxx", "args": {"arg name": "xxx"}}',
|
||||
"[/FUNCTION]",
|
||||
parse_json=True,
|
||||
),
|
||||
)
|
||||
|
||||
target_format_instruction = (
|
||||
"Respond with specific tags as outlined below, and the content "
|
||||
"between [FUNCTION] and [/FUNCTION] MUST be a JSON object:\n"
|
||||
"[THOUGHT]what you think[/THOUGHT]\n"
|
||||
"[SPEAK]what you speak[/SPEAK]\n"
|
||||
'[FUNCTION]{"function name": "xxx", "args": {"arg name": "xxx"}}'
|
||||
"[/FUNCTION]"
|
||||
)
|
||||
|
||||
self.assertEqual(parser.format_instruction, target_format_instruction)
|
||||
|
||||
response = ModelResponse(
|
||||
text=(
|
||||
"This is a test\n[THOUGHT]I think this is a good idea"
|
||||
"[/THOUGHT]\n[SPEAK]I am speaking now[/SPEAK]\n[FUNCTION]"
|
||||
'{"function name": "bing_search", "args": {"query": "news of '
|
||||
'today"}}[/FUNCTION]'
|
||||
),
|
||||
)
|
||||
res = parser.parse(response)
|
||||
|
||||
self.assertDictEqual(
|
||||
res.parsed,
|
||||
{
|
||||
"thought": "I think this is a good idea",
|
||||
"speak": "I am speaking now",
|
||||
"function": {
|
||||
"function name": "bing_search",
|
||||
"args": {"query": "news of today"},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
175
AlgoriAgent/tests/sql_query_test.py
Normal file
175
AlgoriAgent/tests/sql_query_test.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Python sql query test."""
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from agentscope.service.sql_query.mongodb import query_mongodb
|
||||
|
||||
from agentscope.service.sql_query.mysql import query_mysql
|
||||
from agentscope.service.service_response import ServiceExecStatus
|
||||
from agentscope.service.sql_query.sqlite import query_sqlite
|
||||
|
||||
|
||||
class TestSQLQueries(unittest.TestCase):
|
||||
"""ExampleTest for a unit test."""
|
||||
|
||||
@patch("agentscope.service.sql_query.mysql.pymysql.connect")
|
||||
def test_query_mysql_success(self, mock_connect: MagicMock) -> None:
|
||||
"""Test query mysql success"""
|
||||
# Set up a mock connection and cursor
|
||||
mock_cursor = mock_connect.return_value.cursor.return_value
|
||||
mock_cursor.fetchall.return_value = [("data1",), ("data2",)]
|
||||
|
||||
# Call the query_mysql function
|
||||
response = query_mysql(
|
||||
database="test_mysql_db",
|
||||
query="SELECT * FROM my_table",
|
||||
host="localhost",
|
||||
user="user",
|
||||
password="pass",
|
||||
port=3306,
|
||||
allow_change_data=False,
|
||||
maxcount_results=10,
|
||||
)
|
||||
|
||||
# Assert that the query_mysql function returned the expected results
|
||||
self.assertEqual(response.status, ServiceExecStatus.SUCCESS)
|
||||
self.assertEqual(response.content, [("data1",), ("data2",)])
|
||||
|
||||
# Ensure that the SQL query was executed correctly
|
||||
mock_cursor.execute.assert_called_once_with(
|
||||
"SELECT * FROM my_table LIMIT 10",
|
||||
)
|
||||
# Ensure that all results were fetched
|
||||
mock_cursor.fetchall.assert_called_once()
|
||||
|
||||
@patch("agentscope.service.sql_query.mysql.pymysql.connect")
|
||||
def test_query_mysql_failure(self, mock_connect: MagicMock) -> None:
|
||||
"""Test query mysql failure"""
|
||||
# Set up pymysql.connect to raise an exception
|
||||
mock_connect.side_effect = Exception("Connection Error")
|
||||
|
||||
# Call the query_mysql function
|
||||
response = query_mysql(
|
||||
database="test_mysql_db",
|
||||
query="SELECT * FROM my_table",
|
||||
host="localhost",
|
||||
user="user",
|
||||
password="pass",
|
||||
port=3306,
|
||||
)
|
||||
|
||||
# Assert that the query_mysql function handled the exception
|
||||
self.assertEqual(response.status, ServiceExecStatus.ERROR)
|
||||
self.assertIn("Connection Error", str(response.content))
|
||||
|
||||
# Test for mongodb
|
||||
@patch("agentscope.service.sql_query.mongodb.pymongo.MongoClient")
|
||||
def test_query_mongodb_success(self, mock_mongo_client: MagicMock) -> None:
|
||||
"""Test query mongodb success"""
|
||||
# Set up a mock connection
|
||||
mock_collection = MagicMock()
|
||||
mock_collection.find.return_value.limit.return_value = [
|
||||
{"_id": 1, "data": "test"},
|
||||
]
|
||||
mock = MagicMock()
|
||||
mock_db = MagicMock()
|
||||
mock.__getitem__.return_value = mock_db
|
||||
mock_db.__getitem__.return_value = mock_collection
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_mongo_client.return_value.__enter__.return_value = mock_client
|
||||
mock_client.__getitem__.return_value = mock_db
|
||||
|
||||
# Call the query_mongodb function
|
||||
response = query_mongodb(
|
||||
database="db_name",
|
||||
collection="collection_name",
|
||||
query={"data": "test"},
|
||||
host="localhost",
|
||||
port=27017,
|
||||
maxcount_results=10,
|
||||
)
|
||||
|
||||
# Ensure that the query_mongodb returned the expected results
|
||||
self.assertEqual(response.status, ServiceExecStatus.SUCCESS)
|
||||
self.assertEqual(response.content, [{"_id": 1, "data": "test"}])
|
||||
|
||||
# Ensure that the query was executed correctly
|
||||
mock_db.__getitem__.assert_called_with("collection_name")
|
||||
mock_collection.find.assert_called_with({"data": "test"})
|
||||
mock_collection.find.return_value.limit.assert_called_with(10)
|
||||
|
||||
@patch("agentscope.service.sql_query.mongodb.pymongo.MongoClient")
|
||||
def test_query_mongodb_failure(self, mock_mongo_client: MagicMock) -> None:
|
||||
"""Test query mongodb failure"""
|
||||
# Configure the mock to raise a connection error
|
||||
mock_mongo_client.side_effect = Exception("Connection Error")
|
||||
|
||||
# Call the query_mongodb function
|
||||
response = query_mongodb(
|
||||
database="test_db",
|
||||
collection="test_collection",
|
||||
query={"name": "Test Data"},
|
||||
host="localhost",
|
||||
port=27017,
|
||||
)
|
||||
|
||||
# Assert that the function handled the exception
|
||||
self.assertEqual(response.status, ServiceExecStatus.ERROR)
|
||||
self.assertIn("Connection Error", str(response.content))
|
||||
|
||||
# Test for sqlite
|
||||
@patch("agentscope.service.sql_query.sqlite.sqlite3.connect")
|
||||
def test_query_sqlite_success(self, mock_connect: MagicMock) -> None:
|
||||
"""Test successful SELECT query without data modification"""
|
||||
# Mock sqlite connection and cursor
|
||||
mock_cursor = MagicMock()
|
||||
mock_cursor.fetchall.return_value = [("data1",), ("data2",)]
|
||||
|
||||
mock_connection = MagicMock()
|
||||
mock_connection.cursor.return_value = mock_cursor
|
||||
|
||||
mock_connect.return_value = mock_connection
|
||||
|
||||
# Execute the query_sqlite function
|
||||
response = query_sqlite(
|
||||
database="test_db.sqlite",
|
||||
query="SELECT * FROM my_table",
|
||||
allow_change_data=False,
|
||||
maxcount_results=10,
|
||||
)
|
||||
|
||||
# Assert the query_sqlite function returned expected results
|
||||
self.assertEqual(response.status, ServiceExecStatus.SUCCESS)
|
||||
self.assertEqual(response.content, [("data1",), ("data2",)])
|
||||
|
||||
# Verify the correct SQL query was executed
|
||||
mock_cursor.execute.assert_called_with(
|
||||
"SELECT * FROM my_table LIMIT 10",
|
||||
)
|
||||
# Ensure all results were fetched
|
||||
mock_cursor.fetchall.assert_called_once()
|
||||
|
||||
@patch("agentscope.service.sql_query.sqlite.sqlite3.connect")
|
||||
def test_query_sqlite_exception_handling(
|
||||
self,
|
||||
mock_connect: MagicMock,
|
||||
) -> None:
|
||||
"""Test exception handling within the query_sqlite function"""
|
||||
# Configure the mock to raise an exception during connection
|
||||
mock_connect.side_effect = Exception("Connection Error")
|
||||
|
||||
# Execute the query_sqlite function
|
||||
response = query_sqlite(
|
||||
database="test_db.sqlite",
|
||||
query="SELECT * FROM my_table",
|
||||
)
|
||||
|
||||
# Assert that the function handled the exception
|
||||
self.assertEqual(response.status, ServiceExecStatus.ERROR)
|
||||
self.assertIn("Connection Error", str(response.content))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
52
AlgoriAgent/tests/token_utils_test.py
Normal file
52
AlgoriAgent/tests/token_utils_test.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Unit test for token_utils."""
|
||||
import unittest
|
||||
from agentscope.utils.token_utils import get_openai_max_length
|
||||
from agentscope.utils.token_utils import count_openai_token
|
||||
|
||||
|
||||
class TokenUtilsTest(unittest.TestCase):
|
||||
"""Token utils test."""
|
||||
|
||||
def test_get_openai_max_length(self) -> None:
|
||||
"""Test the function get_openai_max_length."""
|
||||
self.assertEqual(get_openai_max_length("gpt-4"), 8192)
|
||||
self.assertEqual(get_openai_max_length("gpt-3.5-turbo"), 4096)
|
||||
with self.assertRaises(KeyError):
|
||||
get_openai_max_length("non-existing-model")
|
||||
|
||||
def test_count_openai_token(self) -> None:
|
||||
"""Test the function count_openai_token."""
|
||||
test_model_1 = "text-davinci-003"
|
||||
test_content_str = "This is a test string."
|
||||
self.assertEqual(
|
||||
count_openai_token(test_content_str, test_model_1),
|
||||
6,
|
||||
)
|
||||
|
||||
test_content_list = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "This is a system content.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"name": "example_user",
|
||||
"content": "This is a user content.",
|
||||
},
|
||||
]
|
||||
|
||||
test_model_2 = "gpt-4"
|
||||
self.assertEqual(
|
||||
count_openai_token(test_content_list, test_model_2),
|
||||
26,
|
||||
)
|
||||
|
||||
# Test unsupported model
|
||||
unsupported_model = "unsupported-model"
|
||||
with self.assertRaises(NotImplementedError):
|
||||
count_openai_token(test_content_str, unsupported_model)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
106
AlgoriAgent/tests/web_digest_test.py
Normal file
106
AlgoriAgent/tests/web_digest_test.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Python web digest test."""
|
||||
|
||||
import unittest
|
||||
from typing import Union, Sequence, List
|
||||
from unittest.mock import patch, MagicMock, Mock
|
||||
|
||||
from agentscope.service import ServiceResponse
|
||||
from agentscope.service import load_web, digest_webpage
|
||||
from agentscope.service.service_status import ServiceExecStatus
|
||||
from agentscope.models import ModelWrapperBase, ModelResponse
|
||||
from agentscope.message import Msg
|
||||
|
||||
|
||||
class TestWebDigest(unittest.TestCase):
|
||||
"""Tests for web loading and digesting."""
|
||||
|
||||
@patch("requests.get")
|
||||
def test_web_load(self, mock_get: MagicMock) -> None:
|
||||
"""test web_load function loading html"""
|
||||
# Set up the mock response
|
||||
mock_response = Mock()
|
||||
mock_return_text = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Hello World!</h1>
|
||||
<div>
|
||||
Testing!
|
||||
<p>
|
||||
Some intro text about Foo. <a href="xxx">Examples</a>
|
||||
<ol>
|
||||
<li>Test list.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<ol>
|
||||
<li>Test list again.</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.SUCCESS,
|
||||
content={
|
||||
"raw": bytes(mock_return_text, "utf-8"),
|
||||
"html_to_text": "Hello World! Testing! Some intro text "
|
||||
"about Foo. Test list.Test list again.",
|
||||
},
|
||||
)
|
||||
|
||||
mock_response.text = mock_return_text
|
||||
mock_response.content = bytes(mock_return_text, "utf-8")
|
||||
mock_response.status_code = 200
|
||||
mock_response.headers = {"Content-Type": "text/html"}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# set parameters
|
||||
fake_url = "fake-url"
|
||||
|
||||
results = load_web(
|
||||
url=fake_url,
|
||||
keep_raw=True,
|
||||
html_selected_tags=["p", "div", "h1", "li"],
|
||||
)
|
||||
self.assertEqual(
|
||||
results,
|
||||
expected_result,
|
||||
)
|
||||
|
||||
def test_web_digest(self) -> None:
|
||||
"""test web_digest function"""
|
||||
|
||||
# test the case with dummy model
|
||||
class DummyModel(ModelWrapperBase):
|
||||
"""Dummy model for testing"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.max_length = 1000
|
||||
|
||||
def __call__(self, messages: list[Msg]) -> ModelResponse:
|
||||
return ModelResponse(text="model return")
|
||||
|
||||
def format(
|
||||
self,
|
||||
*args: Union[Msg, Sequence[Msg]],
|
||||
) -> Union[List[dict], str]:
|
||||
return str(args)
|
||||
|
||||
dummy_model = DummyModel()
|
||||
response = digest_webpage("testing", dummy_model)
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.SUCCESS,
|
||||
content="model return",
|
||||
)
|
||||
self.assertEqual(
|
||||
response,
|
||||
expected_result,
|
||||
)
|
||||
|
||||
|
||||
# This allows the tests to be run from the command line
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
157
AlgoriAgent/tests/web_search_test.py
Normal file
157
AlgoriAgent/tests/web_search_test.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Python web search test."""
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
|
||||
from agentscope.service import ServiceResponse, arxiv_search
|
||||
from agentscope.service import bing_search, google_search
|
||||
from agentscope.service.service_status import ServiceExecStatus
|
||||
from agentscope.service.web.arxiv import _reformat_query
|
||||
|
||||
|
||||
class TestWebSearches(unittest.TestCase):
|
||||
"""ExampleTest for a unit test."""
|
||||
|
||||
@patch("agentscope.utils.common.requests.get")
|
||||
def test_search_bing(self, mock_get: MagicMock) -> None:
|
||||
"""test bing search"""
|
||||
# Set up the mock response
|
||||
mock_response = Mock()
|
||||
mock_dict = {
|
||||
"webPages": {
|
||||
"value": [
|
||||
{
|
||||
"name": "Test name from Bing",
|
||||
"url": "Test url from Bing",
|
||||
"snippet": "Test snippet from Bing",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.SUCCESS,
|
||||
content=[
|
||||
{
|
||||
"title": "Test name from Bing",
|
||||
"link": "Test url from Bing",
|
||||
"snippet": "Test snippet from Bing",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
mock_response.json.return_value = mock_dict
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# set parameters
|
||||
bing_api_key = "fake-bing-api-key"
|
||||
test_question = "test test_question"
|
||||
num_results = 1
|
||||
params = {"q": test_question, "count": num_results}
|
||||
headers = {"Ocp-Apim-Subscription-Key": bing_api_key}
|
||||
|
||||
# Call the function
|
||||
results = bing_search(
|
||||
test_question,
|
||||
api_key=bing_api_key,
|
||||
num_results=num_results,
|
||||
)
|
||||
|
||||
# Assertions
|
||||
mock_get.assert_called_once_with(
|
||||
"https://api.bing.microsoft.com/v7.0/search",
|
||||
params=params,
|
||||
headers=headers,
|
||||
)
|
||||
self.assertEqual(
|
||||
results,
|
||||
expected_result,
|
||||
)
|
||||
|
||||
@patch("agentscope.utils.common.requests.get")
|
||||
def test_search_google(self, mock_get: MagicMock) -> None:
|
||||
"""test google search"""
|
||||
# Set up the mock response
|
||||
mock_response = Mock()
|
||||
mock_dict = {
|
||||
"items": [
|
||||
{
|
||||
"title": "Test title from Google",
|
||||
"link": "Test link from Google",
|
||||
"snippet": "Test snippet from Google",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
expected_result = ServiceResponse(
|
||||
status=ServiceExecStatus.SUCCESS,
|
||||
content=[
|
||||
{
|
||||
"title": "Test title from Google",
|
||||
"link": "Test link from Google",
|
||||
"snippet": "Test snippet from Google",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
mock_response.json.return_value = mock_dict
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# set parameter
|
||||
test_question = "test test_question"
|
||||
google_api_key = "fake-google-api-key"
|
||||
google_cse_id = "fake-google-cse-id"
|
||||
num_results = 1
|
||||
|
||||
params = {
|
||||
"q": test_question,
|
||||
"key": google_api_key,
|
||||
"cx": google_cse_id,
|
||||
"num": num_results,
|
||||
}
|
||||
|
||||
# Call the function
|
||||
results = google_search(
|
||||
test_question,
|
||||
api_key=google_api_key,
|
||||
cse_id=google_cse_id,
|
||||
num_results=num_results,
|
||||
)
|
||||
|
||||
# Assertions
|
||||
mock_get.assert_called_once_with(
|
||||
"https://www.googleapis.com/customsearch/v1",
|
||||
params=params,
|
||||
)
|
||||
self.assertEqual(results, expected_result)
|
||||
|
||||
def test_arxiv_search(self) -> None:
|
||||
"""test arxiv search"""
|
||||
res = arxiv_search(
|
||||
search_query="ti:Agentscope",
|
||||
id_list=["2402.14034"],
|
||||
max_results=1,
|
||||
)
|
||||
self.assertEqual(
|
||||
res.content["entries"][0]["title"],
|
||||
"AgentScope: A Flexible yet Robust Multi-Agent Platform",
|
||||
)
|
||||
|
||||
def test_arxiv_query_format(self) -> None:
|
||||
"""Test arxiv query format."""
|
||||
res = _reformat_query(
|
||||
'ti: "Deep Learning" ANDau:LeCun OR (ti: machine learning '
|
||||
"ANDNOT au:John Doe)",
|
||||
)
|
||||
|
||||
ground_truth = (
|
||||
"ti:%22Deep+Learning%22+AND+au:%22LeCun%22+OR+%28ti:"
|
||||
"%22machine+learning%22+ANDNOT+au:%22John+Doe%22%29"
|
||||
)
|
||||
|
||||
self.assertEqual(ground_truth, res)
|
||||
|
||||
|
||||
# This allows the tests to be run from the command line
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
116
AlgoriAgent/tests/zhipu_test.py
Normal file
116
AlgoriAgent/tests/zhipu_test.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""zhipuai test"""
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
import agentscope
|
||||
from agentscope.models import load_model_by_config_name
|
||||
|
||||
|
||||
class TestZhipuAIChatWrapper(unittest.TestCase):
|
||||
"""Test ZhipuAI Chat Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.api_key = "test_api_key.secret_key"
|
||||
self.messages = [
|
||||
{"role": "user", "content": "Hello, ZhipuAI!"},
|
||||
{"role": "assistant", "content": "How can I assist you?"},
|
||||
]
|
||||
|
||||
@patch("agentscope.models.zhipu_model.zhipuai")
|
||||
def test_chat(self, mock_zhipuai: MagicMock) -> None:
|
||||
"""
|
||||
Test chat"""
|
||||
mock_response = MagicMock()
|
||||
mock_response.model_dump.return_value = {
|
||||
"choices": [
|
||||
{"message": {"content": "Hello, this is a mocked response!"}},
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 100,
|
||||
"completion_tokens": 5,
|
||||
"total_tokens": 105,
|
||||
},
|
||||
}
|
||||
mock_response.choices[
|
||||
0
|
||||
].message.content = "Hello, this is a mocked response!"
|
||||
mock_zhipuai_client = MagicMock()
|
||||
mock_zhipuai.ZhipuAI.return_value = mock_zhipuai_client
|
||||
|
||||
mock_zhipuai_client.chat.completions.create.return_value = (
|
||||
mock_response
|
||||
)
|
||||
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "test_config",
|
||||
"model_type": "zhipuai_chat",
|
||||
"model_name": "glm-4",
|
||||
"api_key": self.api_key,
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("test_config")
|
||||
|
||||
response = model(messages=self.messages)
|
||||
|
||||
self.assertEqual(response.text, "Hello, this is a mocked response!")
|
||||
|
||||
mock_zhipuai_client.chat.completions.create.assert_called_once()
|
||||
|
||||
|
||||
class TestZhipuAIEmbeddingWrapper(unittest.TestCase):
|
||||
"""Test ZhipuAI Embedding Wrapper"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.api_key = "test_api_key"
|
||||
self.model_name = "embedding-2"
|
||||
self.text_to_embed = "This is a test sentence for embedding."
|
||||
|
||||
@patch("agentscope.models.zhipu_model.zhipuai")
|
||||
def test_embedding(self, mock_zhipuai: MagicMock) -> None:
|
||||
"""Test embedding API"""
|
||||
mock_embedding_response = MagicMock()
|
||||
mock_embedding_response.model_dump.return_value = {
|
||||
"data": [
|
||||
{"embedding": [0.1, 0.2, 0.3]},
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 2,
|
||||
"total_tokens": 12,
|
||||
},
|
||||
}
|
||||
|
||||
mock_zhipuai_client = MagicMock()
|
||||
mock_zhipuai.ZhipuAI.return_value = mock_zhipuai_client
|
||||
mock_zhipuai_client.embeddings.create.return_value = (
|
||||
mock_embedding_response
|
||||
)
|
||||
|
||||
agentscope.init(
|
||||
model_configs={
|
||||
"config_name": "test_embedding",
|
||||
"model_type": "zhipuai_embedding",
|
||||
"model_name": self.model_name,
|
||||
"api_key": self.api_key,
|
||||
},
|
||||
)
|
||||
|
||||
model = load_model_by_config_name("test_embedding")
|
||||
|
||||
response = model(self.text_to_embed)
|
||||
|
||||
expected_embedding = [[0.1, 0.2, 0.3]]
|
||||
self.assertEqual(response.embedding, expected_embedding)
|
||||
|
||||
mock_zhipuai_client.embeddings.create.assert_called_once_with(
|
||||
input=self.text_to_embed,
|
||||
model=self.model_name,
|
||||
**{},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user