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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ VeADK provides several useful command line tools for faster deployment and optim
deployed client skips the second OAuth consent confirmation after login;
two dedicated AgentKit CodeEnv Tools are created automatically for temporary
chats and Skill creation unless their IDs are supplied with
`--sandbox-chat-codex-tool-id` and `--sandbox-skill-creator-tool-id`
`--sandbox-chat-codex-tool-id` and `--sandbox-skill-creator-tool-id`; Tool or
credential-relay provisioning failures print the underlying error verbatim
after credential values are redacted
- `veadk studio update --vefaas-app-name <app-name>`: build the frontend from a
local VeADK source checkout and release it through the existing VeFaaS
Application and Function. Omit `--region` and `--project` to search Beijing,
Expand Down
5 changes: 5 additions & 0 deletions docs/content/docs/framework/frontend.en.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ selected with `--sandbox-chat-codex-tool-id` and
credential in KMS and binds only the relay URL and revocable ticket to both
Tools. The VeFaaS Function receives only Tool IDs, never model credentials:

If Tool or credential-relay provisioning fails, the CLI prints the underlying
error verbatim, including its original line breaks, after redacting credential
values. Use the service action, error code, request ID, and request details to
identify the missing permission or account resource.

```bash
veadk studio deploy \
--user-pool-id <pool-id> \
Expand Down
91 changes: 91 additions & 0 deletions tests/cli/test_studio_deploy_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import Mock
from uuid import uuid4

import pytest
from click.testing import CliRunner
Expand Down Expand Up @@ -44,6 +45,96 @@ def _skip_serverless_role_setup(monkeypatch: pytest.MonkeyPatch) -> None:
)


@pytest.mark.parametrize(
("stage", "expected_prefix"),
[
("tool", "Failed to provision the AgentKit chat CodeEnv Tool"),
(
"relay",
"Failed to provision the AgentKit chat model credential relay",
),
],
)
def test_studio_deploy_surfaces_redacted_provisioning_error_chain(
monkeypatch: pytest.MonkeyPatch,
stage: str,
expected_prefix: str,
) -> None:
access_key = uuid4().hex
bearer_value = uuid4().hex
model_key = uuid4().hex
secret_key = uuid4().hex

def _fail(**_: object) -> str:
try:
raise ValueError(f"Authorization: Bearer {bearer_value}")
except ValueError as cause:
raise RuntimeError(
"CreateApiKeyCredentialProvider: AccessDenied\n"
f"Request: api_key={model_key}\n"
f"RequestId=req-123 {access_key}"
) from cause

monkeypatch.setattr(
"veadk.cli.cli_frontend._resolve_studio_identity_region",
lambda **kwargs: kwargs["deployment_region"],
)
if stage == "tool":
monkeypatch.setattr(
"veadk.cli.studio_sandbox_tools.ensure_studio_code_env_tool",
_fail,
)
tool_args: list[str] = []
else:
monkeypatch.setattr(
"veadk.cli.frontend_skill_creator.ensure_skill_creator_model_credential",
_fail,
)
tool_args = [
"--sandbox-chat-codex-tool-id",
"chat-tool-id",
"--sandbox-skill-creator-tool-id",
"skill-tool-id",
]

result = CliRunner().invoke(
studio,
[
"deploy",
"--user-pool-id",
"pool-id",
"--allowed-client-id",
"client-id",
"--vefaas-app-name",
"studio-app",
"--iam-role",
"trn:iam::role/test",
"--gateway-name",
"gateway",
"--volcengine-access-key",
access_key,
"--volcengine-secret-key",
secret_key,
*tool_args,
],
)

assert result.exit_code == 1
assert expected_prefix in result.output
assert (
"Underlying error:\nCreateApiKeyCredentialProvider: AccessDenied"
in result.output
)
assert "Request: api_key=***" in result.output
assert "RequestId=req-123" in result.output
assert "Caused by:\nAuthorization: Bearer ***" in result.output
assert access_key not in result.output
assert bearer_value not in result.output
assert model_key not in result.output
assert secret_key not in result.output
assert "***" in result.output


@pytest.mark.parametrize(
("target_args", "expected_region", "expected_identity_region", "expected_project"),
[
Expand Down
36 changes: 34 additions & 2 deletions veadk/cli/cli_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,30 @@ def _redact_debug_text(text: str) -> str:
)


def _safe_exception_detail(
error: BaseException,
*,
secrets: Iterable[str | None] = (),
) -> str:
"""Return exception messages verbatim except for credential redaction."""
parts: list[str] = []
seen: set[int] = set()
current: BaseException | None = error
while current is not None and id(current) not in seen:
seen.add(id(current))
message = _ANSI_ESCAPE_RE.sub("", str(current)).strip()
for secret in secrets:
if secret:
message = message.replace(secret, "***")
message = _redact_debug_text(message)
if not message:
message = type(current).__name__
if message not in parts:
parts.append(message)
current = current.__cause__ or current.__context__
return "\nCaused by:\n".join(parts)


def _claims_from_forwarded_jwt(authorization: str | None) -> dict | None:
"""Decode the JWT an upstream API gateway forwarded in the Authorization
header, WITHOUT re-verifying its signature.
Expand Down Expand Up @@ -3695,9 +3719,13 @@ def frontend_deploy(
session_token=session_token or "",
)
except Exception as error:
detail = _safe_exception_detail(
error,
secrets=(ak, sk, session_token),
)
raise click.ClickException(
f"Failed to provision the AgentKit {purpose} CodeEnv Tool. "
"Verify account permissions and AgentKit service status."
f"Underlying error:\n{detail}"
) from error
click.echo(f"AgentKit {purpose} CodeEnv Tool is ready.")

Expand All @@ -3720,9 +3748,13 @@ def frontend_deploy(
session_token=session_token,
)
except Exception as error:
detail = _safe_exception_detail(
error,
secrets=(ak, sk, session_token),
)
raise click.ClickException(
f"Failed to provision the AgentKit {purpose} model credential relay. "
"Verify the Tool ID, account permissions, and AgentKit service status."
f"Underlying error:\n{detail}"
) from error
click.echo(f"AgentKit {purpose} model credential relay is ready.")

Expand Down
Loading