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
12 changes: 12 additions & 0 deletions docs/content/docs/framework/frontend.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,15 @@ export function RevenueChart({ node, ctx }: ComponentRendererProps) {
<Callout type="info">
未注册渲染器的未知组件会回退到可折叠的 JSON 视图,因此目录与渲染器不匹配也不会导致界面崩溃。
</Callout>


## 沙箱版本更新

管理员可在「系统信息 → 沙箱信息」检查沙箱版本。Studio 按云厂商和沙箱实际区域
查询发布镜像,展示当前版本和目标版本;存在差异时可点击对应沙箱的更新按钮。
Volcengine 和 BytePlus 使用各自的凭据及接口,镜像目录不会跨厂商、区域复用。

Codex 和 DeepSeek Harness 共用 Tool 时,两处同步显示更新状态,只需更新一次。
快照版使用对应的 Tool 单独检查。Codex 缺失的模型环境变量仍会从原 `CODEX_*`
配置补齐。更新完成表示 Tool 已就绪且镜像匹配目标版本,不代表已有 Session 或
历史快照已切换镜像。版本查询失败可点击「检查更新」重试。
12 changes: 12 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ server that `veadk frontend` launches — no separate backend.

## Features

- **Sandbox updates** in System Information compare each Tool's current image
with `ListToolTypes` for its cloud provider and actual region. Volcengine and
BytePlus use their own credentials and API hosts; catalogs are cached for
60 seconds and fetched again before an update. Admins can update configured
prebuilt Tools with `UpdateTool.ImageUrl`. Codex and DeepSeek Harness share
update state when they reference the same Tool; snapshot Tools are checked
independently. Missing Codex `MODEL_AGENT_API_KEY` / `MODEL_AGENT_BASE_URL`
values are still backfilled from `CODEX_*`, preserving existing variables.
Completion requires Tool `Ready` and the target image; this does not verify
existing Sessions or rebuild their snapshots. BytePlus has automated coverage,
but its image update has not been verified against a live account.

- **Streaming chat** over the ADK `/run_sse` event stream. While an Agent is
generating, the composer exposes a stop control that cancels only the active
response, preserves content already received, and immediately enables the
Expand Down
74 changes: 74 additions & 0 deletions frontend/server/sandbox_updates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Admin-only sandbox image inspection and update routes."""

from __future__ import annotations

import asyncio
from collections.abc import Callable
from typing import Any

from fastapi import FastAPI, HTTPException, Request

from veadk.cli.studio_sandbox_updates import SandboxToolUpdates, SandboxUpdateError


def register_sandbox_update_routes(
app: FastAPI,
*,
service: SandboxToolUpdates,
require_admin: Callable[[Request], None],
configured_tools: Callable[[], dict[str, str]],
) -> None:
@app.get("/web/system-info/sandbox-tools/updates")
async def inspect_updates(request: Request):
require_admin(request)
# Codex and DSH aliases resolve to one physical resource.
tool_ids = set(configured_tools().values()) - {""}

async def inspect(tool_id: str) -> dict[str, Any]:
try:
return await asyncio.wait_for(
asyncio.to_thread(service.inspect, tool_id), timeout=15
)
except TimeoutError:
return {"toolId": tool_id, "error": "查询沙箱版本超时,请刷新重试"}
except Exception:
# SDK errors can include request bodies or credentials. Keep
# this response separate from the raw control-plane exception.
return {
"toolId": tool_id,
"error": "查询沙箱版本失败,请检查凭据、区域及接口权限后重试",
}

return {"tools": await asyncio.gather(*(inspect(t) for t in sorted(tool_ids)))}

@app.post("/web/system-info/sandbox-tools/{kind}/update")
async def update(request: Request, kind: str):
require_admin(request)
tool_id = configured_tools().get(kind)
if tool_id is None:
raise HTTPException(status_code=400, detail="Unsupported Sandbox Tool kind")
if not tool_id:
raise HTTPException(status_code=400, detail="未配置 Sandbox Tool ID")
try:
result = await asyncio.to_thread(service.update, tool_id)
except (SandboxUpdateError, TimeoutError) as error:
raise HTTPException(status_code=409, detail=str(error)) from error
except Exception as error:
raise HTTPException(
status_code=502, detail="Sandbox 更新失败,请刷新检查实际状态及接口权限"
) from error
return {"kind": kind, "toolId": tool_id, **result}
55 changes: 55 additions & 0 deletions frontend/src/adk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5040,3 +5040,58 @@ export async function deleteGeneratedAgentTestRun(runId: string): Promise<void>
throw new Error(await httpErrorMessage(res, adkT("client.cleanupDebugRunFailed")));
}
}

export interface SandboxImageState {
toolId: string;
error: string;
provider?: CloudProvider;
region?: string;
toolType?: string;
status?: string;
currentImage?: string;
latestImage?: string;
needsImageUpdate?: boolean;
needsModelEnvUpdate?: boolean;
canUpdateModelEnv?: boolean;
modelEnvError?: string;
canUpdate?: boolean;
}

function sandboxImageState(value: unknown): SandboxImageState {
if (!value || typeof value !== "object") throw new Error(adkT("client.invalidSandboxVersion"));
const row = value as SandboxImageState;
if (typeof row.toolId !== "string" || typeof row.error !== "string") {
throw new Error(adkT("client.invalidSandboxVersion"));
}
if (!row.error && (
(row.provider !== "volcengine" && row.provider !== "byteplus") ||
typeof row.region !== "string" || typeof row.status !== "string" ||
typeof row.currentImage !== "string" || typeof row.latestImage !== "string" ||
typeof row.needsImageUpdate !== "boolean" || typeof row.canUpdate !== "boolean" ||
typeof row.needsModelEnvUpdate !== "boolean" ||
typeof row.canUpdateModelEnv !== "boolean" || typeof row.modelEnvError !== "string"
)) throw new Error(adkT("client.invalidSandboxVersion"));
return row;
}

export async function getSandboxImageUpdates(signal?: AbortSignal): Promise<SandboxImageState[]> {
const response = await apiFetch("/web/system-info/sandbox-tools/updates", { signal });
if (!response.ok) throw new Error(await httpErrorMessage(response, adkT("client.loadSandboxVersionsFailed")));
const payload = await response.json() as { tools?: unknown };
if (!Array.isArray(payload.tools)) throw new Error(adkT("client.invalidSandboxVersion"));
return payload.tools.map(sandboxImageState);
}

export async function updateSandboxTool(kind: SandboxToolKind): Promise<{
updated: boolean;
state: SandboxImageState;
}> {
const response = await apiFetch(
`/web/system-info/sandbox-tools/${encodeURIComponent(kind)}/update`,
{ method: "POST" }, {}, 330_000,
);
if (!response.ok) throw new Error(await httpErrorMessage(response, adkT("client.updateSandboxFailed")));
const payload = await response.json() as { updated?: unknown; state?: unknown };
if (typeof payload.updated !== "boolean") throw new Error(adkT("client.invalidSandboxUpdate"));
return { updated: payload.updated, state: sandboxImageState(payload.state) };
}
4 changes: 4 additions & 0 deletions frontend/src/i18n/resources/en-US/adk.json
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@
"unsafeToolUrl": "{{label}} returned an unsafe URL."
},
"client": {
"invalidSandboxVersion": "Invalid sandbox version response",
"loadSandboxVersionsFailed": "Failed to check sandbox versions",
"updateSandboxFailed": "Failed to update sandbox",
"invalidSandboxUpdate": "Invalid sandbox update response",
"errorWithDetailAndRawResponse": "{{context}}\n{{detail}}\nRaw response:\n{{response}}",
"errorWithRawResponse": "{{context}}\nRaw response:\n{{response}}",
"loadArkApiKeysFailed": "Failed to load Ark API keys",
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/i18n/resources/en-US/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
"console": "Console"
},
"systemInfo": {
"checkUpdates": "Check for updates",
"checkingVersions": "Checking versions…",
"versionCheckError": "Unable to check sandbox versions. Check credentials, region and API permissions, then retry.",
"sandboxUpdateError": "Sandbox update failed. Refresh its status before retrying.",
"modelEnvRepairUnavailable": "Cannot repair model configuration. Check CODEX_API_KEY and CODEX_BASE_URL.",
"updateSandbox": "Update {{variant}}{{name}}",
"updatingSandbox": "Updating",
"title": "System information",
"description": "View the current Studio version and related infrastructure resources",
"general": "General",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n/resources/zh-CN/adk.json
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@
"unsafeToolUrl": "{{label}} 返回了不安全的地址。"
},
"client": {
"invalidSandboxVersion": "沙箱版本响应格式无效",
"loadSandboxVersionsFailed": "查询沙箱版本失败",
"updateSandboxFailed": "更新 Sandbox 失败",
"invalidSandboxUpdate": "沙箱更新响应格式无效",
"errorWithDetailAndRawResponse": "{{context}}\n{{detail}}\n原始响应:\n{{response}}",
"errorWithRawResponse": "{{context}}\n原始响应:\n{{response}}",
"loadArkApiKeysFailed": "加载 Ark API Key 失败",
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/i18n/resources/zh-CN/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
"console": "控制台"
},
"systemInfo": {
"checkUpdates": "检查更新",
"checkingVersions": "正在检查版本…",
"versionCheckError": "查询沙箱版本失败,请检查凭据、区域及接口权限后重试",
"sandboxUpdateError": "Sandbox 更新失败,请刷新检查实际状态后重试",
"modelEnvRepairUnavailable": "无法补齐模型环境变量,请检查 CODEX_API_KEY 和 CODEX_BASE_URL",
"updateSandbox": "更新{{variant}}{{name}}",
"updatingSandbox": "更新中",
"title": "系统信息",
"description": "查看当前 Studio 版本及关联的基础资源",
"general": "通用",
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/ui/SystemInfo.css
Original file line number Diff line number Diff line change
Expand Up @@ -390,3 +390,16 @@
opacity: 1;
}
}

.system-info-refresh {
border: 0;
background: transparent;
color: hsl(var(--muted-foreground));
font: inherit;
cursor: pointer;
padding: 4px 0;
margin-bottom: 8px;
}
.system-info-refresh:hover { color: hsl(var(--foreground)); }
.system-info-refresh:disabled { cursor: wait; opacity: 0.6; }
.system-info-refresh:focus-visible { outline: 2px solid hsl(var(--primary)); outline-offset: 2px; }
Loading
Loading