Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions docs/_specs/response/interface_response_body_spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 接口响应体说明

## 基础结构

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| status | String | 枚举值:Sucess/Failed。<br>代表请求是否正常由沙箱执行完成。 |
| message | String | 响应信息。 |
| error | String | 表示当status=Failed时的错误描述信息。 |
| result | Object | 请求执行结果。根据具体功能,结构有所差异。 |
| └code | Integer | 响应状态码。参考下方状态码定义。 |
| └failure\_reason | String | 当code=5xxx时,包含一些额外的错误信息。 |
| └... | | 根据不同接口,扩展信息有所不同。 |

### 响应状态码定义

| **值** | **描述** |
| --- | --- |
| 2xxx | 命令执行成功 |
| 5xxx | 服务端异常。该场景建议用户进行重试。 |
| 6xxx | 命令执行错误 |

## result扩展信息定义

### execute/run\_in\_session

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| exit\_code | Integer | 命令执行退出码。<br>特殊退出码:-1(超时/网络/会话失效) |
| stdout | String | 命令执行标准输出 |
| stderr | String | 命令执行标准错误 |

### create\_session

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| output | String | session创建结果。 |
| session\_type | String | session类型。<br>枚举值:<br>* bash |

### close\_session

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| session\_type | String | session类型。<br>枚举值:<br>* bash |

### upload

| **字段** | **类型** | **描述** |
| --- | --- | --- |
| success | Boolean | 文件上传是否成功。 |
| message | String | 上传结果信息。 |
| file\_name | String | 文件名 |

现有响应结构固定返回默认值,需要调整。

```shell
{
"status": "Success",
"message": null,
"error": null,
"result": {
"success": false,
"message": "",
"file_name": ""
}
}
```

### write\_file

| **字段** | **类型** | **描述** |
| --- | --- | --- |
| success | Boolean | 文件写入是否成功。 |
| message | String | 写入结果信息。 |

### read\_file

| **字段** | **类型** | **描述** |
| --- | --- | --- |
| content | String | 文件内容。 |
2 changes: 0 additions & 2 deletions rock/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
OssSetupResponse,
ReadFileResponse,
SandboxResponse,
SandboxStatusResponse,
UploadResponse,
WriteFileResponse,
)
Expand Down Expand Up @@ -65,7 +64,6 @@
"ReadFileRequest",
"UploadRequest",
"IsAliveResponse",
"SandboxStatusResponse",
"CommandResponse",
"WriteFileResponse",
"OssSetupResponse",
Expand Down
10 changes: 10 additions & 0 deletions rock/actions/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ class BaseResponse(BaseModel):

class RockResponse(BaseResponse, Generic[T]):
result: T | None = None


class ChownResponse(BaseModel):
success: bool = False
message: str = ""


class ChmodResponse(BaseModel):
success: bool = False
message: str = ""
49 changes: 10 additions & 39 deletions rock/actions/sandbox/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

class SandboxResponse(BaseModel):
code: codes | None = None
exit_code: int | None = None
failure_reason: str | None = None


Expand All @@ -32,30 +31,13 @@ def __bool__(self) -> bool:
return self.is_alive


class SandboxStatusResponse(BaseModel):
sandbox_id: str = None
status: dict = None
port_mapping: dict = None
host_name: str | None = None
host_ip: str | None = None
is_alive: bool = True
image: str | None = None
gateway_version: str | None = None
swe_rex_version: str | None = None
user_id: str | None = None
experiment_id: str | None = None
namespace: str | None = None
cpus: float | None = None
memory: str | None = None


class CommandResponse(BaseModel):
class CommandResponse(SandboxResponse):
exit_code: int | None = None
stdout: str = ""
stderr: str = ""
exit_code: int | None = None


class WriteFileResponse(BaseModel):
class WriteFileResponse(SandboxResponse):
success: bool = False
message: str = ""

Expand All @@ -70,7 +52,7 @@ class ExecuteBashSessionResponse(BaseModel):
message: str = ""


class CreateBashSessionResponse(BaseModel):
class CreateBashSessionResponse(SandboxResponse):
output: str = ""

session_type: Literal["bash"] = "bash"
Expand All @@ -80,31 +62,30 @@ class CreateBashSessionResponse(BaseModel):
"""Union type for all create session responses. Do not use this directly."""


class BashObservation(BaseModel):
class BashObservation(SandboxResponse):
exit_code: int | None = None
session_type: Literal["bash"] = "bash"
output: str = ""
exit_code: int | None = None
failure_reason: str = ""
expect_string: str = ""


Observation = BashObservation


class CloseBashSessionResponse(BaseModel):
class CloseBashSessionResponse(SandboxResponse):
session_type: Literal["bash"] = "bash"


CloseSessionResponse = Annotated[CloseBashSessionResponse, Field(discriminator="session_type")]
"""Union type for all close session responses. Do not use this directly."""


class ReadFileResponse(BaseModel):
class ReadFileResponse(SandboxResponse):
content: str = ""
"""Content of the file as a string."""


