Files
deepagent/deepagents_sourcecode/libs/deepagents-cli/deepagents_cli/integrations/modal.py
HyunjunJeon af5fbfabec 문서 추가: Context Engineering 문서 추가 및 deepagents_sourcecode 한국어 번역
- Context_Engineering.md: 에이전트 컨텍스트 엔지니어링 개념 정리 문서 추가
- Context_Engineering_Research.ipynb: 연구 노트북 업데이트
- deepagents_sourcecode/: docstring과 주석을 한국어로 번역
2026-01-11 17:55:52 +09:00

130 lines
4.3 KiB
Python

"""Modal 샌드박스 백엔드 구현입니다.
Modal sandbox backend implementation.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from deepagents.backends.protocol import (
ExecuteResponse,
FileDownloadResponse,
FileUploadResponse,
)
from deepagents.backends.sandbox import BaseSandbox
if TYPE_CHECKING:
import modal
class ModalBackend(BaseSandbox):
"""Modal backend implementation conforming to SandboxBackendProtocol.
This implementation inherits all file operation methods from BaseSandbox
and only implements the execute() method using Modal's API.
"""
def __init__(self, sandbox: modal.Sandbox) -> None:
"""Initialize the ModalBackend with a Modal sandbox instance.
Args:
sandbox: Active Modal Sandbox instance
"""
self._sandbox = sandbox
self._timeout = 30 * 60
@property
def id(self) -> str:
"""Unique identifier for the sandbox backend."""
return self._sandbox.object_id
def execute(
self,
command: str,
) -> ExecuteResponse:
"""Execute a command in the sandbox and return ExecuteResponse.
Args:
command: Full shell command string to execute.
Returns:
ExecuteResponse with combined output, exit code, and truncation flag.
"""
# Execute command using Modal's exec API
process = self._sandbox.exec("bash", "-c", command, timeout=self._timeout)
# Wait for process to complete
process.wait()
# Read stdout and stderr
stdout = process.stdout.read()
stderr = process.stderr.read()
# Combine stdout and stderr (matching Runloop's approach)
output = stdout or ""
if stderr:
output += "\n" + stderr if output else stderr
return ExecuteResponse(
output=output,
exit_code=process.returncode,
truncated=False, # Modal doesn't provide truncation info
)
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
"""Download multiple files from the Modal sandbox.
Supports partial success - individual downloads may fail without
affecting others.
Args:
paths: List of file paths to download.
Returns:
List of FileDownloadResponse objects, one per input path.
Response order matches input order.
TODO: Implement proper error handling with standardized FileOperationError codes.
Need to determine what exceptions Modal's sandbox.open() actually raises.
Currently only implements happy path.
"""
# This implementation relies on the Modal sandbox file API.
# https://modal.com/doc/guide/sandbox-files
# The API is currently in alpha and is not recommended for production use.
# We're OK using it here as it's targeting the CLI application.
responses = []
for path in paths:
with self._sandbox.open(path, "rb") as f:
content = f.read()
responses.append(FileDownloadResponse(path=path, content=content, error=None))
return responses
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
"""Upload multiple files to the Modal sandbox.
Supports partial success - individual uploads may fail without
affecting others.
Args:
files: List of (path, content) tuples to upload.
Returns:
List of FileUploadResponse objects, one per input file.
Response order matches input order.
TODO: Implement proper error handling with standardized FileOperationError codes.
Need to determine what exceptions Modal's sandbox.open() actually raises.
Currently only implements happy path.
"""
# This implementation relies on the Modal sandbox file API.
# https://modal.com/doc/guide/sandbox-files
# The API is currently in alpha and is not recommended for production use.
# We're OK using it here as it's targeting the CLI application.
responses = []
for path, content in files:
with self._sandbox.open(path, "wb") as f:
f.write(content)
responses.append(FileUploadResponse(path=path, error=None))
return responses