test_action_serialization.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. from openhands.events.action import (
  2. Action,
  3. AddTaskAction,
  4. AgentFinishAction,
  5. AgentRejectAction,
  6. BrowseInteractiveAction,
  7. BrowseURLAction,
  8. CmdRunAction,
  9. FileReadAction,
  10. FileWriteAction,
  11. MessageAction,
  12. ModifyTaskAction,
  13. )
  14. from openhands.events.action.action import ActionConfirmationStatus
  15. from openhands.events.serialization import (
  16. event_from_dict,
  17. event_to_dict,
  18. event_to_memory,
  19. )
  20. def serialization_deserialization(
  21. original_action_dict, cls, max_message_chars: int = 10000
  22. ):
  23. action_instance = event_from_dict(original_action_dict)
  24. assert isinstance(
  25. action_instance, Action
  26. ), 'The action instance should be an instance of Action.'
  27. assert isinstance(
  28. action_instance, cls
  29. ), f'The action instance should be an instance of {cls.__name__}.'
  30. # event_to_dict is the regular serialization of an event
  31. serialized_action_dict = event_to_dict(action_instance)
  32. # it has an extra message property, for the UI
  33. serialized_action_dict.pop('message')
  34. assert (
  35. serialized_action_dict == original_action_dict
  36. ), 'The serialized action should match the original action dict.'
  37. # memory dict is what is sent to the LLM
  38. serialized_action_memory = event_to_memory(action_instance, max_message_chars)
  39. original_memory_dict = original_action_dict.copy()
  40. # we don't send backend properties like id or 'keep_prompt'
  41. original_memory_dict.pop('id', None)
  42. original_memory_dict.pop('timestamp', None)
  43. if 'args' in original_memory_dict:
  44. original_memory_dict['args'].pop('keep_prompt', None)
  45. original_memory_dict['args'].pop('blocking', None)
  46. original_memory_dict['args'].pop('confirmation_state', None)
  47. # the rest should match
  48. assert (
  49. serialized_action_memory == original_memory_dict
  50. ), 'The serialized action in memory should match the original action dict.'
  51. def test_event_props_serialization_deserialization():
  52. original_action_dict = {
  53. 'id': 42,
  54. 'source': 'agent',
  55. 'timestamp': '2021-08-01T12:00:00',
  56. 'action': 'message',
  57. 'args': {
  58. 'content': 'This is a test.',
  59. 'image_urls': None,
  60. 'wait_for_response': False,
  61. },
  62. }
  63. serialization_deserialization(original_action_dict, MessageAction)
  64. def test_message_action_serialization_deserialization():
  65. original_action_dict = {
  66. 'action': 'message',
  67. 'args': {
  68. 'content': 'This is a test.',
  69. 'image_urls': None,
  70. 'wait_for_response': False,
  71. },
  72. }
  73. serialization_deserialization(original_action_dict, MessageAction)
  74. def test_agent_finish_action_serialization_deserialization():
  75. original_action_dict = {'action': 'finish', 'args': {'outputs': {}, 'thought': ''}}
  76. serialization_deserialization(original_action_dict, AgentFinishAction)
  77. def test_agent_reject_action_serialization_deserialization():
  78. original_action_dict = {'action': 'reject', 'args': {'outputs': {}, 'thought': ''}}
  79. serialization_deserialization(original_action_dict, AgentRejectAction)
  80. def test_cmd_run_action_serialization_deserialization():
  81. original_action_dict = {
  82. 'action': 'run',
  83. 'args': {
  84. 'blocking': False,
  85. 'command': 'echo "Hello world"',
  86. 'thought': '',
  87. 'keep_prompt': True,
  88. 'hidden': False,
  89. 'confirmation_state': ActionConfirmationStatus.CONFIRMED,
  90. },
  91. }
  92. serialization_deserialization(original_action_dict, CmdRunAction)
  93. def test_browse_url_action_serialization_deserialization():
  94. original_action_dict = {
  95. 'action': 'browse',
  96. 'args': {'thought': '', 'url': 'https://www.example.com'},
  97. }
  98. serialization_deserialization(original_action_dict, BrowseURLAction)
  99. def test_browse_interactive_action_serialization_deserialization():
  100. original_action_dict = {
  101. 'action': 'browse_interactive',
  102. 'args': {
  103. 'thought': '',
  104. 'browser_actions': 'goto("https://www.example.com")',
  105. 'browsergym_send_msg_to_user': '',
  106. },
  107. }
  108. serialization_deserialization(original_action_dict, BrowseInteractiveAction)
  109. def test_file_read_action_serialization_deserialization():
  110. original_action_dict = {
  111. 'action': 'read',
  112. 'args': {'path': '/path/to/file.txt', 'start': 0, 'end': -1, 'thought': 'None'},
  113. }
  114. serialization_deserialization(original_action_dict, FileReadAction)
  115. def test_file_write_action_serialization_deserialization():
  116. original_action_dict = {
  117. 'action': 'write',
  118. 'args': {
  119. 'path': '/path/to/file.txt',
  120. 'content': 'Hello world',
  121. 'start': 0,
  122. 'end': 1,
  123. 'thought': 'None',
  124. },
  125. }
  126. serialization_deserialization(original_action_dict, FileWriteAction)
  127. def test_add_task_action_serialization_deserialization():
  128. original_action_dict = {
  129. 'action': 'add_task',
  130. 'args': {
  131. 'parent': 'Test parent',
  132. 'goal': 'Test goal',
  133. 'subtasks': [],
  134. 'thought': '',
  135. },
  136. }
  137. serialization_deserialization(original_action_dict, AddTaskAction)
  138. def test_modify_task_action_serialization_deserialization():
  139. original_action_dict = {
  140. 'action': 'modify_task',
  141. 'args': {'task_id': 1, 'state': 'Test state.', 'thought': ''},
  142. }
  143. serialization_deserialization(original_action_dict, ModifyTaskAction)