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
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.

## Release notifications

MPA session output storage accepts only optional buckets beginning with `agentkit-platform-`. The
Worker Tool uses IAM Role without TOS keys; creation additively grants that role
bucket metadata access and object read/write/delete under `sandbox-session/*`.
The shared role's other consumers receive the same access. Existing Tools,
Sessions and public bucket policies are not changed. Empty selection preserves
server defaults. See [the managed setup](../veadk/integrations/mpa/managed/README.md#tos-output-storage-with-iam-role).

MPA 会话输出存储仅可选以 `agentkit-platform-` 开头的桶,Worker Tool 使用 IAM Role,不再要求 TOS 密钥。
创建时增量补齐角色的桶元数据权限和 `sandbox-session/*` 对象读写/删除权限;共享
角色的其他使用者同样获得该权限。不修改已有 Tool、会话或桶公开策略;空选择保留
服务端默认配置。参见[托管配置](../veadk/integrations/mpa/managed/README.zh.md#使用-iam-role-的-tos-输出存储)。

The MPA creation wizard supports clicking step headings and returning to earlier
settings after restoring a submitted draft. Submitted settings remain read-only;
retry retains the original identity. Any submitted draft offers New Agent to
Expand Down
6 changes: 6 additions & 0 deletions frontend/server/deployment_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ def list_resources(
namespace: str = "",
workspace_id: str = "",
search: str = "",
name_prefix: str = "",
page_number: int = 1,
page_size: int = 100,
) -> dict[str, Any]:
Expand All @@ -562,6 +563,10 @@ def list_resources(
for item in _tos_buckets(client)
if not item.get("Location") or item.get("Location") == service_region
]
if name_prefix:
all_items = [
item for item in all_items if item["name"].startswith(name_prefix)
]
if search:
normalized_search = search.casefold()
all_items = [
Expand Down Expand Up @@ -988,6 +993,7 @@ async def _deployment_resources(request: Request):
namespace=str(request.query_params.get("namespace") or "").strip(),
workspace_id=str(request.query_params.get("workspaceId") or "").strip(),
search=str(request.query_params.get("search") or "").strip(),
name_prefix=str(request.query_params.get("namePrefix") or "").strip(),
page_number=page_number,
page_size=page_size,
)
Expand Down
9 changes: 2 additions & 7 deletions frontend/server/mpa_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ class CreationRequest(BaseModel):
openvikingUrl: str = Field(default="", max_length=1024)
openvikingResourceId: str = Field(default="", max_length=128)
openvikingApiKey: str = Field(default="", max_length=512, repr=False)
tosAccessKey: str = Field(default="", max_length=256, repr=False)
tosSecretKey: str = Field(default="", max_length=256, repr=False)
tosBucket: str = Field(default="", max_length=256)
tosBucket: str = Field(default="", max_length=63)

@field_validator("runtimeImage", "workerImage")
@classmethod
Expand Down Expand Up @@ -170,10 +168,7 @@ async def create(request: Request):
if not payload["name"]:
# Keep the exact input shape of already-submitted legacy tasks.
payload.pop("name")
tos = {
field: payload.pop(field)
for field in ("tosAccessKey", "tosSecretKey", "tosBucket")
}
tos = {"tosBucket": payload.pop("tosBucket")}
with_creation_resources(config, payload)
openviking_api_key = payload.pop("openvikingApiKey")
resources = validate_creation_resources(
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/adk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3264,6 +3264,7 @@ export interface DeploymentResourceQuery {
namespace?: string;
workspaceId?: string;
search?: string;
namePrefix?: string;
pageNumber?: number;
pageSize?: number;
}
Expand All @@ -3284,6 +3285,7 @@ export async function listDeploymentResources(
if (query.namespace) params.set("namespace", query.namespace);
if (query.workspaceId) params.set("workspaceId", query.workspaceId);
if (query.search) params.set("search", query.search);
if (query.namePrefix) params.set("namePrefix", query.namePrefix);
if (query.pageNumber) params.set("pageNumber", String(query.pageNumber));
if (query.pageSize) params.set("pageSize", String(query.pageSize));
const response = await apiFetch(
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/adk/mpaCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ export interface MpaCreationInput {
pgPort?: string;
openvikingUrl?: string;
openvikingResourceId?: string;
tosAccessKey?: string;
tosSecretKey?: string;
tosBucket?: string;
}
export interface MpaCreationRequest extends MpaCreationInput {
Expand All @@ -25,7 +23,7 @@ export interface MpaCreationRequest extends MpaCreationInput {
export interface MpaCreationTask
extends Omit<
MpaCreationInput,
"tosAccessKey" | "tosSecretKey" | "tosBucket"
"tosBucket"
> {
taskId: string;
state: "running" | "cancelling" | "succeeded" | "failed" | "cancelled";
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/i18n/resources/en-US/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -1623,12 +1623,12 @@
"openvikingInvalid": "Enter a valid HTTPS service URL, resource ID and API Key together, or leave all blank.",
"openvikingCredentials": "The API Key is used only for this creation and is not saved in browser drafts; re-enter it after a refresh.",
"tosTitle": "Session output storage",
"tosDescription": "Optional. Enter complete TOS credentials and a bucket to mount an isolated output directory for every Session. Leave all three blank to use the server defaults; TOS mounting is disabled if no defaults are configured.",
"tosAccessKey": "TOS Access Key",
"tosSecretKey": "TOS Secret Key",
"tosDescription": "Optional. Select a bucket to persist each Session's output directory. Access uses the sandbox IAM Role. Leave empty to keep server defaults.",
"tosBucket": "TOS bucket",
"tosInvalid": "TOS Access Key, Secret Key, and bucket are all required together.",
"tosCredentials": "Credentials are used only for this creation and are not saved in browser recovery data or creation task records.",
"tosInvalid": "Select an available bucket starting with agentkit-platform-.",
"tosCredentials": "Creation adds session-output access for the selected bucket to the shared role. Other runtimes and sandboxes using that role receive the same access.",
"tosDisable": "Clear bucket selection",
"tosBucketUnavailable": "The bucket is unavailable in this deployment account or region. Select a bucket from the list and retry.",
"runtimeImage": "MPA image",
"workerImage": "Worker image",
"imageDefault": "Leave blank to use the configured image",
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/i18n/resources/zh-CN/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -1623,12 +1623,12 @@
"openvikingInvalid": "请同时填写有效的 HTTPS 服务地址、资源 ID 和 API Key,或全部留空。",
"openvikingCredentials": "API Key 仅用于本次创建,不保存到浏览器草稿;刷新页面后需重新填写。",
"tosTitle": "会话输出存储",
"tosDescription": "可选。填写完整的 TOS 凭证和存储桶,为新智能体的每个会话挂载独立输出目录。三项均留空时使用服务端预设;服务端未配置时,不启用 TOS 挂载。",
"tosAccessKey": "TOS Access Key",
"tosSecretKey": "TOS Secret Key",
"tosDescription": "可选。选择存储桶,持久保存每个会话的输出目录,使用沙箱 IAM Role 鉴权。未选桶时保留服务端默认配置。",
"tosBucket": "TOS 存储桶",
"tosInvalid": "TOS Access Key、Secret Key 和存储桶必须同时填写。",
"tosCredentials": "凭证仅用于本次创建,不会保存在浏览器恢复数据或创建任务记录中。",
"tosInvalid": "请选择以 agentkit-platform- 开头的可用存储桶。",
"tosCredentials": "创建时为共享角色追加所选桶的会话输出访问权限,使用该角色的其他运行时和沙箱也将获得相同权限。",
"tosDisable": "清除桶选择",
"tosBucketUnavailable": "当前部署账号或地域无法使用该桶,请从列表选择存储桶后重试。",
"runtimeImage": "MPA 镜像",
"workerImage": "Worker 镜像",
"imageDefault": "留空使用配置文件中的默认镜像",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ui/DeploymentResources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ interface ResourceListState {
loadMore: () => void;
}

function useDeploymentResourceList(
export function useDeploymentResourceList(
query: DeploymentResourceQuery | null,
): ResourceListState {
const [items, setItems] = useState<DeploymentResource[]>([]);
Expand Down Expand Up @@ -192,7 +192,7 @@ function resourceOptions(
}));
}

function ResourcePicker({
export function ResourcePicker({
ariaLabel,
value,
valueLabel,
Expand Down
64 changes: 31 additions & 33 deletions frontend/src/ui/mpa-create/MpaCreateDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import {
} from "../../adk/mpaCreation";
import "../../components/composites/ModalButton/ModalButton.css";
import "./MpaCreateDialog.css";
import "../ProjectPreview.css";
import { runtimeNameProblem } from "../../create/runtimeName";
import type { CloudProvider } from "../../adk/cloudProvider";
import { validOpenViking, validPgTarget } from "../../adk/mpaCreationResources";
import { ResourcePicker, useDeploymentResourceList } from "../DeploymentResources";

const PG_CONSOLE_URL =
"https://console.volcengine.com/aidap/region:aidap+cn-beijing/";
Expand All @@ -35,7 +37,10 @@ function freshInput(region: string): MpaCreationInput {
}

function withoutTosCredentials(input: MpaCreationInput): MpaCreationInput {
return { ...input, tosAccessKey: undefined, tosSecretKey: undefined };
const clean = { ...input };
Reflect.deleteProperty(clean, "tosAccessKey");
Reflect.deleteProperty(clean, "tosSecretKey");
return clean;
}

function initial(region: string): {
Expand Down Expand Up @@ -126,15 +131,13 @@ export function MpaCreateDialog({
input.openvikingResourceId,
openvikingApiKey,
);
const tosValues = [
input.tosAccessKey,
input.tosSecretKey,
input.tosBucket,
].map((value) => value?.trim() ?? "");
const tosValid =
tosValues.every(Boolean) || tosValues.every((value) => !value);
const tosBucket = input.tosBucket?.trim() ?? "";
const tosValid = !tosBucket || (tosBucket.startsWith("agentkit-platform-") && /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(tosBucket));
const tosLocked =
busy || (submitted && (!task || running || task.state === "succeeded"));
const tosList = useDeploymentResourceList(
step === 2 && !tosLocked ? { kind: "tos-bucket", region, namePrefix: "agentkit-platform-" } : null,
);
useEffect(() => {
if (submitted || taskId) return;
try {
Expand Down Expand Up @@ -595,31 +598,26 @@ export function MpaCreateDialog({
<fieldset className="mpa-create-tos">
<legend>{t(key("tosTitle"))}</legend>
<p>{t(key("tosDescription"))}</p>
{(
["tosAccessKey", "tosSecretKey", "tosBucket"] as const
).map((field) => (
<label key={field}>
{t(key(field))}
<input
name={field}
type={field === "tosBucket" ? "text" : "password"}
value={input[field] ?? ""}
maxLength={256}
disabled={tosLocked}
autoComplete={
field === "tosBucket" ? "off" : "new-password"
}
spellCheck={false}
aria-invalid={!tosValid}
onChange={(event) =>
setInput((previous) => ({
...previous,
[field]: event.target.value,
}))
}
/>
</label>
))}
<div>
<span>{t(key("tosBucket"))}</span>
<ResourcePicker
ariaLabel={t(key("tosBucket"))}
value={tosBucket}
valueLabel={tosBucket}
valueField="name"
state={tosList}
disabled={tosLocked}
onChange={(resource) => setInput((previous) => ({
...previous, tosBucket: resource.name,
}))}
/>
</div>
{tosBucket && (
<Button variant="outline" disabled={tosLocked}
onClick={() => setInput((previous) => ({ ...previous, tosBucket: "" }))}>
{t(key("tosDisable"))}
</Button>
)}
{!tosValid && <p role="alert">{t(key("tosInvalid"))}</p>}
<p>{t(key("tosCredentials"))}</p>
</fieldset>
Expand Down
Loading