action.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. from openhands.core.exceptions import LLMMalformedActionError
  2. from openhands.events.action.action import Action
  3. from openhands.events.action.agent import (
  4. AgentDelegateAction,
  5. AgentFinishAction,
  6. AgentRejectAction,
  7. ChangeAgentStateAction,
  8. )
  9. from openhands.events.action.browse import BrowseInteractiveAction, BrowseURLAction
  10. from openhands.events.action.commands import (
  11. CmdRunAction,
  12. IPythonRunCellAction,
  13. )
  14. from openhands.events.action.empty import NullAction
  15. from openhands.events.action.files import (
  16. FileEditAction,
  17. FileReadAction,
  18. FileWriteAction,
  19. )
  20. from openhands.events.action.message import MessageAction
  21. from openhands.events.action.tasks import AddTaskAction, ModifyTaskAction
  22. actions = (
  23. NullAction,
  24. CmdRunAction,
  25. IPythonRunCellAction,
  26. BrowseURLAction,
  27. BrowseInteractiveAction,
  28. FileReadAction,
  29. FileWriteAction,
  30. FileEditAction,
  31. AgentFinishAction,
  32. AgentRejectAction,
  33. AgentDelegateAction,
  34. AddTaskAction,
  35. ModifyTaskAction,
  36. ChangeAgentStateAction,
  37. MessageAction,
  38. )
  39. ACTION_TYPE_TO_CLASS = {action_class.action: action_class for action_class in actions} # type: ignore[attr-defined]
  40. def action_from_dict(action: dict) -> Action:
  41. if not isinstance(action, dict):
  42. raise LLMMalformedActionError('action must be a dictionary')
  43. action = action.copy()
  44. if 'action' not in action:
  45. raise LLMMalformedActionError(f"'action' key is not found in {action=}")
  46. if not isinstance(action['action'], str):
  47. raise LLMMalformedActionError(
  48. f"'{action['action']=}' is not defined. Available actions: {ACTION_TYPE_TO_CLASS.keys()}"
  49. )
  50. action_class = ACTION_TYPE_TO_CLASS.get(action['action'])
  51. if action_class is None:
  52. raise LLMMalformedActionError(
  53. f"'{action['action']=}' is not defined. Available actions: {ACTION_TYPE_TO_CLASS.keys()}"
  54. )
  55. args = action.get('args', {})
  56. # Remove timestamp from args if present
  57. timestamp = args.pop('timestamp', None)
  58. # compatibility for older event streams
  59. # is_confirmed has been renamed to confirmation_state
  60. is_confirmed = args.pop('is_confirmed', None)
  61. if is_confirmed is not None:
  62. args['confirmation_state'] = is_confirmed
  63. # images_urls has been renamed to image_urls
  64. if 'images_urls' in args:
  65. args['image_urls'] = args.pop('images_urls')
  66. try:
  67. decoded_action = action_class(**args)
  68. if 'timeout' in action:
  69. decoded_action.timeout = action['timeout']
  70. # Set timestamp if it was provided
  71. if timestamp:
  72. decoded_action._timestamp = timestamp
  73. except TypeError as e:
  74. raise LLMMalformedActionError(
  75. f'action={action} has the wrong arguments: {str(e)}'
  76. )
  77. return decoded_action