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

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