You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_component_factory.py 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import pytest
  2. from typing import List
  3. from autogen_agentchat.agents import AssistantAgent
  4. from autogen_agentchat.teams import RoundRobinGroupChat, SelectorGroupChat
  5. from autogen_agentchat.task import MaxMessageTermination, StopMessageTermination, TextMentionTermination
  6. from autogen_core.components.tools import FunctionTool
  7. from autogenstudio.datamodel.types import (
  8. AgentConfig, ModelConfig, TeamConfig, ToolConfig, TerminationConfig,
  9. ModelTypes, AgentTypes, TeamTypes, TerminationTypes, ToolTypes,
  10. ComponentTypes
  11. )
  12. from autogenstudio.database import ComponentFactory
  13. @pytest.fixture
  14. def component_factory():
  15. return ComponentFactory()
  16. @pytest.fixture
  17. def sample_tool_config():
  18. return ToolConfig(
  19. name="calculator",
  20. description="A simple calculator function",
  21. content="""
  22. def calculator(a: int, b: int, operation: str = '+') -> int:
  23. '''
  24. A simple calculator that performs basic operations
  25. '''
  26. if operation == '+':
  27. return a + b
  28. elif operation == '-':
  29. return a - b
  30. elif operation == '*':
  31. return a * b
  32. elif operation == '/':
  33. return a / b
  34. else:
  35. raise ValueError("Invalid operation")
  36. """,
  37. tool_type=ToolTypes.PYTHON_FUNCTION,
  38. component_type=ComponentTypes.TOOL,
  39. version="1.0.0"
  40. )
  41. @pytest.fixture
  42. def sample_model_config():
  43. return ModelConfig(
  44. model_type=ModelTypes.OPENAI,
  45. model="gpt-4",
  46. api_key="test-key",
  47. component_type=ComponentTypes.MODEL,
  48. version="1.0.0"
  49. )
  50. @pytest.fixture
  51. def sample_agent_config(sample_model_config: ModelConfig, sample_tool_config: ToolConfig):
  52. return AgentConfig(
  53. name="test_agent",
  54. agent_type=AgentTypes.ASSISTANT,
  55. system_message="You are a helpful assistant",
  56. model_client=sample_model_config,
  57. tools=[sample_tool_config],
  58. component_type=ComponentTypes.AGENT,
  59. version="1.0.0"
  60. )
  61. @pytest.fixture
  62. def sample_termination_config():
  63. return TerminationConfig(
  64. termination_type=TerminationTypes.MAX_MESSAGES,
  65. max_messages=10,
  66. component_type=ComponentTypes.TERMINATION,
  67. version="1.0.0"
  68. )
  69. @pytest.fixture
  70. def sample_team_config(sample_agent_config: AgentConfig, sample_termination_config: TerminationConfig, sample_model_config: ModelConfig):
  71. return TeamConfig(
  72. name="test_team",
  73. team_type=TeamTypes.ROUND_ROBIN,
  74. participants=[sample_agent_config],
  75. termination_condition=sample_termination_config,
  76. model_client=sample_model_config,
  77. component_type=ComponentTypes.TEAM,
  78. version="1.0.0"
  79. )
  80. @pytest.mark.asyncio
  81. async def test_load_tool(component_factory: ComponentFactory, sample_tool_config: ToolConfig):
  82. # Test loading tool from ToolConfig
  83. tool = await component_factory.load_tool(sample_tool_config)
  84. assert isinstance(tool, FunctionTool)
  85. assert tool.name == "calculator"
  86. assert tool.description == "A simple calculator function"
  87. # Test tool functionality
  88. result = tool._func(5, 3, '+')
  89. assert result == 8
  90. @pytest.mark.asyncio
  91. async def test_load_tool_invalid_config(component_factory: ComponentFactory):
  92. # Test with missing required fields
  93. with pytest.raises(ValueError):
  94. await component_factory.load_tool(ToolConfig(
  95. name="test",
  96. description="",
  97. content="",
  98. tool_type=ToolTypes.PYTHON_FUNCTION,
  99. component_type=ComponentTypes.TOOL,
  100. version="1.0.0"
  101. ))
  102. # Test with invalid Python code
  103. invalid_config = ToolConfig(
  104. name="invalid",
  105. description="Invalid function",
  106. content="def invalid_func(): return invalid syntax",
  107. tool_type=ToolTypes.PYTHON_FUNCTION,
  108. component_type=ComponentTypes.TOOL,
  109. version="1.0.0"
  110. )
  111. with pytest.raises(ValueError):
  112. await component_factory.load_tool(invalid_config)
  113. @pytest.mark.asyncio
  114. async def test_load_model(component_factory: ComponentFactory, sample_model_config: ModelConfig):
  115. # Test loading model from ModelConfig
  116. model = await component_factory.load_model(sample_model_config)
  117. assert model is not None
  118. @pytest.mark.asyncio
  119. async def test_load_agent(component_factory: ComponentFactory, sample_agent_config: AgentConfig):
  120. # Test loading agent from AgentConfig
  121. agent = await component_factory.load_agent(sample_agent_config)
  122. assert isinstance(agent, AssistantAgent)
  123. assert agent.name == "test_agent"
  124. assert len(agent._tools) == 1
  125. @pytest.mark.asyncio
  126. async def test_load_termination(component_factory: ComponentFactory):
  127. # Test MaxMessageTermination
  128. max_msg_config = TerminationConfig(
  129. termination_type=TerminationTypes.MAX_MESSAGES,
  130. max_messages=5,
  131. component_type=ComponentTypes.TERMINATION,
  132. version="1.0.0"
  133. )
  134. termination = await component_factory.load_termination(max_msg_config)
  135. assert isinstance(termination, MaxMessageTermination)
  136. assert termination._max_messages == 5
  137. # Test StopMessageTermination
  138. stop_msg_config = TerminationConfig(
  139. termination_type=TerminationTypes.STOP_MESSAGE,
  140. component_type=ComponentTypes.TERMINATION,
  141. version="1.0.0"
  142. )
  143. termination = await component_factory.load_termination(stop_msg_config)
  144. assert isinstance(termination, StopMessageTermination)
  145. # Test TextMentionTermination
  146. text_mention_config = TerminationConfig(
  147. termination_type=TerminationTypes.TEXT_MENTION,
  148. text="DONE",
  149. component_type=ComponentTypes.TERMINATION,
  150. version="1.0.0"
  151. )
  152. termination = await component_factory.load_termination(text_mention_config)
  153. assert isinstance(termination, TextMentionTermination)
  154. assert termination._text == "DONE"
  155. # Test AND combination
  156. and_combo_config = TerminationConfig(
  157. termination_type=TerminationTypes.COMBINATION,
  158. operator="and",
  159. conditions=[
  160. TerminationConfig(
  161. termination_type=TerminationTypes.MAX_MESSAGES,
  162. max_messages=5,
  163. component_type=ComponentTypes.TERMINATION,
  164. version="1.0.0"
  165. ),
  166. TerminationConfig(
  167. termination_type=TerminationTypes.TEXT_MENTION,
  168. text="DONE",
  169. component_type=ComponentTypes.TERMINATION,
  170. version="1.0.0"
  171. )
  172. ],
  173. component_type=ComponentTypes.TERMINATION,
  174. version="1.0.0"
  175. )
  176. termination = await component_factory.load_termination(and_combo_config)
  177. assert termination is not None
  178. # Test OR combination
  179. or_combo_config = TerminationConfig(
  180. termination_type=TerminationTypes.COMBINATION,
  181. operator="or",
  182. conditions=[
  183. TerminationConfig(
  184. termination_type=TerminationTypes.MAX_MESSAGES,
  185. max_messages=5,
  186. component_type=ComponentTypes.TERMINATION,
  187. version="1.0.0"
  188. ),
  189. TerminationConfig(
  190. termination_type=TerminationTypes.TEXT_MENTION,
  191. text="DONE",
  192. component_type=ComponentTypes.TERMINATION,
  193. version="1.0.0"
  194. )
  195. ],
  196. component_type=ComponentTypes.TERMINATION,
  197. version="1.0.0"
  198. )
  199. termination = await component_factory.load_termination(or_combo_config)
  200. assert termination is not None
  201. # Test invalid combinations
  202. with pytest.raises(ValueError):
  203. await component_factory.load_termination(TerminationConfig(
  204. termination_type=TerminationTypes.COMBINATION,
  205. conditions=[], # Empty conditions
  206. component_type=ComponentTypes.TERMINATION,
  207. version="1.0.0"
  208. ))
  209. with pytest.raises(ValueError):
  210. await component_factory.load_termination(TerminationConfig(
  211. termination_type=TerminationTypes.COMBINATION,
  212. operator="invalid", # type: ignore
  213. conditions=[
  214. TerminationConfig(
  215. termination_type=TerminationTypes.MAX_MESSAGES,
  216. max_messages=5,
  217. component_type=ComponentTypes.TERMINATION,
  218. version="1.0.0"
  219. )
  220. ],
  221. component_type=ComponentTypes.TERMINATION,
  222. version="1.0.0"
  223. ))
  224. # Test missing operator
  225. with pytest.raises(ValueError):
  226. await component_factory.load_termination(TerminationConfig(
  227. termination_type=TerminationTypes.COMBINATION,
  228. conditions=[
  229. TerminationConfig(
  230. termination_type=TerminationTypes.MAX_MESSAGES,
  231. max_messages=5,
  232. component_type=ComponentTypes.TERMINATION,
  233. version="1.0.0"
  234. ),
  235. TerminationConfig(
  236. termination_type=TerminationTypes.TEXT_MENTION,
  237. text="DONE",
  238. component_type=ComponentTypes.TERMINATION,
  239. version="1.0.0"
  240. )
  241. ],
  242. component_type=ComponentTypes.TERMINATION,
  243. version="1.0.0"
  244. ))
  245. @pytest.mark.asyncio
  246. async def test_load_team(component_factory: ComponentFactory, sample_team_config: TeamConfig, sample_model_config: ModelConfig):
  247. # Test loading RoundRobinGroupChat team
  248. team = await component_factory.load_team(sample_team_config)
  249. assert isinstance(team, RoundRobinGroupChat)
  250. assert len(team._participants) == 1
  251. # Test loading SelectorGroupChat team with multiple participants
  252. selector_team_config = TeamConfig(
  253. name="selector_team",
  254. team_type=TeamTypes.SELECTOR,
  255. participants=[ # Add two participants
  256. sample_team_config.participants[0], # First agent
  257. AgentConfig( # Second agent
  258. name="test_agent_2",
  259. agent_type=AgentTypes.ASSISTANT,
  260. system_message="You are another helpful assistant",
  261. model_client=sample_model_config,
  262. tools=sample_team_config.participants[0].tools,
  263. component_type=ComponentTypes.AGENT,
  264. version="1.0.0"
  265. )
  266. ],
  267. termination_condition=sample_team_config.termination_condition,
  268. model_client=sample_model_config,
  269. component_type=ComponentTypes.TEAM,
  270. version="1.0.0"
  271. )
  272. team = await component_factory.load_team(selector_team_config)
  273. assert isinstance(team, SelectorGroupChat)
  274. assert len(team._participants) == 2
  275. @pytest.mark.asyncio
  276. async def test_invalid_configs(component_factory: ComponentFactory):
  277. # Test invalid agent type
  278. with pytest.raises(ValueError):
  279. await component_factory.load_agent(AgentConfig(
  280. name="test",
  281. agent_type="InvalidAgent", # type: ignore
  282. system_message="test",
  283. component_type=ComponentTypes.AGENT,
  284. version="1.0.0"
  285. ))
  286. # Test invalid team type
  287. with pytest.raises(ValueError):
  288. await component_factory.load_team(TeamConfig(
  289. name="test",
  290. team_type="InvalidTeam", # type: ignore
  291. participants=[],
  292. component_type=ComponentTypes.TEAM,
  293. version="1.0.0"
  294. ))
  295. # Test invalid termination type
  296. with pytest.raises(ValueError):
  297. await component_factory.load_termination(TerminationConfig(
  298. termination_type="InvalidTermination", # type: ignore
  299. component_type=ComponentTypes.TERMINATION,
  300. version="1.0.0"
  301. ))