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_cancellation.py 5.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import asyncio
  2. from dataclasses import dataclass
  3. import pytest
  4. from autogen_core import (
  5. AgentId,
  6. AgentInstantiationContext,
  7. CancellationToken,
  8. MessageContext,
  9. RoutedAgent,
  10. SingleThreadedAgentRuntime,
  11. message_handler,
  12. )
  13. @dataclass
  14. class MessageType: ...
  15. # Note for future reader:
  16. # To do cancellation, only the token should be interacted with as a user
  17. # If you cancel a future, it may not work as you expect.
  18. class LongRunningAgent(RoutedAgent):
  19. def __init__(self) -> None:
  20. super().__init__("A long running agent")
  21. self.called = False
  22. self.cancelled = False
  23. @message_handler
  24. async def on_new_message(self, message: MessageType, ctx: MessageContext) -> MessageType:
  25. self.called = True
  26. sleep = asyncio.ensure_future(asyncio.sleep(100))
  27. ctx.cancellation_token.link_future(sleep)
  28. try:
  29. await sleep
  30. return MessageType()
  31. except asyncio.CancelledError:
  32. self.cancelled = True
  33. raise
  34. class NestingLongRunningAgent(RoutedAgent):
  35. def __init__(self, nested_agent: AgentId) -> None:
  36. super().__init__("A nesting long running agent")
  37. self.called = False
  38. self.cancelled = False
  39. self._nested_agent = nested_agent
  40. @message_handler
  41. async def on_new_message(self, message: MessageType, ctx: MessageContext) -> MessageType:
  42. self.called = True
  43. response = self.send_message(message, self._nested_agent, cancellation_token=ctx.cancellation_token)
  44. try:
  45. val = await response
  46. assert isinstance(val, MessageType)
  47. return val
  48. except asyncio.CancelledError:
  49. self.cancelled = True
  50. raise
  51. @pytest.mark.asyncio
  52. async def test_cancellation_with_token() -> None:
  53. runtime = SingleThreadedAgentRuntime()
  54. await LongRunningAgent.register(runtime, "long_running", LongRunningAgent)
  55. agent_id = AgentId("long_running", key="default")
  56. token = CancellationToken()
  57. response = asyncio.create_task(runtime.send_message(MessageType(), recipient=agent_id, cancellation_token=token))
  58. assert not response.done()
  59. while runtime.unprocessed_messages_count == 0:
  60. await asyncio.sleep(0.01)
  61. await runtime._process_next() # type: ignore
  62. token.cancel()
  63. with pytest.raises(asyncio.CancelledError):
  64. await response
  65. assert response.done()
  66. long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LongRunningAgent)
  67. assert long_running_agent.called
  68. assert long_running_agent.cancelled
  69. @pytest.mark.asyncio
  70. async def test_nested_cancellation_only_outer_called() -> None:
  71. runtime = SingleThreadedAgentRuntime()
  72. await LongRunningAgent.register(runtime, "long_running", LongRunningAgent)
  73. await NestingLongRunningAgent.register(
  74. runtime,
  75. "nested",
  76. lambda: NestingLongRunningAgent(AgentId("long_running", key=AgentInstantiationContext.current_agent_id().key)),
  77. )
  78. long_running_id = AgentId("long_running", key="default")
  79. nested_id = AgentId("nested", key="default")
  80. token = CancellationToken()
  81. response = asyncio.create_task(runtime.send_message(MessageType(), nested_id, cancellation_token=token))
  82. assert not response.done()
  83. while runtime.unprocessed_messages_count == 0:
  84. await asyncio.sleep(0.01)
  85. await runtime._process_next() # type: ignore
  86. token.cancel()
  87. with pytest.raises(asyncio.CancelledError):
  88. await response
  89. assert response.done()
  90. nested_agent = await runtime.try_get_underlying_agent_instance(nested_id, type=NestingLongRunningAgent)
  91. assert nested_agent.called
  92. assert nested_agent.cancelled
  93. long_running_agent = await runtime.try_get_underlying_agent_instance(long_running_id, type=LongRunningAgent)
  94. assert long_running_agent.called is False
  95. assert long_running_agent.cancelled is False
  96. @pytest.mark.asyncio
  97. async def test_nested_cancellation_inner_called() -> None:
  98. runtime = SingleThreadedAgentRuntime()
  99. await LongRunningAgent.register(runtime, "long_running", LongRunningAgent)
  100. await NestingLongRunningAgent.register(
  101. runtime,
  102. "nested",
  103. lambda: NestingLongRunningAgent(AgentId("long_running", key=AgentInstantiationContext.current_agent_id().key)),
  104. )
  105. long_running_id = AgentId("long_running", key="default")
  106. nested_id = AgentId("nested", key="default")
  107. token = CancellationToken()
  108. response = asyncio.create_task(runtime.send_message(MessageType(), nested_id, cancellation_token=token))
  109. assert not response.done()
  110. while runtime.unprocessed_messages_count == 0:
  111. await asyncio.sleep(0.01)
  112. await runtime._process_next() # type: ignore
  113. # allow the inner agent to process
  114. await runtime._process_next() # type: ignore
  115. token.cancel()
  116. with pytest.raises(asyncio.CancelledError):
  117. await response
  118. assert response.done()
  119. nested_agent = await runtime.try_get_underlying_agent_instance(nested_id, type=NestingLongRunningAgent)
  120. assert nested_agent.called
  121. assert nested_agent.cancelled
  122. long_running_agent = await runtime.try_get_underlying_agent_instance(long_running_id, type=LongRunningAgent)
  123. assert long_running_agent.called
  124. assert long_running_agent.cancelled