Spaces:
Runtime error
Runtime error
Merge pull request #41 from EnvisionMindCa/codex/fix-terminal-tool-not-returning-output-after-background-exec
Browse files- README.md +3 -3
- src/tools.py +13 -9
- src/vm.py +5 -5
README.md
CHANGED
|
@@ -9,9 +9,9 @@ conversations can be resumed with context. One example tool is included:
|
|
| 9 |
with network access. Use it to read uploaded documents under ``/data``, fetch
|
| 10 |
web content via tools like ``curl`` or run any other commands. The assistant
|
| 11 |
must invoke this tool to search online when unsure about a response. Output
|
| 12 |
-
from ``stdout`` and ``stderr`` is captured when
|
| 13 |
-
|
| 14 |
-
|
| 15 |
The VM is created when a chat session starts and reused for all subsequent
|
| 16 |
tool calls.
|
| 17 |
|
|
|
|
| 9 |
with network access. Use it to read uploaded documents under ``/data``, fetch
|
| 10 |
web content via tools like ``curl`` or run any other commands. The assistant
|
| 11 |
must invoke this tool to search online when unsure about a response. Output
|
| 12 |
+
from ``stdout`` and ``stderr`` is captured when each command finishes.
|
| 13 |
+
Execution happens asynchronously so the assistant can continue responding
|
| 14 |
+
while the command runs.
|
| 15 |
The VM is created when a chat session starts and reused for all subsequent
|
| 16 |
tool calls.
|
| 17 |
|
src/tools.py
CHANGED
|
@@ -27,29 +27,33 @@ def execute_terminal(command: str) -> str:
|
|
| 27 |
The assistant must call this tool to search the internet whenever unsure
|
| 28 |
about any detail.
|
| 29 |
|
| 30 |
-
The command is executed with network access enabled.
|
| 31 |
-
|
| 32 |
-
|
|
|
|
| 33 |
"""
|
| 34 |
if not command:
|
| 35 |
return "No command provided."
|
| 36 |
|
| 37 |
if _VM:
|
| 38 |
try:
|
| 39 |
-
return _VM.execute(command,
|
| 40 |
except Exception as exc: # pragma: no cover - unforeseen errors
|
| 41 |
return f"Failed to execute command in VM: {exc}"
|
| 42 |
|
| 43 |
try:
|
| 44 |
-
subprocess.
|
| 45 |
command,
|
| 46 |
shell=True,
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
start_new_session=True,
|
| 50 |
env=os.environ.copy(),
|
|
|
|
| 51 |
)
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
| 53 |
except Exception as exc: # pragma: no cover - unforeseen errors
|
| 54 |
return f"Failed to execute command: {exc}"
|
| 55 |
|
|
|
|
| 27 |
The assistant must call this tool to search the internet whenever unsure
|
| 28 |
about any detail.
|
| 29 |
|
| 30 |
+
The command is executed with network access enabled. Output from
|
| 31 |
+
``stdout`` and ``stderr`` is captured when the command completes.
|
| 32 |
+
Execution happens asynchronously so the assistant can continue
|
| 33 |
+
responding while the command runs.
|
| 34 |
"""
|
| 35 |
if not command:
|
| 36 |
return "No command provided."
|
| 37 |
|
| 38 |
if _VM:
|
| 39 |
try:
|
| 40 |
+
return _VM.execute(command, timeout=None)
|
| 41 |
except Exception as exc: # pragma: no cover - unforeseen errors
|
| 42 |
return f"Failed to execute command in VM: {exc}"
|
| 43 |
|
| 44 |
try:
|
| 45 |
+
completed = subprocess.run(
|
| 46 |
command,
|
| 47 |
shell=True,
|
| 48 |
+
capture_output=True,
|
| 49 |
+
text=True,
|
|
|
|
| 50 |
env=os.environ.copy(),
|
| 51 |
+
timeout=None,
|
| 52 |
)
|
| 53 |
+
output = completed.stdout
|
| 54 |
+
if completed.stderr:
|
| 55 |
+
output = f"{output}\n{completed.stderr}" if output else completed.stderr
|
| 56 |
+
return output.strip()
|
| 57 |
except Exception as exc: # pragma: no cover - unforeseen errors
|
| 58 |
return f"Failed to execute command: {exc}"
|
| 59 |
|
src/vm.py
CHANGED
|
@@ -68,7 +68,7 @@ class LinuxVM:
|
|
| 68 |
raise RuntimeError(f"Failed to start VM: {exc}") from exc
|
| 69 |
|
| 70 |
def execute(
|
| 71 |
-
self, command: str, *, timeout: int = 3, detach: bool = False
|
| 72 |
) -> str:
|
| 73 |
"""Execute a command inside the running VM.
|
| 74 |
|
|
@@ -77,8 +77,8 @@ class LinuxVM:
|
|
| 77 |
command:
|
| 78 |
The shell command to run inside the container.
|
| 79 |
timeout:
|
| 80 |
-
Maximum time in seconds to wait for completion.
|
| 81 |
-
``detach`` is ``True``.
|
| 82 |
detach:
|
| 83 |
Run the command in the background without waiting for it to finish.
|
| 84 |
"""
|
|
@@ -105,7 +105,7 @@ class LinuxVM:
|
|
| 105 |
cmd,
|
| 106 |
capture_output=True,
|
| 107 |
text=True,
|
| 108 |
-
timeout=None if detach else timeout,
|
| 109 |
)
|
| 110 |
except subprocess.TimeoutExpired as exc:
|
| 111 |
return f"Command timed out after {timeout}s: {exc.cmd}"
|
|
@@ -118,7 +118,7 @@ class LinuxVM:
|
|
| 118 |
return output.strip()
|
| 119 |
|
| 120 |
async def execute_async(
|
| 121 |
-
self, command: str, *, timeout: int = 3, detach: bool = False
|
| 122 |
) -> str:
|
| 123 |
"""Asynchronously execute ``command`` inside the running VM."""
|
| 124 |
loop = asyncio.get_running_loop()
|
|
|
|
| 68 |
raise RuntimeError(f"Failed to start VM: {exc}") from exc
|
| 69 |
|
| 70 |
def execute(
|
| 71 |
+
self, command: str, *, timeout: int | None = 3, detach: bool = False
|
| 72 |
) -> str:
|
| 73 |
"""Execute a command inside the running VM.
|
| 74 |
|
|
|
|
| 77 |
command:
|
| 78 |
The shell command to run inside the container.
|
| 79 |
timeout:
|
| 80 |
+
Maximum time in seconds to wait for completion. Set to ``None``
|
| 81 |
+
to wait indefinitely. Ignored when ``detach`` is ``True``.
|
| 82 |
detach:
|
| 83 |
Run the command in the background without waiting for it to finish.
|
| 84 |
"""
|
|
|
|
| 105 |
cmd,
|
| 106 |
capture_output=True,
|
| 107 |
text=True,
|
| 108 |
+
timeout=None if detach or timeout is None else timeout,
|
| 109 |
)
|
| 110 |
except subprocess.TimeoutExpired as exc:
|
| 111 |
return f"Command timed out after {timeout}s: {exc.cmd}"
|
|
|
|
| 118 |
return output.strip()
|
| 119 |
|
| 120 |
async def execute_async(
|
| 121 |
+
self, command: str, *, timeout: int | None = 3, detach: bool = False
|
| 122 |
) -> str:
|
| 123 |
"""Asynchronously execute ``command`` inside the running VM."""
|
| 124 |
loop = asyncio.get_running_loop()
|