mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(backend/copilot): return sandbox path from bridge, inform model of copy location
Address CodeRabbit review: _bridge_to_sandbox now returns the sandbox path (or None on failure) so callers can append "[Sandbox copy available at /tmp/file.json]" to the Read result. This gives the model explicit feedback about where to find the file in the sandbox, instead of silently bridging with no indication.
This commit is contained in:
@@ -141,7 +141,11 @@ async def _handle_read_file(args: dict[str, Any]) -> dict[str, Any]:
|
||||
if not result.get("isError"):
|
||||
sandbox = _get_sandbox()
|
||||
if sandbox is not None:
|
||||
await _bridge_to_sandbox(sandbox, file_path, offset, limit)
|
||||
bridged = await _bridge_to_sandbox(sandbox, file_path, offset, limit)
|
||||
if bridged:
|
||||
result["content"][0][
|
||||
"text"
|
||||
] += f"\n[Sandbox copy available at {bridged}]"
|
||||
return result
|
||||
|
||||
result = _get_sandbox_and_path(file_path)
|
||||
@@ -321,7 +325,7 @@ _BRIDGE_SKIP_BYTES = 50 * 1024 * 1024 # 50 MB
|
||||
|
||||
async def _bridge_to_sandbox(
|
||||
sandbox: Any, file_path: str, offset: int, limit: int
|
||||
) -> None:
|
||||
) -> str | None:
|
||||
"""Best-effort copy of a host-side SDK file into the E2B sandbox.
|
||||
|
||||
When the model reads an SDK-internal file (e.g. tool-results), it often
|
||||
@@ -331,6 +335,8 @@ async def _bridge_to_sandbox(
|
||||
Only copies when offset=0 and limit is large enough to indicate the model
|
||||
wants the full file. Errors are logged but never propagated.
|
||||
|
||||
Returns the sandbox path on success, or ``None`` on skip/failure.
|
||||
|
||||
Size handling:
|
||||
- <= 5 MB: written to ``/tmp/<basename>`` via shell base64 (``_sandbox_write``).
|
||||
- 5-50 MB: written to ``/home/user/<basename>`` via ``sandbox.files.write()``
|
||||
@@ -338,7 +344,7 @@ async def _bridge_to_sandbox(
|
||||
- > 50 MB: skipped entirely with a warning.
|
||||
"""
|
||||
if offset != 0 or limit < 2000:
|
||||
return
|
||||
return None
|
||||
basename = os.path.basename(file_path)
|
||||
try:
|
||||
expanded = os.path.realpath(os.path.expanduser(file_path))
|
||||
@@ -365,12 +371,14 @@ async def _bridge_to_sandbox(
|
||||
logger.info(
|
||||
"[E2B] Bridged SDK file to sandbox: %s -> %s", basename, sandbox_path
|
||||
)
|
||||
return sandbox_path
|
||||
except Exception:
|
||||
logger.debug(
|
||||
"[E2B] Failed to bridge SDK file to sandbox: %s",
|
||||
basename,
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
# Local read (for SDK-internal paths)
|
||||
|
||||
@@ -380,8 +380,9 @@ class TestBridgeToSandbox:
|
||||
f.write_text('{"ok": true}')
|
||||
sandbox = _make_bridge_sandbox()
|
||||
|
||||
await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
result = await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
|
||||
assert result == "/tmp/result.json"
|
||||
sandbox.commands.run.assert_called_once()
|
||||
cmd = sandbox.commands.run.call_args[0][0]
|
||||
assert "result.json" in cmd
|
||||
@@ -394,8 +395,9 @@ class TestBridgeToSandbox:
|
||||
f.write_text("content")
|
||||
sandbox = _make_bridge_sandbox()
|
||||
|
||||
await _bridge_to_sandbox(sandbox, str(f), offset=10, limit=2000)
|
||||
result = await _bridge_to_sandbox(sandbox, str(f), offset=10, limit=2000)
|
||||
|
||||
assert result is None
|
||||
sandbox.commands.run.assert_not_called()
|
||||
sandbox.files.write.assert_not_called()
|
||||
|
||||
@@ -424,14 +426,16 @@ class TestBridgeToSandbox:
|
||||
sandbox.files.write.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sandbox_write_failure_does_not_raise(self, tmp_path):
|
||||
"""If sandbox write fails, the error is swallowed (best-effort)."""
|
||||
async def test_sandbox_write_failure_returns_none(self, tmp_path):
|
||||
"""If sandbox write fails, returns None (best-effort)."""
|
||||
f = tmp_path / "data.txt"
|
||||
f.write_text("content")
|
||||
sandbox = _make_bridge_sandbox()
|
||||
sandbox.commands.run.side_effect = RuntimeError("E2B timeout")
|
||||
|
||||
await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
result = await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_large_file_uses_files_api(self, tmp_path):
|
||||
@@ -440,8 +444,9 @@ class TestBridgeToSandbox:
|
||||
f.write_bytes(b"x" * (_BRIDGE_SHELL_MAX_BYTES + 1))
|
||||
sandbox = _make_bridge_sandbox()
|
||||
|
||||
await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
result = await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
|
||||
assert result == "/home/user/big.json"
|
||||
sandbox.files.write.assert_called_once()
|
||||
call_args = sandbox.files.write.call_args[0]
|
||||
assert call_args[0] == "/home/user/big.json"
|
||||
@@ -457,7 +462,9 @@ class TestBridgeToSandbox:
|
||||
fh.write(b"\0")
|
||||
sandbox = _make_bridge_sandbox()
|
||||
|
||||
await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
result = await _bridge_to_sandbox(sandbox, str(f), offset=0, limit=2000)
|
||||
|
||||
assert result is None
|
||||
|
||||
sandbox.commands.run.assert_not_called()
|
||||
sandbox.files.write.assert_not_called()
|
||||
|
||||
@@ -390,10 +390,13 @@ async def _read_file_handler(args: dict[str, Any]) -> dict[str, Any]:
|
||||
#
|
||||
# When E2B is active, also copy the file into the sandbox so
|
||||
# bash_exec can process it (the model often uses Read then bash).
|
||||
text = "".join(selected)
|
||||
sandbox = _current_sandbox.get(None)
|
||||
if sandbox is not None:
|
||||
await _bridge_to_sandbox(sandbox, file_path, offset, limit)
|
||||
return _mcp_ok("".join(selected))
|
||||
bridged = await _bridge_to_sandbox(sandbox, file_path, offset, limit)
|
||||
if bridged:
|
||||
text += f"\n[Sandbox copy available at {bridged}]"
|
||||
return _mcp_ok(text)
|
||||
except FileNotFoundError:
|
||||
return _mcp_err(f"File not found: {file_path}")
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user