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_runtime.py 9.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import asyncio
  2. import pytest
  3. from agnext.application import SingleThreadedAgentRuntime
  4. from agnext.components import TypeSubscription, DefaultTopicId, DefaultSubscription
  5. from agnext.core import AgentId, AgentInstantiationContext
  6. from agnext.core import TopicId
  7. from agnext.core import Subscription
  8. from agnext.core import SubscriptionInstantiationContext
  9. from test_utils import CascadingAgent, CascadingMessageType, LoopbackAgent, MessageType, NoopAgent
  10. @pytest.mark.asyncio
  11. async def test_agent_names_must_be_unique() -> None:
  12. runtime = SingleThreadedAgentRuntime()
  13. def agent_factory() -> NoopAgent:
  14. id = AgentInstantiationContext.current_agent_id()
  15. assert id == AgentId("name1", "default")
  16. agent = NoopAgent()
  17. assert agent.id == id
  18. return agent
  19. await runtime.register("name1", agent_factory)
  20. with pytest.raises(ValueError):
  21. await runtime.register("name1", NoopAgent)
  22. await runtime.register("name3", NoopAgent)
  23. @pytest.mark.asyncio
  24. async def test_register_receives_publish() -> None:
  25. runtime = SingleThreadedAgentRuntime()
  26. await runtime.register("name", LoopbackAgent)
  27. runtime.start()
  28. await runtime.add_subscription(TypeSubscription("default", "name"))
  29. agent_id = AgentId("name", key="default")
  30. topic_id = TopicId("default", "default")
  31. await runtime.publish_message(MessageType(), topic_id=topic_id)
  32. await runtime.stop_when_idle()
  33. # Agent in default namespace should have received the message
  34. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  35. assert long_running_agent.num_calls == 1
  36. # Agent in other namespace should not have received the message
  37. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  38. assert other_long_running_agent.num_calls == 0
  39. @pytest.mark.asyncio
  40. async def test_register_receives_publish_cascade() -> None:
  41. runtime = SingleThreadedAgentRuntime()
  42. num_agents = 5
  43. num_initial_messages = 5
  44. max_rounds = 5
  45. total_num_calls_expected = 0
  46. for i in range(0, max_rounds):
  47. total_num_calls_expected += num_initial_messages * ((num_agents - 1) ** i)
  48. # Register agents
  49. for i in range(num_agents):
  50. await runtime.register(f"name{i}", lambda: CascadingAgent(max_rounds))
  51. await runtime.add_subscription(TypeSubscription("default", f"name{i}"))
  52. runtime.start()
  53. # Publish messages
  54. topic_id = TopicId("default", "default")
  55. for _ in range(num_initial_messages):
  56. await runtime.publish_message(CascadingMessageType(round=1), topic_id)
  57. # Process until idle.
  58. await runtime.stop_when_idle()
  59. # Check that each agent received the correct number of messages.
  60. for i in range(num_agents):
  61. agent = await runtime.try_get_underlying_agent_instance(AgentId(f"name{i}", "default"), CascadingAgent)
  62. assert agent.num_calls == total_num_calls_expected
  63. @pytest.mark.asyncio
  64. async def test_register_factory_explicit_name() -> None:
  65. runtime = SingleThreadedAgentRuntime()
  66. await runtime.register("name", LoopbackAgent, lambda: [TypeSubscription("default", "name")])
  67. runtime.start()
  68. agent_id = AgentId("name", key="default")
  69. topic_id = TopicId("default", "default")
  70. await runtime.publish_message(MessageType(), topic_id=topic_id)
  71. await runtime.stop_when_idle()
  72. # Agent in default namespace should have received the message
  73. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  74. assert long_running_agent.num_calls == 1
  75. # Agent in other namespace should not have received the message
  76. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  77. assert other_long_running_agent.num_calls == 0
  78. @pytest.mark.asyncio
  79. async def test_register_factory_context_var_name() -> None:
  80. runtime = SingleThreadedAgentRuntime()
  81. await runtime.register("name", LoopbackAgent, lambda: [TypeSubscription("default", SubscriptionInstantiationContext.agent_type().type)])
  82. runtime.start()
  83. agent_id = AgentId("name", key="default")
  84. topic_id = TopicId("default", "default")
  85. await runtime.publish_message(MessageType(), topic_id=topic_id)
  86. await runtime.stop_when_idle()
  87. # Agent in default namespace should have received the message
  88. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  89. assert long_running_agent.num_calls == 1
  90. # Agent in other namespace should not have received the message
  91. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  92. assert other_long_running_agent.num_calls == 0
  93. @pytest.mark.asyncio
  94. async def test_register_factory_async() -> None:
  95. runtime = SingleThreadedAgentRuntime()
  96. async def sub_factory() -> list[Subscription]:
  97. await asyncio.sleep(0.1)
  98. return [TypeSubscription("default", SubscriptionInstantiationContext.agent_type().type)]
  99. await runtime.register("name", LoopbackAgent, sub_factory)
  100. runtime.start()
  101. agent_id = AgentId("name", key="default")
  102. topic_id = TopicId("default", "default")
  103. await runtime.publish_message(MessageType(), topic_id=topic_id)
  104. await runtime.stop_when_idle()
  105. # Agent in default namespace should have received the message
  106. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  107. assert long_running_agent.num_calls == 1
  108. # Agent in other namespace should not have received the message
  109. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  110. assert other_long_running_agent.num_calls == 0
  111. @pytest.mark.asyncio
  112. async def test_register_factory_direct_list() -> None:
  113. runtime = SingleThreadedAgentRuntime()
  114. await runtime.register("name", LoopbackAgent, [TypeSubscription("default", "name")])
  115. runtime.start()
  116. agent_id = AgentId("name", key="default")
  117. topic_id = TopicId("default", "default")
  118. await runtime.publish_message(MessageType(), topic_id=topic_id)
  119. await runtime.stop_when_idle()
  120. # Agent in default namespace should have received the message
  121. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  122. assert long_running_agent.num_calls == 1
  123. # Agent in other namespace should not have received the message
  124. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  125. assert other_long_running_agent.num_calls == 0
  126. @pytest.mark.asyncio
  127. async def test_default_subscription() -> None:
  128. runtime = SingleThreadedAgentRuntime()
  129. await runtime.register("name", LoopbackAgent, lambda: [DefaultSubscription()])
  130. runtime.start()
  131. agent_id = AgentId("name", key="default")
  132. await runtime.publish_message(MessageType(), topic_id=DefaultTopicId())
  133. await runtime.stop_when_idle()
  134. # Agent in default namespace should have received the message
  135. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  136. assert long_running_agent.num_calls == 1
  137. # Agent in other namespace should not have received the message
  138. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  139. assert other_long_running_agent.num_calls == 0
  140. @pytest.mark.asyncio
  141. async def test_non_default_default_subscription() -> None:
  142. runtime = SingleThreadedAgentRuntime()
  143. await runtime.register("name", LoopbackAgent, lambda: [DefaultSubscription(topic_type="Other")])
  144. runtime.start()
  145. agent_id = AgentId("name", key="default")
  146. await runtime.publish_message(MessageType(), topic_id=DefaultTopicId(type="Other"))
  147. await runtime.stop_when_idle()
  148. # Agent in default namespace should have received the message
  149. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  150. assert long_running_agent.num_calls == 1
  151. # Agent in other namespace should not have received the message
  152. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  153. assert other_long_running_agent.num_calls == 0
  154. @pytest.mark.asyncio
  155. async def test_non_publish_to_other_source() -> None:
  156. runtime = SingleThreadedAgentRuntime()
  157. await runtime.register("name", LoopbackAgent, lambda: [DefaultSubscription()])
  158. runtime.start()
  159. agent_id = AgentId("name", key="default")
  160. await runtime.publish_message(MessageType(), topic_id=DefaultTopicId(source="other"))
  161. await runtime.stop_when_idle()
  162. # Agent in default namespace should have received the message
  163. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  164. assert long_running_agent.num_calls == 0
  165. # Agent in other namespace should not have received the message
  166. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(AgentId("name", key="other"), type=LoopbackAgent)
  167. assert other_long_running_agent.num_calls == 1