Skip to content

Commit 90142bc

Browse files
authored
fix: ensure close aiodocker.Docker() (#4251)
* fix: ensure close aiodocker.Docker() * fix: code formatted
1 parent 79d0487 commit 90142bc

File tree

1 file changed

+130
-131
lines changed
  • astrbot/builtin_stars/python_interpreter

1 file changed

+130
-131
lines changed

astrbot/builtin_stars/python_interpreter/main.py

Lines changed: 130 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,8 @@ async def file_upload(self, file_path: str):
157157
async def is_docker_available(self) -> bool:
158158
"""Check if docker is available"""
159159
try:
160-
docker = aiodocker.Docker()
161-
await docker.version()
162-
await docker.close()
160+
async with aiodocker.Docker() as docker:
161+
await docker.version()
163162
return True
164163
except BaseException as e:
165164
logger.info(f"检查 Docker 可用性: {e}")
@@ -279,14 +278,14 @@ async def pi_mirror(self, event: AstrMessageEvent, url: str = ""):
279278
@pi.command("repull")
280279
async def pi_repull(self, event: AstrMessageEvent):
281280
"""重新拉取沙箱镜像"""
282-
docker = aiodocker.Docker()
283-
image_name = await self.get_image_name()
284-
try:
285-
await docker.images.get(image_name)
286-
await docker.images.delete(image_name, force=True)
287-
except aiodocker.exceptions.DockerError:
288-
pass
289-
await docker.images.pull(image_name)
281+
async with aiodocker.Docker() as docker:
282+
image_name = await self.get_image_name()
283+
try:
284+
await docker.images.get(image_name)
285+
await docker.images.delete(image_name, force=True)
286+
except aiodocker.exceptions.DockerError:
287+
pass
288+
await docker.images.pull(image_name)
290289
yield event.plain_result("重新拉取沙箱镜像成功。")
291290

292291
@pi.command("file")
@@ -371,137 +370,137 @@ async def python_interpreter(self, event: AstrMessageEvent):
371370
obs = ""
372371
n = 5
373372

374-
for i in range(n):
375-
if i > 0:
376-
logger.info(f"Try {i + 1}/{n}")
377-
378-
PROMPT_ = PROMPT.format(
379-
prompt=plain_text,
380-
extra_input=extra_inputs,
381-
extra_prompt=obs,
382-
)
383-
provider = self.context.get_using_provider()
384-
llm_response = await provider.text_chat(
385-
prompt=PROMPT_,
386-
session_id=f"{event.session_id}_{magic_code}_{i!s}",
387-
)
388-
389-
logger.debug(
390-
"code interpreter llm gened code:" + llm_response.completion_text,
391-
)
392-
393-
# 整理代码并保存
394-
code_clean = await self.tidy_code(llm_response.completion_text)
395-
with open(os.path.join(workplace_path, "exec.py"), "w") as f:
396-
f.write(code_clean)
397-
398-
# 启动容器
399-
docker = aiodocker.Docker()
400-
401-
# 检查有没有image
402-
image_name = await self.get_image_name()
403-
try:
404-
await docker.images.get(image_name)
405-
except aiodocker.exceptions.DockerError:
406-
# 拉取镜像
407-
logger.info(f"未找到沙箱镜像,正在尝试拉取 {image_name}...")
408-
await docker.images.pull(image_name)
373+
async with aiodocker.Docker() as docker:
374+
for i in range(n):
375+
if i > 0:
376+
logger.info(f"Try {i + 1}/{n}")
409377

410-
yield event.plain_result(
411-
f"使用沙箱执行代码中,请稍等...(尝试次数: {i + 1}/{n})",
412-
)
378+
PROMPT_ = PROMPT.format(
379+
prompt=plain_text,
380+
extra_input=extra_inputs,
381+
extra_prompt=obs,
382+
)
383+
provider = self.context.get_using_provider()
384+
llm_response = await provider.text_chat(
385+
prompt=PROMPT_,
386+
session_id=f"{event.session_id}_{magic_code}_{i!s}",
387+
)
413388

414-
self.docker_host_astrbot_abs_path = self.config.get(
415-
"docker_host_astrbot_abs_path",
416-
"",
417-
)
418-
if self.docker_host_astrbot_abs_path:
419-
host_shared = os.path.join(
420-
self.docker_host_astrbot_abs_path,
421-
self.shared_path,
389+
logger.debug(
390+
"code interpreter llm gened code:" + llm_response.completion_text,
422391
)
423-
host_output = os.path.join(
424-
self.docker_host_astrbot_abs_path,
425-
output_path,
392+
393+
# 整理代码并保存
394+
code_clean = await self.tidy_code(llm_response.completion_text)
395+
with open(os.path.join(workplace_path, "exec.py"), "w") as f:
396+
f.write(code_clean)
397+
398+
# 检查有没有image
399+
image_name = await self.get_image_name()
400+
try:
401+
await docker.images.get(image_name)
402+
except aiodocker.exceptions.DockerError:
403+
# 拉取镜像
404+
logger.info(f"未找到沙箱镜像,正在尝试拉取 {image_name}...")
405+
await docker.images.pull(image_name)
406+
407+
yield event.plain_result(
408+
f"使用沙箱执行代码中,请稍等...(尝试次数: {i + 1}/{n})",
426409
)
427-
host_workplace = os.path.join(
428-
self.docker_host_astrbot_abs_path,
429-
workplace_path,
410+
411+
self.docker_host_astrbot_abs_path = self.config.get(
412+
"docker_host_astrbot_abs_path",
413+
"",
430414
)
415+
if self.docker_host_astrbot_abs_path:
416+
host_shared = os.path.join(
417+
self.docker_host_astrbot_abs_path,
418+
self.shared_path,
419+
)
420+
host_output = os.path.join(
421+
self.docker_host_astrbot_abs_path,
422+
output_path,
423+
)
424+
host_workplace = os.path.join(
425+
self.docker_host_astrbot_abs_path,
426+
workplace_path,
427+
)
431428

432-
else:
433-
host_shared = os.path.abspath(self.shared_path)
434-
host_output = os.path.abspath(output_path)
435-
host_workplace = os.path.abspath(workplace_path)
429+
else:
430+
host_shared = os.path.abspath(self.shared_path)
431+
host_output = os.path.abspath(output_path)
432+
host_workplace = os.path.abspath(workplace_path)
436433

437-
logger.debug(
438-
f"host_shared: {host_shared}, host_output: {host_output}, host_workplace: {host_workplace}",
439-
)
434+
logger.debug(
435+
f"host_shared: {host_shared}, host_output: {host_output}, host_workplace: {host_workplace}",
436+
)
440437

441-
container = await docker.containers.run(
442-
{
443-
"Image": image_name,
444-
"Cmd": ["python", "exec.py"],
445-
"Memory": 512 * 1024 * 1024,
446-
"NanoCPUs": 1000000000,
447-
"HostConfig": {
448-
"Binds": [
449-
f"{host_shared}:/astrbot_sandbox/shared:ro",
450-
f"{host_output}:/astrbot_sandbox/output:rw",
451-
f"{host_workplace}:/astrbot_sandbox:rw",
452-
],
438+
container = await docker.containers.run(
439+
{
440+
"Image": image_name,
441+
"Cmd": ["python", "exec.py"],
442+
"Memory": 512 * 1024 * 1024,
443+
"NanoCPUs": 1000000000,
444+
"HostConfig": {
445+
"Binds": [
446+
f"{host_shared}:/astrbot_sandbox/shared:ro",
447+
f"{host_output}:/astrbot_sandbox/output:rw",
448+
f"{host_workplace}:/astrbot_sandbox:rw",
449+
],
450+
},
451+
"Env": [f"MAGIC_CODE={magic_code}"],
452+
"AutoRemove": True,
453453
},
454-
"Env": [f"MAGIC_CODE={magic_code}"],
455-
"AutoRemove": True,
456-
},
457-
)
454+
)
458455

459-
logger.debug(f"Container {container.id} created.")
460-
logs = await self.run_container(container)
461-
462-
logger.debug(f"Container {container.id} finished.")
463-
logger.debug(f"Container {container.id} logs: {logs}")
464-
465-
# 发送结果
466-
pattern = r"\[ASTRBOT_(TEXT|IMAGE|FILE)_OUTPUT#\w+\]: (.*)"
467-
ok = False
468-
traceback = ""
469-
for idx, log in enumerate(logs):
470-
match = re.match(pattern, log)
471-
if match:
472-
ok = True
473-
if match.group(1) == "TEXT":
474-
yield event.plain_result(match.group(2))
475-
elif match.group(1) == "IMAGE":
476-
image_path = os.path.join(workplace_path, match.group(2))
477-
logger.debug(f"Sending image: {image_path}")
478-
yield event.image_result(image_path)
479-
elif match.group(1) == "FILE":
480-
file_path = os.path.join(workplace_path, match.group(2))
481-
# logger.debug(f"Sending file: {file_path}")
482-
# file_s3_url = await self.file_upload(file_path)
483-
# logger.info(f"文件上传到 AstrBot 云节点: {file_s3_url}")
484-
file_name = os.path.basename(file_path)
485-
chain: list[BaseMessageComponent] = [
486-
File(name=file_name, file=file_path)
487-
]
488-
yield event.set_result(MessageEventResult(chain=chain))
489-
490-
elif "Traceback (most recent call last)" in log or "[Error]: " in log:
491-
traceback = "\n".join(logs[idx:])
492-
493-
if not ok:
494-
if traceback:
495-
obs = f"## Observation \n When execute the code: ```python\n{code_clean}\n```\n\n Error occurred:\n\n{traceback}\n Need to improve/fix the code."
456+
logger.debug(f"Container {container.id} created.")
457+
logs = await self.run_container(container)
458+
459+
logger.debug(f"Container {container.id} finished.")
460+
logger.debug(f"Container {container.id} logs: {logs}")
461+
462+
# 发送结果
463+
pattern = r"\[ASTRBOT_(TEXT|IMAGE|FILE)_OUTPUT#\w+\]: (.*)"
464+
ok = False
465+
traceback = ""
466+
for idx, log in enumerate(logs):
467+
match = re.match(pattern, log)
468+
if match:
469+
ok = True
470+
if match.group(1) == "TEXT":
471+
yield event.plain_result(match.group(2))
472+
elif match.group(1) == "IMAGE":
473+
image_path = os.path.join(workplace_path, match.group(2))
474+
logger.debug(f"Sending image: {image_path}")
475+
yield event.image_result(image_path)
476+
elif match.group(1) == "FILE":
477+
file_path = os.path.join(workplace_path, match.group(2))
478+
# logger.debug(f"Sending file: {file_path}")
479+
# file_s3_url = await self.file_upload(file_path)
480+
# logger.info(f"文件上传到 AstrBot 云节点: {file_s3_url}")
481+
file_name = os.path.basename(file_path)
482+
chain: list[BaseMessageComponent] = [
483+
File(name=file_name, file=file_path)
484+
]
485+
yield event.set_result(MessageEventResult(chain=chain))
486+
487+
elif (
488+
"Traceback (most recent call last)" in log or "[Error]: " in log
489+
):
490+
traceback = "\n".join(logs[idx:])
491+
492+
if not ok:
493+
if traceback:
494+
obs = f"## Observation \n When execute the code: ```python\n{code_clean}\n```\n\n Error occurred:\n\n{traceback}\n Need to improve/fix the code."
495+
else:
496+
logger.warning(
497+
f"未从沙箱输出中捕获到合法的输出。沙箱输出日志: {logs}",
498+
)
499+
break
496500
else:
497-
logger.warning(
498-
f"未从沙箱输出中捕获到合法的输出。沙箱输出日志: {logs}",
499-
)
500-
break
501-
else:
502-
# 成功了
503-
self.user_file_msg_buffer.pop(event.get_session_id())
504-
return
501+
# 成功了
502+
self.user_file_msg_buffer.pop(event.get_session_id())
503+
return
505504

506505
yield event.plain_result(
507506
"经过多次尝试后,未从沙箱输出中捕获到合法的输出,请更换问法或者查看日志。",

0 commit comments

Comments
 (0)