| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- from opendevin.action import (
- Action,
- AgentFinishAction,
- CmdRunAction,
- FileReadAction,
- FileWriteAction,
- BrowseURLAction,
- AgentEchoAction,
- AgentThinkAction,
- )
- import re
- from .prompts import CUSTOM_DOCS, COMMAND_USAGE
- # commands: exit, read, write, browse, kill, search_file, search_dir
- no_open_file_error = AgentEchoAction(
- 'You are not currently in a file. You can use the read command to open a file and then use goto to navigate through it.')
- def invalid_error(cmd, docs):
- return f"""ERROR:
- Invalid command structure for
- ```
- {cmd}
- ```
- You may have caused this error by having multiple commands within your command block.
- If so, try again by running only one of the commands:
- Try again using this format:
- {COMMAND_USAGE[docs]}
- """
- def get_action_from_string(command_string: str, path: str, line: int, thoughts: str = '') -> Action | None:
- """
- Parses the command string to find which command the agent wants to run
- Converts the command into a proper Action and returns
- """
- vars = command_string.split(' ')
- cmd = vars[0]
- args = [] if len(vars) == 1 else vars[1:]
- if 'exit' == cmd:
- return AgentFinishAction()
- elif 'think' == cmd:
- return AgentThinkAction(' '.join(args))
- elif 'scroll_up' == cmd:
- if not path:
- return no_open_file_error
- return FileReadAction(path, line + 100, line + 200, thoughts)
- elif 'scroll_down' == cmd:
- if not path:
- return no_open_file_error
- return FileReadAction(path, line - 100, line, thoughts)
- elif 'goto' == cmd:
- if not path:
- return no_open_file_error
- rex = r'^goto\s+(\d+)$'
- valid = re.match(rex, command_string)
- if valid:
- start = int(valid.group(1))
- end = start + 100
- return FileReadAction(path, start, end, thoughts)
- else:
- return AgentEchoAction(invalid_error(command_string, 'goto'))
- elif 'edit' == cmd:
- if not path:
- return no_open_file_error
- rex = r'^edit\s+(\d+)\s+(-?\d+)\s+(\S.*)$'
- valid = re.match(rex, command_string, re.DOTALL)
- if valid:
- start = int(valid.group(1))
- end = int(valid.group(2))
- change = valid.group(3)
- if '"' == change[-1] and '"' == change[0]:
- change = change[1:-1]
- return FileWriteAction(path, change, start, end, thoughts)
- else:
- return AgentEchoAction(invalid_error(command_string, 'edit'))
- elif 'read' == cmd:
- rex = r'^read\s+(\S+)(?:\s+(\d+))?(?:\s+(-?\d+))?$'
- valid = re.match(rex, command_string, re.DOTALL)
- if valid:
- file = valid.group(1)
- start_str = valid.group(2)
- end_str = valid.group(3)
- start = 0 if not start_str else int(start_str)
- end = -1 if not end_str else int(end_str)
- return FileReadAction(file, start, end, thoughts)
- else:
- return AgentEchoAction(invalid_error(command_string, 'read'))
- elif 'write' == cmd:
- rex = r'^write\s+(\S+)\s+(.*?)\s*(\d+)?\s*(-?\d+)?$'
- valid = re.match(rex, command_string, re.DOTALL)
- if valid:
- file = valid.group(1)
- content = valid.group(2)
- start_str = valid.group(3)
- end_str = valid.group(4)
- start = 0 if not start_str else int(start_str)
- end = -1 if not end_str else int(end_str)
- if '"' == content[-1] and '"' == content[0]:
- content = content[1:-1]
- return FileWriteAction(file, content, start, end, thoughts)
- else:
- return AgentEchoAction(invalid_error(command_string, 'write'))
- elif 'browse' == cmd:
- return BrowseURLAction(args[0].strip())
- elif cmd in ['search_file', 'search_dir', 'find_file']:
- rex = r'^(search_file|search_dir|find_file)\s+(\S+)(?:\s+(\S+))?$'
- valid = re.match(rex, command_string, re.DOTALL)
- if valid:
- return CmdRunAction(command_string)
- else:
- return AgentEchoAction(f'Invalid command structure for\n ```\n{command_string}\n```.\nTry again using this format:\n{CUSTOM_DOCS}')
- else:
- # check bash command
- obs = str(CmdRunAction(f'type {cmd}'))
- if obs.split(':')[-1].strip() == 'not found':
- # echo not found error for llm
- return AgentEchoAction(content=obs)
- else:
- # run valid command
- return CmdRunAction(command_string)
- def parse_command(input_str: str, path: str, line: int):
- """
- Parses a given string and separates the command (enclosed in triple backticks) from any accompanying text.
- Args:
- input_str (str): The input string to be parsed.
- Returns:
- tuple: A tuple containing the command and the accompanying text (if any).
- """
- input_str = input_str.strip()
- if '```' in input_str:
- parts = input_str.split('```')
- command_str = parts[1].strip()
- ind = 2 if len(parts) > 2 else 1
- accompanying_text = ''.join(parts[:-ind]).strip()
- action = get_action_from_string(
- command_str, path, line, accompanying_text)
- if action:
- return action, accompanying_text
- return None, input_str # used for retry
|