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 14 kB

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