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

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