Spaces:
Build error
Build error
from typing import Any | |
from openhands.core.exceptions import LLMMalformedActionError | |
from openhands.events.action.action import Action | |
from openhands.events.action.agent import ( | |
AgentDelegateAction, | |
AgentFinishAction, | |
AgentRejectAction, | |
AgentThinkAction, | |
ChangeAgentStateAction, | |
CondensationAction, | |
RecallAction, | |
) | |
from openhands.events.action.browse import BrowseInteractiveAction, BrowseURLAction | |
from openhands.events.action.commands import ( | |
CmdRunAction, | |
IPythonRunCellAction, | |
) | |
from openhands.events.action.empty import NullAction | |
from openhands.events.action.files import ( | |
FileEditAction, | |
FileReadAction, | |
FileWriteAction, | |
) | |
from openhands.events.action.mcp import MCPAction | |
from openhands.events.action.message import MessageAction, SystemMessageAction | |
actions = ( | |
NullAction, | |
CmdRunAction, | |
IPythonRunCellAction, | |
BrowseURLAction, | |
BrowseInteractiveAction, | |
FileReadAction, | |
FileWriteAction, | |
FileEditAction, | |
AgentThinkAction, | |
AgentFinishAction, | |
AgentRejectAction, | |
AgentDelegateAction, | |
RecallAction, | |
ChangeAgentStateAction, | |
MessageAction, | |
SystemMessageAction, | |
CondensationAction, | |
MCPAction, | |
) | |
ACTION_TYPE_TO_CLASS = {action_class.action: action_class for action_class in actions} # type: ignore[attr-defined] | |
def handle_action_deprecated_args(args: dict[str, Any]) -> dict[str, Any]: | |
# keep_prompt has been deprecated in https://github.com/All-Hands-AI/OpenHands/pull/4881 | |
if 'keep_prompt' in args: | |
args.pop('keep_prompt') | |
# Handle translated_ipython_code deprecation | |
if 'translated_ipython_code' in args: | |
code = args.pop('translated_ipython_code') | |
# Check if it's a file_editor call using a prefix check for efficiency | |
file_editor_prefix = 'print(file_editor(**' | |
if ( | |
code is not None | |
and code.startswith(file_editor_prefix) | |
and code.endswith('))') | |
): | |
try: | |
# Extract and evaluate the dictionary string | |
import ast | |
# Extract the dictionary string between the prefix and the closing parentheses | |
dict_str = code[len(file_editor_prefix) : -2] # Remove prefix and '))' | |
file_args = ast.literal_eval(dict_str) | |
# Update args with the extracted file editor arguments | |
args.update(file_args) | |
except (ValueError, SyntaxError): | |
# If parsing fails, just remove the translated_ipython_code | |
pass | |
if args.get('command') == 'view': | |
args.pop( | |
'command' | |
) # "view" will be translated to FileReadAction which doesn't have a command argument | |
return args | |
def action_from_dict(action: dict) -> Action: | |
if not isinstance(action, dict): | |
raise LLMMalformedActionError('action must be a dictionary') | |
action = action.copy() | |
if 'action' not in action: | |
raise LLMMalformedActionError(f"'action' key is not found in {action=}") | |
if not isinstance(action['action'], str): | |
raise LLMMalformedActionError( | |
f"'{action['action']=}' is not defined. Available actions: {ACTION_TYPE_TO_CLASS.keys()}" | |
) | |
action_class = ACTION_TYPE_TO_CLASS.get(action['action']) | |
if action_class is None: | |
raise LLMMalformedActionError( | |
f"'{action['action']=}' is not defined. Available actions: {ACTION_TYPE_TO_CLASS.keys()}" | |
) | |
args = action.get('args', {}) | |
# Remove timestamp from args if present | |
timestamp = args.pop('timestamp', None) | |
# compatibility for older event streams | |
# is_confirmed has been renamed to confirmation_state | |
is_confirmed = args.pop('is_confirmed', None) | |
if is_confirmed is not None: | |
args['confirmation_state'] = is_confirmed | |
# images_urls has been renamed to image_urls | |
if 'images_urls' in args: | |
args['image_urls'] = args.pop('images_urls') | |
# handle deprecated args | |
args = handle_action_deprecated_args(args) | |
try: | |
decoded_action = action_class(**args) | |
if 'timeout' in action: | |
blocking = args.get('blocking', False) | |
decoded_action.set_hard_timeout(action['timeout'], blocking=blocking) | |
# Set timestamp if it was provided | |
if timestamp: | |
decoded_action._timestamp = timestamp | |
except TypeError as e: | |
raise LLMMalformedActionError( | |
f'action={action} has the wrong arguments: {str(e)}' | |
) | |
assert isinstance(decoded_action, Action) | |
return decoded_action | |