-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Process interactive commands and stream output in logs #2059
Conversation
opendevin/runtime/docker/ssh_box.py
Outdated
@@ -738,21 +769,21 @@ def close(self): | |||
except Exception as e: | |||
logger.exception('Failed to start Docker container: %s', e) | |||
sys.exit(1) | |||
|
|||
logger.setLevel('DEBUG') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forget to delete or intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intentional; as it is for devs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am strongly against changing logger level in the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is in the main block only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is in the main block only.
Ah I see, that's much less concerning then... I still prefer we don't do it in the code, coz it might surprise developers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe logging about the change would help?
_exit_code = self.ssh.before.strip() | ||
return int(_exit_code) | ||
try: | ||
return int(_exit_code) | ||
except ValueError: | ||
logger.error(f'Invalid exit code: {_exit_code}') | ||
return -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to check the error rather than use try catch
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got Jupyter kernel ready
in _exit_code and the program stopped when I set timeout to 3 for testing. There some bugs in that like #1915. Also, it is handled in https://github.com/OpenDevin/OpenDevin/blob/d8d17aae03c8c0987cbce625103cd15020fb05da/opendevin/runtime/plugins/mixin.py#L55-L58
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC: @neubig, I am waiting for the reply.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree with @yufansong that we should fix the underlying problem, rather than catching the error here.
opendevin/runtime/docker/ssh_box.py
Outdated
@@ -738,21 +769,21 @@ def close(self): | |||
except Exception as e: | |||
logger.exception('Failed to start Docker container: %s', e) | |||
sys.exit(1) | |||
|
|||
logger.setLevel('DEBUG') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am strongly against changing logger level in the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think some code need to be refactor.
@SmartManoj : if this is ready for review again, could you re-request review? You can do it by clicking the "cycle" button next to the reviewers' names. |
I don't understand what problem this PR is trying to solve, and how it is solved. Do you mind elaborating more? |
It will parse the output and if it requires "yes" confirmation from the user, it will send that. |
prompts = [ | ||
r'Do you want to continue\? \[Y/n\]', | ||
self.ssh.PROMPT, | ||
EOF, | ||
TIMEOUT, | ||
] | ||
command_output = '' | ||
timeout_counter = 0 | ||
while True: | ||
try: | ||
# Wait for one of the prompts | ||
index = self.ssh.expect(prompts, timeout=1) | ||
line = self.ssh.before | ||
logger.info(line) | ||
command_output += line | ||
if index == 0: | ||
self.ssh.sendline('Y') | ||
elif index == 1: | ||
break | ||
elif index == 2: | ||
logger.debug('End of file') | ||
break | ||
elif index == 3: | ||
timeout_counter += 1 | ||
if timeout_counter > timeout: | ||
logger.exception( | ||
'Command timed out, killing process...', exc_info=False | ||
) | ||
return self._send_interrupt(cmd) | ||
except ExceptionPexpect as e: | ||
logger.exception(f'Unexpected exception: {e}') | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems brittle to me. Auto-confirm based on prompt text is kind of a hack
It looks like you have another PR which warns the agent about commands like this...will that suffice?
I'm generally concerned that this file is filling up with little hacks for the agent, instead of just being a thin wrapper around SSH.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean #2034 (I'm not the author)? We can warn them but they are not AGI models yet to follow that always. So need some little tweaks. Also helps weaker LLMs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is introducing several changes in the way that prompts are handled.
- Automatic entering of "Y" -- I think this is a bad idea... Usually a confirmation dialog is there because you need to check before doing something dangerous, and having the system automatically choose to proceed seems dangerous.
- This does nothing, so it can probably just be removed?
- Adding "end of file" may be good, but what was the behavior before?
- This seems like a good idea, and I actually like it better than the solution in fix: codeact bug [If running a command that never returns, it gets stuck #1895] #2034, which expands the prompt for something that can probably be done automatically like we do it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this! I had a few comments.
prompts = [ | ||
r'Do you want to continue\? \[Y/n\]', | ||
self.ssh.PROMPT, | ||
EOF, | ||
TIMEOUT, | ||
] | ||
command_output = '' | ||
timeout_counter = 0 | ||
while True: | ||
try: | ||
# Wait for one of the prompts | ||
index = self.ssh.expect(prompts, timeout=1) | ||
line = self.ssh.before | ||
logger.info(line) | ||
command_output += line | ||
if index == 0: | ||
self.ssh.sendline('Y') | ||
elif index == 1: | ||
break | ||
elif index == 2: | ||
logger.debug('End of file') | ||
break | ||
elif index == 3: | ||
timeout_counter += 1 | ||
if timeout_counter > timeout: | ||
logger.exception( | ||
'Command timed out, killing process...', exc_info=False | ||
) | ||
return self._send_interrupt(cmd) | ||
except ExceptionPexpect as e: | ||
logger.exception(f'Unexpected exception: {e}') | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is introducing several changes in the way that prompts are handled.
- Automatic entering of "Y" -- I think this is a bad idea... Usually a confirmation dialog is there because you need to check before doing something dangerous, and having the system automatically choose to proceed seems dangerous.
- This does nothing, so it can probably just be removed?
- Adding "end of file" may be good, but what was the behavior before?
- This seems like a good idea, and I actually like it better than the solution in fix: codeact bug [If running a command that never returns, it gets stuck #1895] #2034, which expands the prompt for something that can probably be done automatically like we do it here.
_exit_code = self.ssh.before.strip() | ||
return int(_exit_code) | ||
try: | ||
return int(_exit_code) | ||
except ValueError: | ||
logger.error(f'Invalid exit code: {_exit_code}') | ||
return -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree with @yufansong that we should fix the underlying problem, rather than catching the error here.
|
logger.exception('Command timed out, killing process...', exc_info=False) | ||
return self._send_interrupt(cmd) | ||
command_output = self.ssh.before | ||
prompts = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we inherit from pxssh.pxssh
, and override their .prompt
method to implement this? It can make the code much more readable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we move to Paramiko for windows support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the next week, I'm thinking about completely get rid of SSH, and spin up a websocket client inside sandbox, connect it to the backend, and do things from there -- this will allow us to use ONE event stream for everything which can be cleaner. I can start a draft PR and we can discuss from there i guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that would be better for connecting the frontend terminal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we inherit from
pxssh.pxssh
, and override their.prompt
method to implement this? It can make the code much more readable?
In which file, do you want this to be implemented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still need to inherit as it is reaching the end of life?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so! We can leave it as is for now probably?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you reopen it?
@SmartManoj wondering if this is going to be continued or should be closed? |
Let's close it for now since it has been stale for a while! Feel free to reopen if things progress and need review. |
Confirmation mode will handle this now.
|
Solves #577 and also streaming output to logs only #1625 (comment)