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
8 changes: 4 additions & 4 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1538,13 +1538,13 @@ agent terminology rather than exposing these control-plane resource types.
`StudioEnv`,火山引擎继续使用 `Private`。创建时获取模型凭据并配置模型名称、
地址、API Key、鉴权和快照;已有工作区绑定保持不变。

按云环境和地域选择 `studio-sandbox-1.0.1` 镜像:
按云环境和地域选择 `all-in-one-1.0.2` 镜像:

| 云环境 | 地域 | 镜像 |
| --- | --- | --- |
| 火山引擎 | cn-beijing | `enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1` |
| 火山引擎 | cn-shanghai | `enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1` |
| BytePlus | ap-southeast-1 | `enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1` |
| 火山引擎 | cn-beijing | `enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2` |
| 火山引擎 | cn-shanghai | `enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2` |
| BytePlus | ap-southeast-1 | `enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2` |

可通过 `STUDIO_WORKSPACE_IMAGE` 指定区域可访问的其他镜像。启动命令使用镜像内的
`/opt/gem/run.sh`,不再注入编辑器补丁。火山引擎默认中文,BytePlus 默认英文,
Expand Down
51 changes: 43 additions & 8 deletions frontend/server/environments/dockerfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,21 @@ class _Package:
"ubuntu-24.04": ("Ubuntu 24.04", "ubuntu:24.04"),
}

AIO_BASE_IMAGE = (
"agentkit-cli-2107625663-cn-beijing.cr.volces.com/agentkit/"
"agent-native-requirements-aio:0.2.1-20260831"
)
AIO_BASE_IMAGES = {
(
"volcengine",
"cn-beijing",
): "enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
(
"volcengine",
"cn-shanghai",
): "enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
(
"byteplus",
"ap-southeast-1",
): "enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
}
AIO_BASE_IMAGE = AIO_BASE_IMAGES[("volcengine", "cn-beijing")]

