feat(tools): add OpenSandbox sandbox tool#5756
Conversation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR introduces three new OpenSandbox-backed tools for the crewai-tools library: a base class managing sandbox lifecycle (ephemeral, persistent, or external), an execution tool for running shell commands, and a filesystem tool supporting read/write/append/list/delete/mkdir/info operations. The opensandbox SDK is added as an optional dependency, and all tools are exported through the package hierarchy. ChangesOpenSandbox Tool Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py (2)
146-162: 💤 Low valueAppend is non-atomic (read-modify-write).
The append operation reads the entire file, concatenates, and writes back. For large files this is memory-intensive, and concurrent appends could race. This is acceptable for the documented use case (chunked file uploads), but worth noting in the docstring.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py` around lines 146 - 162, The _append method performs a read-modify-write (sandbox.files.read_bytes -> concat -> sandbox.files.write_file) which is non-atomic and can be memory-intensive or race-prone for large files or concurrent appends; update the method's docstring (near _append in open_sandbox_file_tool.py) to explicitly state this limitation and recommend its intended usage (chunked uploads) and/or mention that callers should handle concurrency or use a more atomic mechanism if needed, referencing _ensure_parent_dir, sandbox.files.read_bytes, and sandbox.files.write_file so readers can locate the related operations.
4-4: 💤 Low valueSame unnecessary
builtinsimport as in exec_tool.See comment on
open_sandbox_exec_tool.py— this import can be removed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py` at line 4, Remove the unnecessary import "from builtins import type as type_" from open_sandbox_file_tool.py; if the module references the alias type_ (search for "type_"), replace those uses with the built-in type or appropriate isinstance/type calls (e.g., use type(...) or isinstance(..., type)) and delete the import line entirely.lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py (1)
3-3: 💤 Low valueUnnecessary import from
builtins.
from builtins import type as type_is redundant —typeis already a builtin available in all Python versions. For the type annotation on line 39, you can usetype[BaseModel]directly (Python 3.9+).♻️ Suggested simplification
-from builtins import type as type_Then on line 39:
- args_schema: type_[BaseModel] = OpenSandboxExecToolSchema + args_schema: type[BaseModel] = OpenSandboxExecToolSchema🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py` at line 3, Remove the unnecessary import "from builtins import type as type_" and update any usage of the alias type_ (e.g., the type annotation currently written using type_) to the builtin typing form type[BaseModel] (or simply type[YourModelClass]) as applicable—specifically replace the annotation that uses type_ on the function/method where BaseModel is referenced with the native Python 3.9+ generic builtin type and delete the imported symbol type_ from the module.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`:
- Around line 55-58: The default Unix permission for the Pydantic Field named
mode is currently set as decimal 755; change the default to octal 0o755 and
update every usage that constructs or applies permissions from this field (e.g.,
the code path handling action="mkdir" and any os.chmod/os.makedirs or
permission-bit computations that consume mode) so they treat mode as an octal
permission integer; ensure the Field declaration remains mode: int =
Field(default=0o755, ...) and adjust any tests or comparisons that assumed
decimal 755 accordingly.
---
Nitpick comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py`:
- Line 3: Remove the unnecessary import "from builtins import type as type_" and
update any usage of the alias type_ (e.g., the type annotation currently written
using type_) to the builtin typing form type[BaseModel] (or simply
type[YourModelClass]) as applicable—specifically replace the annotation that
uses type_ on the function/method where BaseModel is referenced with the native
Python 3.9+ generic builtin type and delete the imported symbol type_ from the
module.
In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`:
- Around line 146-162: The _append method performs a read-modify-write
(sandbox.files.read_bytes -> concat -> sandbox.files.write_file) which is
non-atomic and can be memory-intensive or race-prone for large files or
concurrent appends; update the method's docstring (near _append in
open_sandbox_file_tool.py) to explicitly state this limitation and recommend its
intended usage (chunked uploads) and/or mention that callers should handle
concurrency or use a more atomic mechanism if needed, referencing
_ensure_parent_dir, sandbox.files.read_bytes, and sandbox.files.write_file so
readers can locate the related operations.
- Line 4: Remove the unnecessary import "from builtins import type as type_"
from open_sandbox_file_tool.py; if the module references the alias type_ (search
for "type_"), replace those uses with the built-in type or appropriate
isinstance/type calls (e.g., use type(...) or isinstance(..., type)) and delete
the import line entirely.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 111d41e9-4ba2-49e0-a795-f8c9c1ff74bd
📒 Files selected for processing (7)
lib/crewai-tools/pyproject.tomllib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_base_tool.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py
| mode: int = Field( | ||
| default=755, | ||
| description="For action='mkdir': Unix file mode as an integer (default 755).", | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
opensandbox python SDK WriteEntry mode parameter documentation
💡 Result:
In the OpenSandbox Python SDK, WriteEntry.mode is the Unix file permission bits (octal integer). It’s passed as an integer like 644 or 755 when creating a WriteEntry for sandbox.files.write_files. [1][2] Documented usage (examples): - mode=644 creates a normal data file with rw-r--r-- permissions. [1] - mode=755 creates an executable script with rwxr-xr-x permissions. [1] - Common recommended values include 600 (rw-------) for sensitive files. [1] Permission meanings (octal): - 755 => rwxr-xr-x (owner read/write/execute; group/others read/execute) [3] - 644 => rw-r--r-- (owner read/write; group/others read only) [3] - 600 => rw------- (only owner read/write) [3] Therefore, WriteEntry.mode expects a numeric octal Unix mode/permission value (e.g., 644, 755, 600), and it controls the file’s permission bits inside the sandbox. [1][3]
Citations:
- 1: https://mintlify.com/alibaba/OpenSandbox/examples/file-operations
- 2: https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/README.md
- 3: https://www.mintlify.com/alibaba/OpenSandbox/api/execution/filesystem
Fix Unix file mode to use octal notation.
mode: int = 755 is the decimal value 755, not octal. Unix file permissions should be specified in octal: 0o755 (octal) = 493 (decimal) = rwxr-xr-x permissions. The SDK interprets mode as octal Unix permission bits; passing 755 decimal would set permissions to 0o1363 instead of 0o755, resulting in incorrect file permissions.
Update the default value and all usages:
Suggested fixes
mode: int = Field(
- default=755,
+ default=0o755,
description="For action='mkdir': Unix file mode as an integer (default 755).",
)And update line 96 and line 175:
- mode: int = 755,
+ mode: int = 0o755,- sandbox.files.create_directories([sdk["WriteEntry"](path=parent, mode=755)])
+ sandbox.files.create_directories([sdk["WriteEntry"](path=parent, mode=0o755)])📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| mode: int = Field( | |
| default=755, | |
| description="For action='mkdir': Unix file mode as an integer (default 755).", | |
| ) | |
| mode: int = Field( | |
| default=0o755, | |
| description="For action='mkdir': Unix file mode as an integer (default 755).", | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`
around lines 55 - 58, The default Unix permission for the Pydantic Field named
mode is currently set as decimal 755; change the default to octal 0o755 and
update every usage that constructs or applies permissions from this field (e.g.,
the code path handling action="mkdir" and any os.chmod/os.makedirs or
permission-bit computations that consume mode) so they treat mode as an octal
permission integer; ensure the Field declaration remains mode: int =
Field(default=0o755, ...) and adjust any tests or comparisons that assumed
decimal 755 accordingly.
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
4 similar comments
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
Summary
Adds
OpenSandboxExecToolandOpenSandboxFileTooltocrewai-tools, following the exact same pattern as the Daytona sandbox tool.What's new
crewai_tools.tools.open_sandbox_toolpackage with three classes:OpenSandboxBaseTool— base class with lazy SDK import,ConnectionConfigSyncclient,SandboxSynclifecycle (create/connect/delete), atexit cleanup for persistent sandboxes, threading lock for thread safetyOpenSandboxExecTool— runs shell commands, returns{exit_code, result, artifacts}OpenSandboxFileTool— read/write/append/list/delete/mkdir/info file actionsOptional dependency
opensandboxis declared as an optional dep inpyproject.toml:Install with:
pip install crewai-tools[opensandbox]The module is fully importable without
opensandboxinstalled — theImportErroronly fires when a tool method is actually called, matching the Daytona pattern.Env vars
OPEN_SANDBOX_API_KEYOPEN_SANDBOX_DOMAINlocalhost:8080)Pattern reference
Modelled directly on
daytona_sandbox_tool— same base class structure, same_acquire_sandbox/_release_sandboxlifecycle, same lazy import guard, samepersistent/sandbox_id/sandbox_timeoutfields.Testing
opensandboxinstalledruff check+ruff formatcleanSummary by CodeRabbit
opensandboxdependency for enhanced sandbox capabilities