class UploadResponse(BaseModel):
class UploadResponse(SandboxResponse):
success: bool = False
message: str = ""
file_name: str = ""
Expand All @@ -113,17 +94,7 @@ class UploadResponse(BaseModel):
FileUploadResponse = UploadResponse


class CloseResponse(BaseModel):
class CloseResponse(SandboxResponse):
"""Response for close operations."""

pass


class ChownResponse(BaseModel):
success: bool = False
message: str = ""


class ChmodResponse(BaseModel):
success: bool = False
message: str = ""
2 changes: 1 addition & 1 deletion rock/sdk/sandbox/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
ReadFileRequest,
ReadFileResponse,
SandboxResponse,
SandboxStatusResponse,
UploadRequest,
UploadResponse,
WriteFileRequest,
WriteFileResponse,
)
from rock.admin.proto.response import SandboxStatusResponse
from rock.common.constants import PID_PREFIX, PID_SUFFIX
from rock.sdk.common.constants import RunModeType
from rock.sdk.common.exceptions import (
Expand Down
3 changes: 2 additions & 1 deletion rock/sdk/sandbox/file_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
from pathlib import Path

from rock.actions import CreateBashSessionRequest, Observation
from rock.actions.response import ChmodResponse, ChownResponse
from rock.actions.sandbox.base import AbstractSandbox
from rock.actions.sandbox.request import ChmodRequest, ChownRequest, Command
from rock.actions.sandbox.response import ChmodResponse, ChownResponse, CommandResponse
from rock.actions.sandbox.response import CommandResponse

logger = logging.getLogger(__name__)

Expand Down
3 changes: 2 additions & 1 deletion tests/integration/sdk/sandbox/test_file_system.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging

from rock.actions.response import ChmodResponse, ChownResponse
from rock.actions.sandbox.request import ChmodRequest, ChownRequest, Command, CreateBashSessionRequest
from rock.actions.sandbox.response import ChmodResponse, ChownResponse, CommandResponse, Observation
from rock.actions.sandbox.response import CommandResponse, Observation
from rock.sdk.sandbox.client import Sandbox

logger = logging.getLogger(__name__)
Expand Down
4 changes: 1 addition & 3 deletions tests/integration/sdk/sandbox/test_sdk_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from rock.actions.sandbox.request import ReadFileRequest
from rock.actions.sandbox.response import SandboxStatusResponse
from rock.admin.proto.response import SandboxStatusResponse
from rock.sdk.sandbox.client import Sandbox
from rock.sdk.sandbox.config import SandboxConfig
from rock.utils.docker import DockerUtil
Expand Down Expand Up @@ -126,8 +126,6 @@ async def test_execute(sandbox_instance: Sandbox):
@SKIP_IF_NO_DOCKER
@pytest.mark.asyncio
async def test_start_sandbox_upper_limit(sandbox_instance: Sandbox):
from rock.actions import SandboxStatusResponse

status: SandboxStatusResponse = await sandbox_instance.get_status()
assert status.cpus == 4

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/sandbox/test_sandbox_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import pytest
import ray

from rock.actions import SandboxStatusResponse
from rock.actions.sandbox.response import State
from rock.admin.proto.response import SandboxStatusResponse
from rock.deployments.config import DockerDeploymentConfig, RayDeploymentConfig
from rock.deployments.constants import Port
from rock.deployments.status import ServiceStatus
Expand Down
5 changes: 2 additions & 3 deletions tests/unit/sdk/test_arun_nohup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from httpx import ReadTimeout

from rock.actions.sandbox.response import Observation
from rock.common.constants import PID_PREFIX
from rock.common.constants import PID_SUFFIX
from rock.common.constants import PID_PREFIX, PID_SUFFIX
from rock.sdk.sandbox.client import Sandbox
from rock.sdk.sandbox.config import SandboxConfig

Expand Down Expand Up @@ -42,7 +41,7 @@ async def fake_wait(self, pid, session, wait_timeout, wait_interval):
)

assert result.exit_code == 0
assert result.failure_reason == ""
assert result.failure_reason is None
assert "/tmp/tmp_1701.out" in result.output
assert "without streaming the log content" in result.output
assert "File size: 2.00 KB" in result.output
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/test_sandbox_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from rock.actions.sandbox.response import (
BashObservation,
CloseBashSessionResponse,
CommandResponse,
CreateBashSessionResponse,
ReadFileResponse,
UploadResponse,
WriteFileResponse,
)


class TestResponseContainsCodeAndFailureReason:
"""各 result Response 序列化后应包含 code 和 failure_reason 字段"""

RESPONSE_CLASSES = [
CommandResponse,
BashObservation,
CreateBashSessionResponse,
CloseBashSessionResponse,
ReadFileResponse,
WriteFileResponse,
UploadResponse,
]

def test_all_responses_have_code_field(self):
for cls in self.RESPONSE_CLASSES:
data = cls().model_dump()
assert "code" in data, f"{cls.__name__} missing 'code'"

def test_all_responses_have_failure_reason_field(self):
for cls in self.RESPONSE_CLASSES:
data = cls().model_dump()
assert "failure_reason" in data, f"{cls.__name__} missing 'failure_reason'"
Loading