_PYTHON_PATCH_VERSIONS = {
"3.10": "3.10.18",
Expand Down Expand Up @@ -198,7 +209,24 @@ def _apt_packages(
return packages


def build_dockerfile(config: EnvironmentInput) -> str:
def resolve_aio_base_image(provider: str = "volcengine", region: str = "") -> str:
provider = provider.strip() or "volcengine"
region = region.strip()
if region:
image = AIO_BASE_IMAGES.get((provider, region))
if image is not None:
return image
if provider == "byteplus":
return AIO_BASE_IMAGES[("byteplus", "ap-southeast-1")]
return AIO_BASE_IMAGES[("volcengine", "cn-beijing")]


def build_dockerfile(
config: EnvironmentInput,
*,
provider: str = "volcengine",
region: str = "",
) -> str:
"""Build the canonical Dockerfile when the user did not provide one."""
if config.dockerfile:
return validate_dockerfile(config.dockerfile)
Expand All @@ -221,7 +249,7 @@ def build_dockerfile(config: EnvironmentInput) -> str:
)
lines = (
[
f"ARG AIO_BASE_IMAGE={AIO_BASE_IMAGE}",
f"ARG AIO_BASE_IMAGE={resolve_aio_base_image(provider, region)}",
"ARG AIO_BASE_PLATFORM=linux/amd64",
"",
f"# Base environment: AIO Sandbox ({os_label})",
Expand Down Expand Up @@ -350,9 +378,14 @@ def build_dockerfile(config: EnvironmentInput) -> str:
return "\n".join(lines)


def environment_base_image(config: EnvironmentInput) -> str:
def environment_base_image(
config: EnvironmentInput,
*,
provider: str = "volcengine",
region: str = "",
) -> str:
if config.base_environment == "aio-sandbox":
return AIO_BASE_IMAGE
return resolve_aio_base_image(provider, region)
if config.base_environment == "codex-sandbox":
match = re.search(
r"^\s*FROM(?:\s+--platform=\S+)?\s+(\S+)",
Expand Down Expand Up @@ -384,8 +417,10 @@ def validate_dockerfile(value: str) -> str:

__all__ = [
"AIO_BASE_IMAGE",
"AIO_BASE_IMAGES",
"build_dockerfile",
"environment_base_image",
"environment_capabilities",
"resolve_aio_base_image",
"validate_dockerfile",
]
33 changes: 30 additions & 3 deletions frontend/server/environments/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,14 @@ async def create(self, owner_id: str, body: EnvironmentInput) -> EnvironmentView
elif body.git_source is not None:
body = body.model_copy(update={"dockerfile": ""})
else:
body = body.model_copy(update={"dockerfile": build_dockerfile(body)})
provider, region = self._cloud_location()
body = body.model_copy(
update={
"dockerfile": build_dockerfile(
body, provider=provider, region=region
)
}
)
environment_id = uuid4().hex
selected_skills = await self._persist_local_skill_assets(
owner_id, environment_id, body.selected_skills
Expand Down Expand Up @@ -386,11 +393,16 @@ async def update(
)
}
)
provider, region = self._cloud_location()
values["dockerfile"] = (
""
if merged_input.git_source is not None
or merged_input.image_source is not None
else build_dockerfile(merged_input)
else build_dockerfile(
merged_input,
provider=provider,
region=region,
)
)
external_binding: tuple[EnvironmentResources, str] | None = None
if (
Expand Down Expand Up @@ -653,6 +665,11 @@ async def get_manifest(
skills = await repository.get_skill_manifest(
owner_id, environment_id, version_id
)
base_provider, base_region = (
(build.resources.provider, build.resources.region)
if isinstance(build.resources, EnvironmentResources)
else self._cloud_location()
)
return EnvironmentManifest(
apiVersion="agentkit.studio/v3",
metadata=EnvironmentManifestMetadata(
Expand All @@ -664,7 +681,11 @@ async def get_manifest(
spec=EnvironmentManifestSpec(
image=build.image,
baseEnvironment=environment.base_environment,
baseImage=environment_base_image(environment),
baseImage=environment_base_image(
environment,
provider=base_provider,
region=base_region,
),
operatingSystem=environment.operating_system,
language=environment.language,
executionRuntime=environment.execution_runtime,
Expand Down Expand Up @@ -1301,6 +1322,12 @@ def _require_cloud(self) -> EnvironmentCloudGateway:
)
return self._cloud

def _cloud_location(self) -> tuple[str, str]:
if self._cloud is None:
return "volcengine", "cn-beijing"
info = self._cloud.describe()
return info.provider, info.region


async def _to_thread_start(
cloud: EnvironmentCloudGateway,
Expand Down
6 changes: 3 additions & 3 deletions frontend/server/workspace_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
(
"volcengine",
"cn-beijing",
): "enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1",
): "enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
(
"volcengine",
"cn-shanghai",
): "enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1",
): "enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
(
"byteplus",
"ap-southeast-1",
): "enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1",
): "enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
}


Expand Down
6 changes: 3 additions & 3 deletions frontend/src/ui/EnvironmentCenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ import {
} from "../adk/client";
import {
buildEnvironmentDockerfile,
AIO_BASE_IMAGE,
AIO_BASE_IMAGES,
CODEX_SANDBOX_BASE_IMAGES,
EMPTY_ENVIRONMENT_DRAFT,
ENVIRONMENT_BASE_ENVIRONMENTS,
Expand Down Expand Up @@ -147,7 +147,7 @@ function dockerfilePresetEnvironmentOptions(t: TFunction): Option[] {

function dockerfilePresetEnvironmentFromContent(content: string): DockerfilePresetEnvironment {
const baseImage = dockerfileBaseImage(content, "");
if (baseImage === AIO_BASE_IMAGE) return "aio-sandbox";
if (Object.values(AIO_BASE_IMAGES).includes(baseImage)) return "aio-sandbox";
if (baseImage.includes("/codexenv:")) return "codex-sandbox";
return "none";
}
Expand Down Expand Up @@ -1566,7 +1566,7 @@ function EnvironmentEditor({
const customDockerfile = draft.dockerfile ?? generatedDockerfile;
const hasDockerfilePresetEnvironment = dockerfilePresetEnvironment !== "none";
const selectedBaseImage = dockerfilePresetEnvironment === "aio-sandbox"
? AIO_BASE_IMAGE
? AIO_BASE_IMAGES[cloudProvider]
: dockerfilePresetEnvironment === "codex-sandbox"
? CODEX_SANDBOX_BASE_IMAGES[cloudProvider]
: "";
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/ui/environmentModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ export const ENVIRONMENT_BASE_ENVIRONMENTS: ReadonlyArray<{
},
];

export const AIO_BASE_IMAGE = "agentkit-cli-2107625663-cn-beijing.cr.volces.com/agentkit/agent-native-requirements-aio:0.2.1-20260831";
export const AIO_BASE_IMAGES: Record<CloudProvider, string> = {
volcengine: "enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
byteplus: "enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
};

export const AIO_BASE_IMAGE = AIO_BASE_IMAGES.volcengine;

export const CODEX_SANDBOX_BASE_IMAGES: Record<CloudProvider, string> = {
volcengine: "enterprise-public-cn-beijing.cr.volces.com/vefaas-public/codexenv:1.1.0",
Expand Down Expand Up @@ -335,7 +340,7 @@ export function buildEnvironmentDockerfile(
);
const lines = usesAioPython
? [
`ARG AIO_BASE_IMAGE=${AIO_BASE_IMAGE}`,
`ARG AIO_BASE_IMAGE=${AIO_BASE_IMAGES[cloudProvider]}`,
"ARG AIO_BASE_PLATFORM=linux/amd64",
"",
`# Base environment: AIO Sandbox (${operatingSystem.label})`,
Expand Down
6 changes: 5 additions & 1 deletion frontend/tests/environmentCenter.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,11 @@ test("uses AIO Sandbox as the default base environment and preserves its runtime
const model = await server.ssrLoadModule("/src/ui/environmentModel.ts");
assert.equal(model.EMPTY_ENVIRONMENT_DRAFT.baseEnvironment, "aio-sandbox");
const dockerfile = model.buildEnvironmentDockerfile(model.EMPTY_ENVIRONMENT_DRAFT);
assert.match(dockerfile, /^ARG AIO_BASE_IMAGE=agentkit-cli-2107625663-cn-beijing\.cr\.volces\.com\/agentkit\/agent-native-requirements-aio:0\.2\.1-20260831$/m);
assert.match(dockerfile, /^ARG AIO_BASE_IMAGE=enterprise-public-cn-beijing\.cr\.volces\.com\/vefaas-public\/agentkit-sandbox:all-in-one-1\.0\.2$/m);
assert.match(
model.buildEnvironmentDockerfile(model.EMPTY_ENVIRONMENT_DRAFT, "byteplus"),
/^ARG AIO_BASE_IMAGE=enterprise-public-ap-southeast-1\.cr\.volces\.com\/vefaas-public\/agentkit-sandbox:all-in-one-1\.0\.2$/m,
);
assert.match(dockerfile, /^FROM --platform=\$\{AIO_BASE_PLATFORM\} \$\{AIO_BASE_IMAGE\}$/m);
assert.match(dockerfile, /BASH_VENV_PATH=\/opt\/veadk-environment\/\.venv/);
assert.match(dockerfile, /^EXPOSE 8080$/m);
Expand Down
10 changes: 8 additions & 2 deletions tests/cli/test_generated_agent_backend_codegen_extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"agents/__init__.py": "a6449a6cac3bfda8b834ea39ea95ca2f8d0471ac480e1e876313d7398eea59ba",
"agents/demo_agent/agent.py": "3c28f3e63f185d1ee8402d58b62c8654cf18fe4180a1f348abaa63547d91446c",
"agents/demo_agent/__init__.py": "ba3abbb199bbae74dc75151a44ba53a557e5f47d509835950ca756346c5a9582",
"agents/demo_agent/dynamic_a2a.py": "d136f27d6a77439708c415686a3d167f2ad2fb9a96a5f8a0751916b09d46e364",
"agents/demo_agent/dynamic_a2a.py": "d8f1854d78267a9a1a8a568881a7630cd1ae98cb8fc2a736d432576b31415ec5",
".env.example": "ec3258da9bef4e74333376d8554c265ccb12a4a1e5d4e1e1b0acdf5c9ae93ab6",
"requirements.txt": "9f55ba1a16ae8199165fde7de39f5f93133963634ebb6d1bdb241cc3cefa10ee",
"README.md": "a34208314cf9061c02662028d7a9dd97448e6b73c1d732cb4aeaa8f70dbbc684",
Expand All @@ -87,7 +87,7 @@
"agents/__init__.py": "a6449a6cac3bfda8b834ea39ea95ca2f8d0471ac480e1e876313d7398eea59ba",
"agents/full_agent/agent.py": "35560cfa5ea93955244482d727c8f8369599fa5b9560ba1f3804df7273e245ce",
"agents/full_agent/__init__.py": "ba3abbb199bbae74dc75151a44ba53a557e5f47d509835950ca756346c5a9582",
"agents/full_agent/dynamic_a2a.py": "d136f27d6a77439708c415686a3d167f2ad2fb9a96a5f8a0751916b09d46e364",
"agents/full_agent/dynamic_a2a.py": "d8f1854d78267a9a1a8a568881a7630cd1ae98cb8fc2a736d432576b31415ec5",
".env.example": "2bfd3afda4e661fbb71588ec5f0d584ce6682363cacc81b0394f8da09f7977e8",
"requirements.txt": "3372cfe49df006b3d0e5bdc4f7b4fcc152a16f663d9e10770f09c708e6daa32b",
"README.md": "1bf4dc889c7d1076f50784d253b53412ba7c49bcb69a5d948f9092dbbecb18ac",
Expand Down Expand Up @@ -2410,6 +2410,9 @@ def test_agentkit_app_adds_dynamic_a2a_tools_per_run() -> None:
assert "def _spawn_dynamic_a2a_agent(" in source
assert "def _configure_dynamic_a2a_routes(" in source
assert "def _run_request_custom_metadata(" in source
assert "class _SsePayloadEventIdRewriter" in source
assert "event_id_rewriter.prepare(" in source
assert ":veid:" in source
assert 'getattr(req, "custom_metadata", None)' in source
assert "plugins=[FrontendInvocationPlugin()]" in source
assert "session_service is None or not _has_a2a_registry_config" not in source
Expand Down Expand Up @@ -2440,6 +2443,9 @@ def test_generated_agent_always_enables_per_invocation_metadata() -> None:
in files["agents/demo_agent/dynamic_a2a.py"]
)
dynamic_source = files["agents/demo_agent/dynamic_a2a.py"]
assert "class _SsePayloadEventIdRewriter" in dynamic_source
assert "event_id_rewriter.prepare(" in dynamic_source
assert ":veid:" in dynamic_source
run_sse_body = dynamic_source[
dynamic_source.index('@app.post("/run_sse")') : dynamic_source.index(
"async def event_generator"
Expand Down
8 changes: 8 additions & 0 deletions tests/cloud/test_harness_app_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import importlib
import types
import uuid
from pathlib import Path

import pytest
from fastapi.responses import PlainTextResponse
Expand All @@ -38,6 +39,13 @@ def _closure_cell(function, name):
return closure[freevars.index(name)]


def test_harness_run_sse_rewrites_stream_payload_event_ids() -> None:
source = Path("veadk/cloud/harness_app/app.py").read_text()

assert "_SsePayloadEventIdRewriter" in source
assert "event_id_rewriter.prepare(event_to_stream)" in source


def test_harness_app_exposes_agent_info(monkeypatch):
monkeypatch.delenv("MODEL_AGENT_NAME", raising=False)
monkeypatch.setenv("MODEL_AGENT_API_KEY", "test-api-key")
Expand Down
35 changes: 33 additions & 2 deletions tests/frontend/server/environments/test_environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,8 @@ def test_aio_environment_preserves_the_shell_runtime_contract():
assert config.language == "python-3.12"
assert (
"ARG AIO_BASE_IMAGE="
"agentkit-cli-2107625663-cn-beijing.cr.volces.com/agentkit/"
"agent-native-requirements-aio:0.2.1-20260831"
"enterprise-public-cn-beijing.cr.volces.com/vefaas-public/"
"agentkit-sandbox:all-in-one-1.0.2"
) in dockerfile
assert "FROM --platform=${AIO_BASE_PLATFORM} ${AIO_BASE_IMAGE}" in dockerfile
assert "BASH_VENV_PATH=/opt/veadk-environment/.venv" in dockerfile
Expand All @@ -464,6 +464,37 @@ def test_aio_environment_preserves_the_shell_runtime_contract():
assert "ENTRYPOINT " not in dockerfile


@pytest.mark.parametrize(
"provider,region,expected",
[
(
"volcengine",
"cn-beijing",
"enterprise-public-cn-beijing.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
),
(
"volcengine",
"cn-shanghai",
"enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
),
(
"byteplus",
"ap-southeast-1",
"enterprise-public-ap-southeast-1.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2",
),
],
)
def test_aio_environment_base_image_matches_provider_region(provider, region, expected):
from frontend.server.environments.dockerfile import resolve_aio_base_image

config = EnvironmentInput.model_validate(_payload(baseEnvironment="aio-sandbox"))

assert resolve_aio_base_image(provider, region) == expected
assert f"ARG AIO_BASE_IMAGE={expected}" in build_dockerfile(
config, provider=provider, region=region
)


@pytest.mark.parametrize("operating_system", ["ubuntu-22.04", "ubuntu-24.04"])
@pytest.mark.parametrize("language", ["python-3.10", "python-3.12"])
def test_all_os_language_combinations_generate_buildable_commented_dockerfiles(
Expand Down
2 changes: 1 addition & 1 deletion tests/frontend/server/test_workspace_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_workspace_image_matches_deployment_region(

monkeypatch.delenv("STUDIO_WORKSPACE_IMAGE", raising=False)
assert resolve_workspace_image(provider, region) == (
f"{registry}.cr.volces.com/vefaas-public/agentkit-sandbox:studio-sandbox-1.0.1"
f"{registry}.cr.volces.com/vefaas-public/agentkit-sandbox:all-in-one-1.0.2"
)


Expand Down
Loading
Loading