-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add explicit HITL decision statuses #2
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,7 +3,9 @@ | |||||||||||||||||||||
| # SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| from dataclasses import asdict, dataclass | ||||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||||
| from typing import Any, Literal | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| DecisionStatus = Literal["approved", "modified", "rejected"] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||
|
|
@@ -20,11 +22,36 @@ class ConfirmationUIResult: | |||||||||||||||||||||
| :param new_tool_params: | ||||||||||||||||||||||
| Optional set of new parameters for the tool. For example, if the user chooses to modify the tool parameters, | ||||||||||||||||||||||
| they can provide a new set of parameters here. | ||||||||||||||||||||||
| :param status: | ||||||||||||||||||||||
| Optional explicit decision status. When omitted, built-in actions are normalized as: | ||||||||||||||||||||||
| `confirm` -> `approved`, `modify` -> `modified`, `reject` -> `rejected`. | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| action: str # "confirm", "reject", "modify" | ||||||||||||||||||||||
| feedback: str | None = None | ||||||||||||||||||||||
| new_tool_params: dict[str, Any] | None = None | ||||||||||||||||||||||
| status: DecisionStatus | None = None | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def resolved_status(self) -> DecisionStatus: | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| Resolve the explicit decision status for this UI result. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| :raises ValueError: | ||||||||||||||||||||||
| If neither a supported built-in action nor an explicit status is provided. | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| if self.status is not None: | ||||||||||||||||||||||
| return self.status | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if self.action == "confirm": | ||||||||||||||||||||||
| return "approved" | ||||||||||||||||||||||
| if self.action == "modify": | ||||||||||||||||||||||
| return "modified" | ||||||||||||||||||||||
| if self.action == "reject": | ||||||||||||||||||||||
| return "rejected" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| raise ValueError( | ||||||||||||||||||||||
| "Unsupported confirmation action. Provide one of 'confirm', 'modify', 'reject' or set 'status' explicitly." | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||
|
|
@@ -45,13 +72,36 @@ class ToolExecutionDecision: | |||||||||||||||||||||
| modified, this can contain the modification details. | ||||||||||||||||||||||
| :param final_tool_params: | ||||||||||||||||||||||
| Optional final parameters for the tool if execution is confirmed or modified. | ||||||||||||||||||||||
| :param status: | ||||||||||||||||||||||
| Explicit decision status. When omitted, it is inferred from the legacy `execute`/`feedback` shape to remain | ||||||||||||||||||||||
| backward-compatible with older serialized payloads. | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| tool_name: str | ||||||||||||||||||||||
| execute: bool | ||||||||||||||||||||||
| tool_call_id: str | None = None | ||||||||||||||||||||||
| feedback: str | None = None | ||||||||||||||||||||||
| final_tool_params: dict[str, Any] | None = None | ||||||||||||||||||||||
| status: DecisionStatus | None = None | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def __post_init__(self) -> None: | ||||||||||||||||||||||
| if self.status is None: | ||||||||||||||||||||||
| self.status = self._infer_status() | ||||||||||||||||||||||
| else: | ||||||||||||||||||||||
| self.execute = self.status != "rejected" | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a Useful? React with 👍 / 👎. |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def _infer_status(self) -> DecisionStatus: | ||||||||||||||||||||||
| if not self.execute: | ||||||||||||||||||||||
| return "rejected" | ||||||||||||||||||||||
| if self.feedback is not None and self.final_tool_params is not None: | ||||||||||||||||||||||
| return "modified" | ||||||||||||||||||||||
| return "approved" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def resolved_status(self) -> DecisionStatus: | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| Return the normalized decision status for this execution decision. | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| return self.status | ||||||||||||||||||||||
|
Comment on lines
+100
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def to_dict(self) -> dict[str, Any]: | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| enhancements: | ||
| - | | ||
| Adds an explicit `status` field to `ToolExecutionDecision` with the values | ||
| `approved`, `modified`, and `rejected`, while preserving the legacy | ||
| `execute` boolean for backward compatibility. `BlockingConfirmationStrategy` | ||
| now normalizes built-in confirmation UI actions into these explicit HITL | ||
| decision statuses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message could be more helpful by including the actual unsupported action and listing the valid values for the
statusfield. This assists developers in debugging customConfirmationUIimplementations.