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
24 changes: 22 additions & 2 deletions dashscope/agentstudio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,17 @@
)
from .types import (
Message,
PermissionPolicy,
SecurityAlert,
SecurityAlertList,
SecurityOverview,
ServerEvent,
SessionResource,
StopReason,
user_define_outcome,
user_interrupt,
user_message,
user_tool_confirmation,
user_tool_approval_response,
user_custom_tool_result,
user_tool_result,
)
Expand All @@ -75,6 +81,10 @@
WebhookEventListParams,
)
from .constants import (
PermissionPolicyType,
SSEEventType,
SessionStatus,
StopReasonType,
WebhookDeliveryStatus,
WebhookDisabledReason,
WebhookEventType,
Expand Down Expand Up @@ -108,11 +118,17 @@
"AsyncCursorPage",
# unified message type
"Message",
"PermissionPolicy",
"SecurityAlert",
"SecurityAlertList",
"SecurityOverview",
"ServerEvent",
"SessionResource",
"StopReason",
# client event helpers (re-exported for convenience)
"user_message",
"user_interrupt",
"user_tool_confirmation",
"user_tool_approval_response",
"user_custom_tool_result",
"user_tool_result",
"user_define_outcome",
Expand All @@ -135,4 +151,8 @@
"WebhookDisabledReason",
"WebhookDeliveryStatus",
"WebhookEventType",
"SSEEventType",
"SessionStatus",
"StopReasonType",
"PermissionPolicyType",
]
3 changes: 3 additions & 0 deletions dashscope/agentstudio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
AsyncEnvironments,
)
from dashscope.agentstudio.resources.files import Files, AsyncFiles
from dashscope.agentstudio.resources.security import Security, AsyncSecurity
from dashscope.agentstudio.resources.sessions import Sessions, AsyncSessions
from dashscope.agentstudio.resources.skills import Skills, AsyncSkills
from dashscope.agentstudio.resources.vaults import Vaults, AsyncVaults
Expand Down Expand Up @@ -134,6 +135,7 @@ def __init__(
self.skills = Skills(self)
self.vaults = Vaults(self)
self.webhook_endpoints = WebhookEndpoints(self)
self.security = Security(self)

def close(self) -> None:
self.transport.close()
Expand Down Expand Up @@ -207,6 +209,7 @@ def __init__(
self.skills = AsyncSkills(self)
self.vaults = AsyncVaults(self)
self.webhook_endpoints = AsyncWebhookEndpoints(self)
self.security = AsyncSecurity(self)

async def aclose(self) -> None:
await self.transport.aclose()
Expand Down
43 changes: 38 additions & 5 deletions dashscope/agentstudio/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ class StrEnum(str, enum.Enum): # type: ignore[no-redef]
class SSEEventType(StrEnum):
"""Server-sent event types (the value of ``event.type`` in SSE payloads).

Client-sendable: MESSAGE, INTERRUPT, TOOL_CONFIRMATION,
Client-sendable: MESSAGE, INTERRUPT, TOOL_APPROVAL_RESPONSE,
FUNCTION_CALL_OUTPUT, TOOL_CALL_OUTPUT, DEFINE_OUTCOME.
Server-emitted: all types.
Server-emitted: all types (23 total).
"""

# Client-sendable
MESSAGE = "message"
INTERRUPT = "interrupt"
TOOL_CONFIRMATION = "tool_confirmation"
TOOL_APPROVAL_RESPONSE = "tool_approval_response"
FUNCTION_CALL_OUTPUT = "function_call_output"
TOOL_CALL_OUTPUT = "tool_call_output"
DEFINE_OUTCOME = "define_outcome"
Expand All @@ -53,6 +53,7 @@ class SSEEventType(StrEnum):
REASONING = "reasoning"
MCP_CALL = "mcp_call"
MCP_CALL_OUTPUT = "mcp_call_output"
TOOL_APPROVAL_REQUEST = "tool_approval_request"
THREAD_MESSAGE_SENT = "thread_message_sent"
THREAD_MESSAGE_RECEIVED = "thread_message_received"
THREAD_CONTEXT_COMPACTED = "thread_context_compacted"
Expand Down Expand Up @@ -87,12 +88,44 @@ class BlockType(StrEnum):


class SessionStatus(StrEnum):
"""Session run-status values (``session_status``)."""
"""Session run-status values (``session_status``).

Top-level ``Session.status`` ∈ {idle, running, terminated, rescheduled};
the ``session_status`` event ``data.session_status`` value set is the
same plus ``deleted`` (a delete returns a tombstone, not a Session with
``status=deleted``). ``idle`` also covers waiting for tool approval,
signalled by ``stop_reason.type=requires_action``.
"""

IDLE = "idle"
RUNNING = "running"
RESCHEDULING = "rescheduling"
RESCHEDULED = "rescheduled"
TERMINATED = "terminated"
DELETED = "deleted"


class StopReasonType(StrEnum):
"""``stop_reason.type`` carried by ``session_status`` idle events.

Only ``idle`` carries ``stop_reason``; a running Session's
``stop_reason`` is ``null``.
"""

END_TURN = "end_turn"
REQUIRES_ACTION = "requires_action"
RETRIES_EXHAUSTED = "retries_exhausted"


class PermissionPolicyType(StrEnum):
"""Tool approval policy (``permission_policy.type``).

``always_allow`` (default) executes the tool without asking;
``always_ask`` produces a ``tool_approval_request`` per call.
Subagent/subthread ``always_ask`` degrades to ``always_allow``.
"""

ALWAYS_ALLOW = "always_allow"
ALWAYS_ASK = "always_ask"


class WebhookStatus(StrEnum):
Expand Down
34 changes: 34 additions & 0 deletions dashscope/agentstudio/resources/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
File,
ServerEvent,
Session,
SessionResource,
SessionThread,
Skill,
SkillVersion,
Vault,
Expand Down Expand Up @@ -63,6 +65,14 @@ def _coerce_session(payload: Mapping[str, Any]) -> Session:
return Session(**dict(payload))


def _coerce_session_resource(payload: Mapping[str, Any]) -> SessionResource:
return SessionResource(**dict(payload))


def _coerce_session_thread(payload: Mapping[str, Any]) -> SessionThread:
return SessionThread(**dict(payload))


def _coerce_vault(payload: Mapping[str, Any]) -> Vault:
return Vault(**dict(payload))

Expand Down Expand Up @@ -104,3 +114,27 @@ def _events_path(session_id: str) -> str:

def _stream_path(session_id: str) -> str:
return f"/sessions/{session_id}/events/stream"


def _session_resources_path(session_id: str) -> str:
return f"/sessions/{session_id}/resources"


def _session_resource_item_path(session_id: str, resource_id: str) -> str:
return f"/sessions/{session_id}/resources/{resource_id}"


def _session_threads_path(session_id: str) -> str:
return f"/sessions/{session_id}/threads"


def _session_thread_item_path(session_id: str, thread_id: str) -> str:
return f"/sessions/{session_id}/threads/{thread_id}"


def _session_thread_events_path(session_id: str, thread_id: str) -> str:
return f"/sessions/{session_id}/threads/{thread_id}/events"


def _session_thread_archive_path(session_id: str, thread_id: str) -> str:
return f"/sessions/{session_id}/threads/{thread_id}/archive"
136 changes: 136 additions & 0 deletions dashscope/agentstudio/resources/security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
"""Security center read interface.

Exposes the two highest-value read endpoints: ``GET /security/overview``
(24h dashboard) and ``GET /security/agent_logs`` (alert list). Other
security endpoints (asset summary, policies, alert detail, export,
authorization, activation) are not surfaced here.
"""

from __future__ import annotations

from typing import Any, Mapping, Optional, Sequence

from dashscope.agentstudio.types import SecurityAlertList, SecurityOverview
from dashscope.agentstudio.types.params import SecurityListAgentLogsParams

_PATH_SECURITY_OVERVIEW = "/security/overview"
_PATH_SECURITY_AGENT_LOGS = "/security/agent_logs"


def _overview_from_payload(payload: Mapping[str, Any]) -> SecurityOverview:
return SecurityOverview(**dict(payload))


def _alert_list_from_payload(payload: Mapping[str, Any]) -> SecurityAlertList:
return SecurityAlertList(**dict(payload))


class Security:
"""Security center read interface (overview + agent logs)."""

def __init__(self, client) -> None:
self._client = client

def overview(self) -> SecurityOverview:
"""24h protection dashboard (``GET /security/overview``)."""
resp = self._client.transport.request(
"GET",
_PATH_SECURITY_OVERVIEW,
)
return _overview_from_payload(resp.data)

def list_agent_logs(
self,
*,
current_page: Optional[int] = None,
page_size: Optional[int] = None,
risk_level: Optional[str] = None,
status: Optional[str] = None,
risk_name: Optional[str] = None,
app_name: Optional[str] = None,
asset_type: Optional[str] = None,
vendor: Optional[str] = None,
order_by: Optional[str] = None,
order: Optional[str] = None,
lang: Optional[str] = None,
status_list: Optional[Sequence[str]] = None,
) -> SecurityAlertList:
"""Alert list (``GET /security/agent_logs``).

Page-number pagination: pass ``current_page + 1`` for the next
page, or use the returned ``next_page`` cursor if non-null.
``check_time`` / ``handle_time`` on rows are millisecond strings.
"""
params = SecurityListAgentLogsParams(
current_page=current_page,
page_size=page_size,
risk_level=risk_level,
status=status,
risk_name=risk_name,
app_name=app_name,
asset_type=asset_type,
vendor=vendor,
order_by=order_by,
order=order,
lang=lang,
status_list=status_list,
).to_dict()
resp = self._client.transport.request(
"GET",
_PATH_SECURITY_AGENT_LOGS,
params=params,
)
return _alert_list_from_payload(resp.data)


class AsyncSecurity:
"""Async security center read interface."""

def __init__(self, client) -> None:
self._client = client

async def overview(self) -> SecurityOverview:
resp = await self._client.transport.request(
"GET",
_PATH_SECURITY_OVERVIEW,
)
return _overview_from_payload(resp.data)

async def list_agent_logs(
self,
*,
current_page: Optional[int] = None,
page_size: Optional[int] = None,
risk_level: Optional[str] = None,
status: Optional[str] = None,
risk_name: Optional[str] = None,
app_name: Optional[str] = None,
asset_type: Optional[str] = None,
vendor: Optional[str] = None,
order_by: Optional[str] = None,
order: Optional[str] = None,
lang: Optional[str] = None,
status_list: Optional[Sequence[str]] = None,
) -> SecurityAlertList:
params = SecurityListAgentLogsParams(
current_page=current_page,
page_size=page_size,
risk_level=risk_level,
status=status,
risk_name=risk_name,
app_name=app_name,
asset_type=asset_type,
vendor=vendor,
order_by=order_by,
order=order,
lang=lang,
status_list=status_list,
).to_dict()
resp = await self._client.transport.request(
"GET",
_PATH_SECURITY_AGENT_LOGS,
params=params,
)
return _alert_list_from_payload(resp.data)
Loading
Loading