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

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import logging
  2. import pytest
  3. from autogen_core.application import SingleThreadedAgentRuntime
  4. from autogen_core.base import (
  5. AgentId,
  6. AgentInstantiationContext,
  7. AgentType,
  8. TopicId,
  9. try_get_known_serializers_for_type,
  10. )
  11. from autogen_core.components import DefaultTopicId, TypeSubscription, type_subscription
  12. from opentelemetry.sdk.trace import TracerProvider
  13. from test_utils import (
  14. CascadingAgent,
  15. CascadingMessageType,
  16. LoopbackAgent,
  17. LoopbackAgentWithDefaultSubscription,
  18. MessageType,
  19. NoopAgent,
  20. )
  21. from test_utils.telemetry_test_utils import TestExporter, get_test_tracer_provider
  22. test_exporter = TestExporter()
  23. @pytest.fixture
  24. def tracer_provider() -> TracerProvider:
  25. test_exporter.clear()
  26. return get_test_tracer_provider(test_exporter)
  27. @pytest.mark.asyncio
  28. async def test_agent_type_must_be_unique() -> None:
  29. runtime = SingleThreadedAgentRuntime()
  30. def agent_factory() -> NoopAgent:
  31. id = AgentInstantiationContext.current_agent_id()
  32. assert id == AgentId("name1", "default")
  33. agent = NoopAgent()
  34. assert agent.id == id
  35. return agent
  36. await NoopAgent.register(runtime, "name1", agent_factory)
  37. # await runtime.register_factory(type=AgentType("name1"), agent_factory=agent_factory, expected_class=NoopAgent)
  38. with pytest.raises(ValueError):
  39. await runtime.register_factory(type=AgentType("name1"), agent_factory=agent_factory, expected_class=NoopAgent)
  40. await runtime.register_factory(type=AgentType("name2"), agent_factory=agent_factory, expected_class=NoopAgent)
  41. @pytest.mark.asyncio
  42. async def test_register_receives_publish(tracer_provider: TracerProvider) -> None:
  43. runtime = SingleThreadedAgentRuntime(tracer_provider=tracer_provider)
  44. runtime.add_message_serializer(try_get_known_serializers_for_type(MessageType))
  45. await runtime.register_factory(
  46. type=AgentType("name"), agent_factory=lambda: LoopbackAgent(), expected_class=LoopbackAgent
  47. )
  48. await runtime.add_subscription(TypeSubscription("default", "name"))
  49. runtime.start()
  50. await runtime.publish_message(MessageType(), topic_id=TopicId("default", "default"))
  51. await runtime.stop_when_idle()
  52. # Agent in default namespace should have received the message
  53. long_running_agent = await runtime.try_get_underlying_agent_instance(AgentId("name", "default"), type=LoopbackAgent)
  54. assert long_running_agent.num_calls == 1
  55. # Agent in other namespace should not have received the message
  56. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(
  57. AgentId("name", key="other"), type=LoopbackAgent
  58. )
  59. assert other_long_running_agent.num_calls == 0
  60. exported_spans = test_exporter.get_exported_spans()
  61. assert len(exported_spans) == 3
  62. span_names = [span.name for span in exported_spans]
  63. assert span_names == [
  64. "autogen create default.(default)-T",
  65. "autogen process name.(default)-A",
  66. "autogen publish default.(default)-T",
  67. ]
  68. @pytest.mark.asyncio
  69. async def test_register_receives_publish_with_exception(caplog: pytest.LogCaptureFixture) -> None:
  70. runtime = SingleThreadedAgentRuntime()
  71. runtime.add_message_serializer(try_get_known_serializers_for_type(MessageType))
  72. async def agent_factory() -> LoopbackAgent:
  73. raise ValueError("test")
  74. await runtime.register_factory(type=AgentType("name"), agent_factory=agent_factory, expected_class=LoopbackAgent)
  75. await runtime.add_subscription(TypeSubscription("default", "name"))
  76. with caplog.at_level(logging.ERROR):
  77. runtime.start()
  78. await runtime.publish_message(MessageType(), topic_id=TopicId("default", "default"))
  79. await runtime.stop_when_idle()
  80. # Check if logger has the exception.
  81. assert any("Error processing publish message" in e.message for e in caplog.records)
  82. @pytest.mark.asyncio
  83. async def test_register_receives_publish_cascade() -> None:
  84. num_agents = 5
  85. num_initial_messages = 5
  86. max_rounds = 5
  87. total_num_calls_expected = 0
  88. for i in range(0, max_rounds):
  89. total_num_calls_expected += num_initial_messages * ((num_agents - 1) ** i)
  90. runtime = SingleThreadedAgentRuntime()
  91. # Register agents
  92. for i in range(num_agents):
  93. await CascadingAgent.register(runtime, f"name{i}", lambda: CascadingAgent(max_rounds))
  94. runtime.start()
  95. # Publish messages
  96. for _ in range(num_initial_messages):
  97. await runtime.publish_message(CascadingMessageType(round=1), DefaultTopicId())
  98. # Process until idle.
  99. await runtime.stop_when_idle()
  100. # Check that each agent received the correct number of messages.
  101. for i in range(num_agents):
  102. agent = await runtime.try_get_underlying_agent_instance(AgentId(f"name{i}", "default"), CascadingAgent)
  103. assert agent.num_calls == total_num_calls_expected
  104. @pytest.mark.asyncio
  105. async def test_register_factory_explicit_name() -> None:
  106. runtime = SingleThreadedAgentRuntime()
  107. await LoopbackAgent.register(runtime, "name", LoopbackAgent)
  108. await runtime.add_subscription(TypeSubscription("default", "name"))
  109. runtime.start()
  110. agent_id = AgentId("name", key="default")
  111. topic_id = TopicId("default", "default")
  112. await runtime.publish_message(MessageType(), topic_id=topic_id)
  113. await runtime.stop_when_idle()
  114. # Agent in default namespace should have received the message
  115. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent)
  116. assert long_running_agent.num_calls == 1
  117. # Agent in other namespace should not have received the message
  118. other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance(
  119. AgentId("name", key="other"), type=LoopbackAgent
  120. )
  121. assert other_long_running_agent.num_calls == 0
  122. @pytest.mark.asyncio
  123. async def test_default_subscription() -> None:
  124. runtime = SingleThreadedAgentRuntime()
  125. runtime.start()
  126. await LoopbackAgentWithDefaultSubscription.register(runtime, "name", LoopbackAgentWithDefaultSubscription)
  127. agent_id = AgentId("name", key="default")
  128. await runtime.publish_message(MessageType(), topic_id=DefaultTopicId())
  129. await runtime.stop_when_idle()
  130. long_running_agent = await runtime.try_get_underlying_agent_instance(
  131. agent_id, type=LoopbackAgentWithDefaultSubscription
  132. )
  133. assert long_running_agent.num_calls == 1
  134. other_long_running_agent = await runtime.try_get_underlying_agent_instance(
  135. AgentId("name", key="other"), type=LoopbackAgentWithDefaultSubscription
  136. )
  137. assert other_long_running_agent.num_calls == 0
  138. @pytest.mark.asyncio
  139. async def test_type_subscription() -> None:
  140. runtime = SingleThreadedAgentRuntime()
  141. runtime.start()
  142. @type_subscription(topic_type="Other")
  143. class LoopbackAgentWithSubscription(LoopbackAgent): ...
  144. await LoopbackAgentWithSubscription.register(runtime, "name", LoopbackAgentWithSubscription)
  145. agent_id = AgentId("name", key="default")
  146. await runtime.publish_message(MessageType(), topic_id=TopicId("Other", "default"))
  147. await runtime.stop_when_idle()
  148. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgentWithSubscription)
  149. assert long_running_agent.num_calls == 1
  150. other_long_running_agent = await runtime.try_get_underlying_agent_instance(
  151. AgentId("name", key="other"), type=LoopbackAgentWithSubscription
  152. )
  153. assert other_long_running_agent.num_calls == 0
  154. @pytest.mark.asyncio
  155. async def test_default_subscription_publish_to_other_source() -> None:
  156. runtime = SingleThreadedAgentRuntime()
  157. runtime.start()
  158. await LoopbackAgentWithDefaultSubscription.register(runtime, "name", LoopbackAgentWithDefaultSubscription)
  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. long_running_agent = await runtime.try_get_underlying_agent_instance(
  163. agent_id, type=LoopbackAgentWithDefaultSubscription
  164. )
  165. assert long_running_agent.num_calls == 0
  166. other_long_running_agent = await runtime.try_get_underlying_agent_instance(
  167. AgentId("name", key="other"), type=LoopbackAgentWithDefaultSubscription
  168. )
  169. assert other_long_running_agent.num_calls == 1