feat: change Jupyter cwd alone with "bash" (#3331)

* remove unused plugin mixin

* change the entire jupyter PWD with bash;
print jupyter pwd in obs as well;

* remove unused field

* remove unused comments

* change the entire jupyter PWD with bash;
print jupyter pwd in obs as well;

* fix runtime tests for jupyter

* update intgeration tests

* fix test again

---------

Co-authored-by: Graham Neubig <neubig@gmail.com>
This commit is contained in:
Xingyao Wang 2024-08-13 18:08:31 +08:00 committed by GitHub
parent 9ad665a996
commit 568e6cdb40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 58 additions and 93 deletions

View File

@ -301,7 +301,7 @@ class RuntimeClient:
logger.debug(
f"{self.pwd} != {getattr(self, '_jupyter_pwd', None)} -> reset Jupyter PWD"
)
reset_jupyter_pwd_code = f'import os; os.environ["JUPYTER_PWD"] = os.path.abspath("{self.pwd}")'
reset_jupyter_pwd_code = f'import os; os.chdir("{self.pwd}")'
_aux_action = IPythonRunCellAction(code=reset_jupyter_pwd_code)
_reset_obs = await _jupyter_plugin.run(_aux_action)
logger.debug(
@ -310,6 +310,8 @@ class RuntimeClient:
self._jupyter_pwd = self.pwd
obs: IPythonRunCellObservation = await _jupyter_plugin.run(action)
obs.content = obs.content.rstrip()
obs.content += f'\n[Jupyter current working directory: {self.pwd}]'
return obs
else:
raise RuntimeError(

View File

@ -17,7 +17,6 @@ Functions:
"""
import base64
import functools
import os
import re
import shutil
@ -75,52 +74,6 @@ def _get_openai_client():
# ==================================================================================================
# Define the decorator using the functionality of UpdatePwd
def update_pwd_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
jupyter_pwd = os.environ.get('JUPYTER_PWD', None)
try:
old_pwd = os.getcwd()
except FileNotFoundError:
import json
import subprocess
print(
f'DEBUGGING Environment variables: {json.dumps(dict(os.environ), indent=2)}'
)
print(f'DEBUGGING User ID: {os.getuid()}, Group ID: {os.getgid()}')
out = subprocess.run(['pwd'], capture_output=True)
old_pwd = out.stdout.decode('utf-8').strip()
os.chdir(old_pwd)
print(f'DEBUGGING Change to working directory: {old_pwd}')
import tempfile
try:
tempfile.TemporaryFile(dir=old_pwd)
print(f'DEBUGGING Directory {old_pwd} is writable')
except Exception as e:
print(f'DEBUGGING Directory {old_pwd} is not writable: {str(e)}')
# ls -alh
out = subprocess.run(['ls', '-alh', old_pwd], capture_output=True)
print(
f'DEBUGGING OLD working directory contents: {out.stdout.decode("utf-8")}'
)
print(f'DEBUGGING Target JUPYTER pwd: {jupyter_pwd}')
if jupyter_pwd:
os.chdir(jupyter_pwd)
try:
return func(*args, **kwargs)
finally:
os.chdir(old_pwd)
return wrapper
def _is_valid_filename(file_name) -> bool:
if not file_name or not isinstance(file_name, str) or not file_name.strip():
return False
@ -240,7 +193,6 @@ def _cur_file_header(current_file, total_lines) -> str:
return f'[File: {os.path.abspath(current_file)} ({total_lines} lines total)]\n'
@update_pwd_decorator
def open_file(
path: str, line_number: int | None = 1, context_lines: int | None = WINDOW
) -> None:
@ -277,7 +229,6 @@ def open_file(
print(output)
@update_pwd_decorator
def goto_line(line_number: int) -> None:
"""Moves the window to show the specified line number.
@ -299,7 +250,6 @@ def goto_line(line_number: int) -> None:
print(output)
@update_pwd_decorator
def scroll_down() -> None:
"""Moves the window down by 100 lines.
@ -317,7 +267,6 @@ def scroll_down() -> None:
print(output)
@update_pwd_decorator
def scroll_up() -> None:
"""Moves the window up by 100 lines.
@ -335,7 +284,6 @@ def scroll_up() -> None:
print(output)
@update_pwd_decorator
def create_file(filename: str) -> None:
"""Creates and opens a new file with the given name.
@ -647,7 +595,6 @@ def _edit_file_impl(
return ret_str
@update_pwd_decorator
def edit_file_by_replace(file_name: str, to_replace: str, new_content: str) -> None:
"""Edit a file. This will search for `to_replace` in the given file and replace it with `new_content`.
@ -749,7 +696,6 @@ def edit_file_by_replace(file_name: str, to_replace: str, new_content: str) -> N
print(ret_str)
@update_pwd_decorator
def insert_content_at_line(file_name: str, line_number: int, content: str) -> None:
"""Insert content at the given line number in a file.
This will NOT modify the content of the lines before OR after the given line number.
@ -784,7 +730,6 @@ def insert_content_at_line(file_name: str, line_number: int, content: str) -> No
print(ret_str)
@update_pwd_decorator
def append_file(file_name: str, content: str) -> None:
"""Append content to the given file.
It appends text `content` to the end of the specified file.
@ -805,7 +750,6 @@ def append_file(file_name: str, content: str) -> None:
print(ret_str)
@update_pwd_decorator
def search_dir(search_term: str, dir_path: str = './') -> None:
"""Searches for search_term in all files in dir. If dir is not provided, searches in the current directory.
@ -845,7 +789,6 @@ def search_dir(search_term: str, dir_path: str = './') -> None:
print(f'[End of matches for "{search_term}" in {dir_path}]')
@update_pwd_decorator
def search_file(search_term: str, file_path: Optional[str] = None) -> None:
"""Searches for search_term in file. If file is not provided, searches in the current open file.
@ -878,7 +821,6 @@ def search_file(search_term: str, file_path: Optional[str] = None) -> None:
print(f'[No matches found for "{search_term}" in {file_path}]')
@update_pwd_decorator
def find_file(file_name: str, dir_path: str = './') -> None:
"""Finds all files with the given name in the specified directory.
@ -904,7 +846,6 @@ def find_file(file_name: str, dir_path: str = './') -> None:
print(f'[No matches found for "{file_name}" in {dir_path}]')
@update_pwd_decorator
def parse_pdf(file_path: str) -> None:
"""Parses the content of a PDF file and prints it.
@ -923,7 +864,6 @@ def parse_pdf(file_path: str) -> None:
print(text.strip())
@update_pwd_decorator
def parse_docx(file_path: str) -> None:
"""Parses the content of a DOCX file and prints it.
@ -938,7 +878,6 @@ def parse_docx(file_path: str) -> None:
print(text)
@update_pwd_decorator
def parse_latex(file_path: str) -> None:
"""Parses the content of a LaTex file and prints it.
@ -991,7 +930,6 @@ def _prepare_image_messages(task: str, base64_image: str):
]
@update_pwd_decorator
def parse_audio(file_path: str, model: str = 'whisper-1') -> None:
"""Parses the content of an audio file and prints it.
@ -1012,7 +950,6 @@ def parse_audio(file_path: str, model: str = 'whisper-1') -> None:
print(f'Error transcribing audio file: {e}')
@update_pwd_decorator
def parse_image(
file_path: str, task: str = 'Describe this image as detail as possible.'
) -> None:
@ -1038,7 +975,6 @@ def parse_image(
print(f'Error with the request: {error}')
@update_pwd_decorator
def parse_video(
file_path: str,
task: str = 'Describe this image as detail as possible.',
@ -1086,7 +1022,6 @@ def parse_video(
print(f'Error with the request: {error}')
@update_pwd_decorator
def parse_pptx(file_path: str) -> None:
"""Parses the content of a pptx file and prints it.

View File

@ -396,6 +396,8 @@ The server is running on port 5000 with PID 126. You can access the list of numb
NOW, LET'S START!
----------
Use Jupyter IPython to write a text file containing 'hello world' to '/workspace/test.txt'. Do not ask me for confirmation at any point.
ENVIRONMENT REMINDER: You have 14 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -412,5 +412,6 @@ with open('/workspace/test.txt', 'w') as file:
OBSERVATION:
[Code executed successfully with no output]
[Jupyter current working directory: /workspace]
ENVIRONMENT REMINDER: You have 13 turns left to complete the task. When finished reply with <finish></finish>
ENVIRONMENT REMINDER: You have 13 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -396,6 +396,8 @@ The server is running on port 5000 with PID 126. You can access the list of numb
NOW, LET'S START!
----------
Install and import pymsgbox==1.0.9 and print it's version in /workspace/test.txt. Do not ask me for confirmation at any point.
ENVIRONMENT REMINDER: You have 14 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -412,12 +412,12 @@ Sure! Let's start by installing the `pymsgbox` package.
OBSERVATION:
Collecting pymsgbox==1.0.9
Downloading PyMsgBox-1.0.9.tar.gz (18 kB)
Installing build dependencies ... [?25l- \ | / done
Installing build dependencies ... [?25l- \ | / - \ | / - \ | / - \ | / - done
[?25h Getting requirements to build wheel ... [?25l- done
[?25h Preparing metadata (pyproject.toml) ... [?25l- done
[?25hBuilding wheels for collected packages: pymsgbox
Building wheel for pymsgbox (pyproject.toml) ... [?25l- done
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=7cc4ff18827e3c2e2bd0780a92ca70fd625dc7645b994b6f3191dfb0660e606e
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=0aa91e03ed30fdcf42db3522fd0444c80951cccb215529a6ed9a95fc2b8a6f0c
Stored in directory: /home/opendevin/.cache/pip/wheels/85/92/63/e126ee5f33d8f2ed04f96e43ef5df7270a2f331848752e8662
Successfully built pymsgbox
Installing collected packages: pymsgbox
@ -426,6 +426,6 @@ Successfully installed pymsgbox-1.0.9
[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[Jupyter current working directory: /workspace]
ENVIRONMENT REMINDER: You have 13 turns left to complete the task. When finished reply with <finish></finish>
ENVIRONMENT REMINDER: You have 13 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -412,12 +412,12 @@ Sure! Let's start by installing the `pymsgbox` package.
OBSERVATION:
Collecting pymsgbox==1.0.9
Downloading PyMsgBox-1.0.9.tar.gz (18 kB)
Installing build dependencies ... [?25l- \ | / done
Installing build dependencies ... [?25l- \ | / - \ | / - \ | / - \ | / - done
[?25h Getting requirements to build wheel ... [?25l- done
[?25h Preparing metadata (pyproject.toml) ... [?25l- done
[?25hBuilding wheels for collected packages: pymsgbox
Building wheel for pymsgbox (pyproject.toml) ... [?25l- done
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=7cc4ff18827e3c2e2bd0780a92ca70fd625dc7645b994b6f3191dfb0660e606e
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=0aa91e03ed30fdcf42db3522fd0444c80951cccb215529a6ed9a95fc2b8a6f0c
Stored in directory: /home/opendevin/.cache/pip/wheels/85/92/63/e126ee5f33d8f2ed04f96e43ef5df7270a2f331848752e8662
Successfully built pymsgbox
Installing collected packages: pymsgbox
@ -426,7 +426,7 @@ Successfully installed pymsgbox-1.0.9
[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[Jupyter current working directory: /workspace]
----------
@ -444,5 +444,6 @@ with open('/workspace/test.txt', 'w') as file:
OBSERVATION:
[Code executed successfully with no output]
[Jupyter current working directory: /workspace]
ENVIRONMENT REMINDER: You have 12 turns left to complete the task. When finished reply with <finish></finish>
ENVIRONMENT REMINDER: You have 12 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -412,12 +412,12 @@ Sure! Let's start by installing the `pymsgbox` package.
OBSERVATION:
Collecting pymsgbox==1.0.9
Downloading PyMsgBox-1.0.9.tar.gz (18 kB)
Installing build dependencies ... [?25l- \ | / done
Installing build dependencies ... [?25l- \ | / - \ | / - \ | / - \ | / - done
[?25h Getting requirements to build wheel ... [?25l- done
[?25h Preparing metadata (pyproject.toml) ... [?25l- done
[?25hBuilding wheels for collected packages: pymsgbox
Building wheel for pymsgbox (pyproject.toml) ... [?25l- done
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=7cc4ff18827e3c2e2bd0780a92ca70fd625dc7645b994b6f3191dfb0660e606e
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=0aa91e03ed30fdcf42db3522fd0444c80951cccb215529a6ed9a95fc2b8a6f0c
Stored in directory: /home/opendevin/.cache/pip/wheels/85/92/63/e126ee5f33d8f2ed04f96e43ef5df7270a2f331848752e8662
Successfully built pymsgbox
Installing collected packages: pymsgbox
@ -426,7 +426,7 @@ Successfully installed pymsgbox-1.0.9
[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[Jupyter current working directory: /workspace]
----------
@ -444,6 +444,7 @@ with open('/workspace/test.txt', 'w') as file:
OBSERVATION:
[Code executed successfully with no output]
[Jupyter current working directory: /workspace]
----------
@ -460,4 +461,4 @@ pymsgbox version: 1.0.9
opendevin@docker-desktop:/workspace $
[Command -1 finished with exit code 0]
ENVIRONMENT REMINDER: You have 11 turns left to complete the task. When finished reply with <finish></finish>
ENVIRONMENT REMINDER: You have 11 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -603,5 +603,6 @@ with open('/workspace/test.txt', 'w') as file:
OBSERVATION:
[Code executed successfully with no output]
[Jupyter current working directory: /workspace]
ENVIRONMENT REMINDER: You have 13 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -603,12 +603,12 @@ Understood. Let's start by installing the `pymsgbox` package.
OBSERVATION:
Collecting pymsgbox==1.0.9
Downloading PyMsgBox-1.0.9.tar.gz (18 kB)
Installing build dependencies ... [?25l- \ | / - \ | / done
Installing build dependencies ... [?25l- \ | / - \ | / - \ | / - \ | / - \ done
[?25h Getting requirements to build wheel ... [?25l- done
[?25h Preparing metadata (pyproject.toml) ... [?25l- done
[?25hBuilding wheels for collected packages: pymsgbox
Building wheel for pymsgbox (pyproject.toml) ... [?25l- done
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=e504d1b40c05b7da59bba1f908d23edcd98381d8e0ecc41a1162745ee4ee6fd2
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=e6c344aecd9e7b02d3ff2bb4d98a74d0fe6156b5f523d40b402350da7aac55e6
Stored in directory: /home/opendevin/.cache/pip/wheels/85/92/63/e126ee5f33d8f2ed04f96e43ef5df7270a2f331848752e8662
Successfully built pymsgbox
Installing collected packages: pymsgbox
@ -617,6 +617,6 @@ Successfully installed pymsgbox-1.0.9
[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[Jupyter current working directory: /workspace]
ENVIRONMENT REMINDER: You have 13 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -603,12 +603,12 @@ Understood. Let's start by installing the `pymsgbox` package.
OBSERVATION:
Collecting pymsgbox==1.0.9
Downloading PyMsgBox-1.0.9.tar.gz (18 kB)
Installing build dependencies ... [?25l- \ | / - \ | / done
Installing build dependencies ... [?25l- \ | / - \ | / - \ | / - \ | / - \ done
[?25h Getting requirements to build wheel ... [?25l- done
[?25h Preparing metadata (pyproject.toml) ... [?25l- done
[?25hBuilding wheels for collected packages: pymsgbox
Building wheel for pymsgbox (pyproject.toml) ... [?25l- done
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=e504d1b40c05b7da59bba1f908d23edcd98381d8e0ecc41a1162745ee4ee6fd2
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=e6c344aecd9e7b02d3ff2bb4d98a74d0fe6156b5f523d40b402350da7aac55e6
Stored in directory: /home/opendevin/.cache/pip/wheels/85/92/63/e126ee5f33d8f2ed04f96e43ef5df7270a2f331848752e8662
Successfully built pymsgbox
Installing collected packages: pymsgbox
@ -617,7 +617,7 @@ Successfully installed pymsgbox-1.0.9
[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[Jupyter current working directory: /workspace]
----------
@ -637,5 +637,6 @@ version_info
OBSERVATION:
'1.0.9'
[Jupyter current working directory: /workspace]
ENVIRONMENT REMINDER: You have 12 turns left to complete the task. When finished reply with <finish></finish>.

View File

@ -603,12 +603,12 @@ Understood. Let's start by installing the `pymsgbox` package.
OBSERVATION:
Collecting pymsgbox==1.0.9
Downloading PyMsgBox-1.0.9.tar.gz (18 kB)
Installing build dependencies ... [?25l- \ | / - \ | / done
Installing build dependencies ... [?25l- \ | / - \ | / - \ | / - \ | / - \ done
[?25h Getting requirements to build wheel ... [?25l- done
[?25h Preparing metadata (pyproject.toml) ... [?25l- done
[?25hBuilding wheels for collected packages: pymsgbox
Building wheel for pymsgbox (pyproject.toml) ... [?25l- done
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=e504d1b40c05b7da59bba1f908d23edcd98381d8e0ecc41a1162745ee4ee6fd2
[?25h Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-py3-none-any.whl size=7406 sha256=e6c344aecd9e7b02d3ff2bb4d98a74d0fe6156b5f523d40b402350da7aac55e6
Stored in directory: /home/opendevin/.cache/pip/wheels/85/92/63/e126ee5f33d8f2ed04f96e43ef5df7270a2f331848752e8662
Successfully built pymsgbox
Installing collected packages: pymsgbox
@ -617,7 +617,7 @@ Successfully installed pymsgbox-1.0.9
[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[Jupyter current working directory: /workspace]
----------
@ -637,6 +637,7 @@ version_info
OBSERVATION:
'1.0.9'
[Jupyter current working directory: /workspace]
----------

View File

@ -303,7 +303,10 @@ async def test_simple_cmd_ipython_and_fileop(temp_dir, box_class, run_as_devin):
assert isinstance(obs, IPythonRunCellObservation)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert obs.content.strip() == 'Hello, `World`!'
assert (
obs.content.strip()
== 'Hello, `World`!\n[Jupyter current working directory: /workspace]'
)
# Test read file (file should not exist)
action_read = FileReadAction(path='hello.sh')
@ -768,7 +771,10 @@ async def test_ipython_multi_user(temp_dir, box_class, run_as_devin):
obs = await runtime.run_action(action_ipython)
assert isinstance(obs, IPythonRunCellObservation)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert obs.content.strip() == '/workspace'
assert (
obs.content.strip()
== '/workspace\n[Jupyter current working directory: /workspace]'
)
# write a file
test_code = "with open('test.txt', 'w') as f: f.write('Hello, world!')"
@ -777,7 +783,10 @@ async def test_ipython_multi_user(temp_dir, box_class, run_as_devin):
obs = await runtime.run_action(action_ipython)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert isinstance(obs, IPythonRunCellObservation)
assert obs.content.strip() == '[Code executed successfully with no output]'
assert (
obs.content.strip()
== '[Code executed successfully with no output]\n[Jupyter current working directory: /workspace]'
)
# check file owner via bash
action = CmdRunAction(command='ls -alh test.txt')
@ -816,7 +825,7 @@ async def test_ipython_simple(temp_dir, box_class):
obs = await runtime.run_action(action_ipython)
assert isinstance(obs, IPythonRunCellObservation)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert obs.content.strip() == '1'
assert obs.content.strip() == '1\n[Jupyter current working directory: /workspace]'
await runtime.close()
await asyncio.sleep(1)
@ -850,6 +859,7 @@ async def _test_ipython_agentskills_fileop_pwd_impl(
'1|\n'
'(this is the end of the file)\n'
'[File hello.py created.]\n'
'[Jupyter current working directory: /workspace]'
).strip().split('\n')
action = CmdRunAction(command='cd test')
@ -872,6 +882,7 @@ async def _test_ipython_agentskills_fileop_pwd_impl(
'1|\n'
'(this is the end of the file)\n'
'[File hello.py created.]\n'
'[Jupyter current working directory: /workspace/test]'
).strip().split('\n')
if enable_auto_lint:
@ -904,6 +915,7 @@ ERRORS:
Your changes have NOT been applied. Please fix your edit command and try again.
You either need to 1) Specify the correct start/end line arguments or 2) Correct your edit code.
DO NOT re-run the same failed edit command. Running it again will lead to the same error.
[Jupyter current working directory: /workspace/test]
"""
).strip().split('\n')
@ -922,6 +934,7 @@ DO NOT re-run the same failed edit command. Running it again will lead to the sa
1|print("hello world")
(this is the end of the file)
[File updated (edited at line 1). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]
[Jupyter current working directory: /workspace/test]
"""
).strip().split('\n')
@ -988,6 +1001,7 @@ async def test_ipython_agentskills_fileop_pwd_with_userdir(temp_dir, box_class):
'1|\n'
'(this is the end of the file)\n'
'[File hello.py created.]\n'
'[Jupyter current working directory: /root]'
).strip().split('\n')
action = CmdRunAction(command='cd test')
@ -1010,6 +1024,7 @@ async def test_ipython_agentskills_fileop_pwd_with_userdir(temp_dir, box_class):
'1|\n'
'(this is the end of the file)\n'
'[File hello.py created.]\n'
'[Jupyter current working directory: /root/test]'
).strip().split('\n')
await runtime.close()
@ -1073,7 +1088,10 @@ async def test_ipython_package_install(temp_dir, box_class, run_as_devin):
obs = await runtime.run_action(action)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
# import should not error out
assert obs.content.strip() == '[Code executed successfully with no output]'
assert (
obs.content.strip()
== '[Code executed successfully with no output]\n[Jupyter current working directory: /workspace]'
)
await runtime.close()
await asyncio.sleep(1)