Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,5 @@ Builtin transport: [direct HTTP](notes/implemented/architecture/2026-10-18-nativ

- [Current public SDK documentation](notes/proposed/process/2026-10-25-public-sdk-documentation.md) · [verification](evidence/verify/2026-10-25-public-sdk-documentation.md)
- [Bounded public PyPI readback](notes/proposed/process/2026-10-25-pypi-public-readback.md) · [verification](evidence/verify/2026-10-25-pypi-public-readback.md)

- [Public runtime fixture images](notes/proposed/process/2026-10-25-public-runtime-fixture-images.md) · [verification](evidence/verify/2026-10-25-public-runtime-fixture-images.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
[
{
"name": "Public runtime fixture (python-hello)",
"head_sha": "ca65d37801bea99ed988ad40c3665d2b80ac664c",
"html_url": "https://github.com/Alibaba-Agent-Sandbox/python-sdk/actions/runs/40205183614/job/121204482025",
"status": "completed",
"conclusion": "success",
"steps": [
{
"name": "Set up job",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run actions/checkout@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Build the unmodified fixture Dockerfile",
"status": "completed",
"conclusion": "success"
},
{
"name": "Verify the published guest runtime",
"status": "completed",
"conclusion": "success"
},
{
"name": "Start the fixture and check its real HTTP application",
"status": "completed",
"conclusion": "success"
},
{
"name": "Remove the owned fixture container and verify absence",
"status": "completed",
"conclusion": "success"
},
{
"name": "Post Run actions/checkout@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Complete job",
"status": "completed",
"conclusion": "success"
}
]
},
{
"name": "Public runtime fixture (agents-cli)",
"head_sha": "ca65d37801bea99ed988ad40c3665d2b80ac664c",
"html_url": "https://github.com/Alibaba-Agent-Sandbox/python-sdk/actions/runs/40205183614/job/121204481973",
"status": "completed",
"conclusion": "success",
"steps": [
{
"name": "Set up job",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run actions/checkout@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Build the unmodified fixture Dockerfile",
"status": "completed",
"conclusion": "success"
},
{
"name": "Verify the published guest runtime",
"status": "completed",
"conclusion": "success"
},
{
"name": "Start the fixture and check its real HTTP application",
"status": "completed",
"conclusion": "success"
},
{
"name": "Remove the owned fixture container and verify absence",
"status": "completed",
"conclusion": "success"
},
{
"name": "Post Run actions/checkout@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Complete job",
"status": "completed",
"conclusion": "success"
}
]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Verification: public runtime fixture images

- Date: 2026-10-25
- Base: ea4df4e9cf2bdf4ae120f2863e130fb3a318b64b plus scoped fixture/CI/docs changes.
- Runtime: official PyPI agent-sandbox-server 0.4.0a2, unchanged published files.
- Result: PASS for actual Docker fixture build/guest/HTTP/cleanup at reviewed
functional revision ca65d37801bea99ed988ad40c3665d2b80ac664c. Docker remains
unavailable on the author host; these actual checks executed in GitHub CI.

## Regression scope

The previous Dockerfiles COPY local wheels before a fallback shell, and install
an unversioned server if reached. This is a source finding; no unavailable local
Docker execution is represented as a pass or an observed build failure.

The existing CI now builds each actual fixture Dockerfile without injecting files,
checks the server distribution version and import inside the image, starts the
image unchanged, checks public health and either the hello business result or
agents runtime listing, and removes the owned container even on failure.
A separate daemon check and inventory confirm cleanup absence. No model CLI or
model endpoint is substituted into these image checks.

## Executed local checks

- PASS: workflow YAML loads; each maintained shell step passes bash-n.
- PASS: public a2 healthcheck help confirms its host/port/timeout contract.
- PASS: owner health 106 PASS / 1 warning / 0 failures; hygiene/diff checks.
- PASS: [python-hello Docker job](https://github.com/Alibaba-Agent-Sandbox/python-sdk/actions/runs/40205183614/job/121204482025).
- PASS: [agents-cli Docker job](https://github.com/Alibaba-Agent-Sandbox/python-sdk/actions/runs/40205183614/job/121204481973).
Independently queried each job and every step: actual Docker build, guest package
check, unchanged image startup and HTTP application, owned container removal
and absence all completed SUCCESS. No skip was counted as a pass.
- [Sanitized job/step receipt](2026-10-25-public-runtime-fixture-images-jobs.json).
- All final-head required CI and independent review remain mandatory before merge.

## Limits

These are SDK offline fixtures, not a new template publishing source or an
OpenSandbox/Express template matrix. Agents runtime listing does not establish
model execution; real Codex/Qwen calls retain their installation/auth requirements.
No cloud resources or external registry uploads were created by local validation.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Decision: Build SDK fixtures with the published server runtime
Status: proposed

## Problem

Both offline fixture Dockerfiles require local wheel files before their shell
fallback could run. The fallback installs an unversioned server. Neither behavior
matches the documented public-package path. The agents fixture README also
incorrectly describes omitted-adapter requests as HTTP 400 rather than 200/default.

## Decision

Install the exact public server 0.4.0a2 from the official PyPI index in both images.
Remove the wheel COPY and fallback entirely. Correct the README and retain the
explicit offline-fixture and real-model prerequisites. Build both unmodified
Dockerfiles in the existing CI, check their actual guest runtime version and
HTTP application, then remove and independently confirm absence of owned containers.

## API Design

No product API, package version or source-fallback compatibility is added. Python
hello still serves its registered command; agents-cli still requires installed
real agent CLIs and model credentials for actual model execution.

## Alternatives considered

Supplying candidate wheels or manufacturing empty files preserves an unnecessary
second installation path. Merely checking Dockerfile text does not prove the build.
A new test framework or cloud service is unnecessary; use existing Docker CI.

## Dependencies

Docker, the existing public base images and the official PyPI server 0.4.0a2.
No cloud/model credentials or provider account are needed.

## Test Strategy

Run the actual Docker builds, verify package imports/version inside each image,
start the image unchanged and check health plus hello command or agents runtime
listing. Use owned container cleanup even on failure. The smoke does not claim
real Codex/Qwen model execution. Run owner health/hygiene and required CI.

## Acceptance criteria

- Both fixture images build without a local wheel or source fallback.
- Actual guest package is 0.4.0a2; expected HTTP behavior passes.
- Owned containers are removed and confirmed absent, including after failures.
- README accurately distinguishes protocol fixtures from actual agent execution.

## Evidence

- [Verification](../../../evidence/verify/2026-10-25-public-runtime-fixture-images.md)
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,48 @@ jobs:
ABX_TEST_CLI_DIR: ${{ github.workspace }}/.test-deps/cli
ABX_TEST_PROVIDERS_DIR: ${{ github.workspace }}/.test-deps/providers
run: python -m pytest -c client-sdk/pyproject.toml client-sdk/tests/test_providers/test_engine_provider.py -v

fixture-images:
name: Public runtime fixture (${{ matrix.fixture }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
fixture: [python-hello, agents-cli]
steps:
- uses: actions/checkout@v4
- name: Build the unmodified fixture Dockerfile
run: docker build --tag abx-sdk-fixture:current "examples/templates/${{ matrix.fixture }}"
- name: Verify the published guest runtime
run: |
docker run --rm --entrypoint python3 abx-sdk-fixture:current -c 'from importlib.metadata import version; from agent_sandbox.server import start; assert version("agent-sandbox-server") == "0.4.0a2" and callable(start)'
- name: Start the fixture and check its real HTTP application
env:
FIXTURE_NAME: ${{ matrix.fixture }}
run: |
docker run -d --name abx-sdk-fixture-server abx-sdk-fixture:current
for attempt in 1 2 3 4 5 6; do
if docker exec abx-sdk-fixture-server agent-sandbox-server healthcheck --port 9000; then break; fi
if [ "$attempt" = 6 ]; then exit 1; fi
sleep 1
done
docker exec -i -e FIXTURE_NAME="$FIXTURE_NAME" abx-sdk-fixture-server python3 - <<'PYTHON'
import json, os, urllib.request
base = "http://127.0.0.1:9000"
if os.environ["FIXTURE_NAME"] == "python-hello":
request = urllib.request.Request(base + "/commands/hello", data=b'{"name":"Fixture"}', headers={"Content-Type":"application/json"})
with urllib.request.urlopen(request, timeout=10) as response:
assert json.load(response)["result"] == "Hello, Fixture!"
else:
with urllib.request.urlopen(base + "/v1/agents/runtimes", timeout=10) as response:
assert isinstance(json.load(response)["data"], list)
PYTHON
- name: Remove the owned fixture container and verify absence
if: always()
run: |
if docker container inspect abx-sdk-fixture-server >/dev/null 2>&1; then
docker rm -f abx-sdk-fixture-server
fi
docker info >/dev/null
test -z "$(docker ps -aq --filter 'name=^abx-sdk-fixture-server$')"
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

### Fixed

- Build both SDK offline fixtures from the exact public a2 server on official PyPI, remove unreachable local-wheel fallback, and verify actual guest HTTP behavior and cleanup in CI. / SDK 两个离线夹具统一从官方 PyPI 安装精确 a2 服务端,删除不可达的本地 wheel 回退,并通过 CI 验证真实镜像 HTTP 行为及清理。

### Fixed

- Use the installed public provider API in the creation benchmark, clean each owned sandbox before continuing, propagate cleanup failures, and remove unsupported performance targets. / 创建基准改用已安装公开 Provider API,逐轮清理自有沙箱,透传清理错误并删除无依据性能目标。

- Align maintained bilingual SDK guides with public 0.4.0a2, canonical decorator/MCP arguments, current ProviderSandbox imports and unified template launch. Remove stale source-install recipes and retired Python-engine instructions. / 将维护中的双语 SDK 指南统一为公开 0.4.0a2、规范装饰器/MCP 参数、ProviderSandbox 公开导入及统一模板启动;删除旧源码安装方案和已退役 Python 引擎说明。
Expand Down
8 changes: 8 additions & 0 deletions docs/en/guide/testing-e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,11 @@ The separate fresh installation from the official PyPI index has the same bounde
retry interval because that index may appear later than package metadata. Each
failed installation remains visible in the log. Dependency and public import checks
run once after installation succeeds; their failures stop the release.

## SDK fixture images

The SDK repository keeps offline python-hello and agents-cli fixtures, separate
from the template catalog. Their Dockerfiles install public server 0.4.0a2 from
the official PyPI index without local wheels. CI builds both, checks the actual
guest package and HTTP application, and confirms owned container cleanup.
Agents runtime listing does not prove an installed agent or a real model call.
7 changes: 7 additions & 0 deletions docs/zh/guide/testing-e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,10 @@ make e2e-cleanup # 清理遗留资源的 dry-run janitor;ARGS=--yes 才
会立即失败。通过官方 PyPI 索引进行的全新安装也采用同样的有限重试,因为索引
可能晚于包元数据同步。每次安装失败均保留在日志中。安装成功后只执行一次依赖
及公开导入检查,这些检查失败会直接阻止发布。

## SDK 夹具镜像

SDK 仓库保留独立于模板目录的 python-hello 和 agents-cli 离线夹具。两个
Dockerfile 均从官方 PyPI 安装公开 server 0.4.0a2,不需要本地 wheel。CI 构建
两个镜像,检查实际客体包与 HTTP 应用,并确认自有容器清理。Agents 运行时
列表不代表已安装 Agent 或完成真实模型调用。
8 changes: 2 additions & 6 deletions examples/templates/agents-cli/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ ENV ABX_SERVER_ENABLED_GROUPS=agents
# RUN apt-get update && apt-get install -y --no-install-recommends nodejs npm ca-certificates \
# && npm install -g @qwen-code/qwen-code && rm -rf /var/lib/apt/lists/*

COPY *.whl /tmp/
RUN if ls /tmp/*.whl 1>/dev/null 2>&1; then \
pip install --no-cache-dir /tmp/*.whl && rm -f /tmp/*.whl; \
else \
pip install --no-cache-dir agent-sandbox-server; \
fi
# Install the published runtime; no local wheel or source fallback is required.
RUN python3 -m pip install --index-url https://pypi.org/simple --no-cache-dir "agent-sandbox-server==0.4.0a2"

WORKDIR /app
COPY commands.py .
Expand Down
11 changes: 9 additions & 2 deletions examples/templates/agents-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,17 @@ E2E_RUN_LLM=0 ABX_SERVER_PORT=9000 python3 commands.py
E2E_BASE_URL=http://127.0.0.1:9000 python3 e2e_test.py
```

测试会检查:健康检查、未指定适配器时 400、`claude-code` 为 404、名为 `codex` 与 `qwen` 的本地程序各完成一轮并带回文本。
测试会检查:健康检查、未指定适配器时使用默认运行时并返回 200、`claude-code` 为 404、名为 `codex` 与 `qwen` 的本地程序各完成一轮并带回文本。

## English

This template turns on the sandbox Agents API for **Codex** and **Qwen Code**. The image does not install either CLI. Add the `npm install -g` line for the one you call, or install it inside a running sandbox. Other adapter names, including Claude Code, Cursor, and Qoder, are HTTP 404.

The server listens on port **9000**. Point `examples/agents/hosted_codex.py` or `hosted_qwen.py` at it. When `E2E_RUN_LLM=0`, `e2e_test.py` puts the local programs in `e2e-bin/` on `PATH`. Those programs are named `codex` and `qwen`.
The server listens on port **9000**. Point `examples/agents/hosted_codex.py` or `hosted_qwen.py` at it. When `E2E_RUN_LLM=0`, `commands.py` puts the local programs in `e2e-bin/` on `PATH`. Those programs are named `codex` and `qwen`.

Both offline fixture Dockerfiles install `agent-sandbox-server==0.4.0a2` from the
official PyPI index. No local wheel is required. The image does not include real
Codex/Qwen CLIs; model execution still requires the selected CLI and credentials.

两个离线夹具 Dockerfile 均从官方 PyPI 安装 `agent-sandbox-server==0.4.0a2`,
不需要本地 wheel。镜像不预装真实 Codex/Qwen CLI;模型执行仍需相应工具和凭据。
12 changes: 2 additions & 10 deletions examples/templates/python-hello/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,8 @@ FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y python3 python3-pip && rm -rf /var/lib/apt/lists/*

# Install the agent-sandbox-server runtime (the container-side `agent_sandbox.server`).
# When building locally via `abx template deploy`, the agent-sandbox-server wheel
# is automatically injected into the build context as .whl file.
# Fallback: install from PyPI if the wheel is not present.
COPY *.whl /tmp/
RUN if ls /tmp/*.whl 1>/dev/null 2>&1; then \
pip3 install --no-cache-dir /tmp/*.whl && rm -f /tmp/*.whl; \
else \
pip3 install --no-cache-dir agent-sandbox-server; \
fi
# Install the published runtime; no local wheel or source fallback is required.
RUN python3 -m pip install --index-url https://pypi.org/simple --no-cache-dir "agent-sandbox-server==0.4.0a2"

WORKDIR /app
COPY commands.py .
Expand Down
Loading