add action hydration (#2675)

This commit is contained in:
Shuchang Zheng
2025-06-10 16:07:02 -07:00
committed by GitHub
parent f05170df61
commit 9a42c2ad9f
2 changed files with 110 additions and 0 deletions

View File

@@ -58,6 +58,7 @@ from skyvern.forge.sdk.db.utils import (
convert_to_workflow_run_block,
convert_to_workflow_run_output_parameter,
convert_to_workflow_run_parameter,
hydrate_action,
)
from skyvern.forge.sdk.log_artifacts import save_workflow_run_logs
from skyvern.forge.sdk.models import Step, StepStatus
@@ -417,6 +418,26 @@ class AgentDB:
LOG.error("UnexpectedError", exc_info=True)
raise
async def get_task_actions_hydrated(self, task_id: str, organization_id: str | None = None) -> list[Action]:
try:
async with self.Session() as session:
query = (
select(ActionModel)
.filter(ActionModel.organization_id == organization_id)
.filter(ActionModel.task_id == task_id)
.order_by(ActionModel.created_at)
)
actions = (await session.scalars(query)).all()
return [hydrate_action(action) for action in actions]
except SQLAlchemyError:
LOG.error("SQLAlchemyError", exc_info=True)
raise
except Exception:
LOG.error("UnexpectedError", exc_info=True)
raise
async def get_tasks_actions(self, task_ids: list[str], organization_id: str | None = None) -> list[Action]:
try:
async with self.Session() as session:

View File

@@ -7,6 +7,7 @@ import structlog
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
from skyvern.forge.sdk.db.models import (
ActionModel,
ArtifactModel,
AWSSecretParameterModel,
BitwardenLoginCredentialParameterModel,
@@ -46,9 +47,55 @@ from skyvern.forge.sdk.workflow.models.workflow import (
WorkflowStatus,
)
from skyvern.schemas.runs import ProxyLocation
from skyvern.webeye.actions.actions import (
Action,
ActionType,
CheckboxAction,
ClickAction,
CompleteAction,
DownloadFileAction,
DragAction,
ExtractAction,
InputTextAction,
KeypressAction,
LeftMouseAction,
MoveAction,
NullAction,
ReloadPageAction,
ScrollAction,
SelectOptionAction,
SolveCaptchaAction,
TerminateAction,
UploadFileAction,
VerificationCodeAction,
WaitAction,
)
LOG = structlog.get_logger()
# Mapping of action types to their corresponding action classes
ACTION_TYPE_TO_CLASS = {
ActionType.CLICK: ClickAction,
ActionType.INPUT_TEXT: InputTextAction,
ActionType.UPLOAD_FILE: UploadFileAction,
ActionType.DOWNLOAD_FILE: DownloadFileAction,
ActionType.NULL_ACTION: NullAction,
ActionType.TERMINATE: TerminateAction,
ActionType.COMPLETE: CompleteAction,
ActionType.SELECT_OPTION: SelectOptionAction,
ActionType.CHECKBOX: CheckboxAction,
ActionType.WAIT: WaitAction,
ActionType.SOLVE_CAPTCHA: SolveCaptchaAction,
ActionType.RELOAD_PAGE: ReloadPageAction,
ActionType.EXTRACT: ExtractAction,
ActionType.SCROLL: ScrollAction,
ActionType.KEYPRESS: KeypressAction,
ActionType.MOVE: MoveAction,
ActionType.DRAG: DragAction,
ActionType.VERIFICATION_CODE: VerificationCodeAction,
ActionType.LEFT_MOUSE: LeftMouseAction,
}
@typing.no_type_check
def _custom_json_serializer(*args, **kwargs) -> str:
@@ -426,3 +473,45 @@ def convert_to_workflow_run_block(
block.include_action_history_in_verification = task.include_action_history_in_verification
return block
def hydrate_action(action_model: ActionModel) -> Action:
"""
Convert ActionModel to the appropriate Action type based on action_type.
The action_json contains all the metadata of different types of actions.
"""
# Create base action data from the model
action_data = {
"action_type": action_model.action_type,
"status": action_model.status,
"action_id": action_model.action_id,
"source_action_id": action_model.source_action_id,
"organization_id": action_model.organization_id,
"workflow_run_id": action_model.workflow_run_id,
"task_id": action_model.task_id,
"step_id": action_model.step_id,
"step_order": action_model.step_order,
"action_order": action_model.action_order,
"confidence_float": action_model.confidence_float,
"reasoning": action_model.reasoning,
"intention": action_model.intention,
"response": action_model.response,
"element_id": action_model.element_id,
"skyvern_element_hash": action_model.skyvern_element_hash,
"skyvern_element_data": action_model.skyvern_element_data,
"created_at": action_model.created_at,
"modified_at": action_model.modified_at,
}
# Merge with action_json data, skipping None values
if action_model.action_json:
for key, value in action_model.action_json.items():
if value is not None:
action_data[key] = value
# Get the appropriate action class and instantiate it
action_class = ACTION_TYPE_TO_CLASS.get(action_model.action_type)
if action_class is None:
raise ValueError(f"Unsupported action type: {action_model.action_type}")
return action_class(**action_data)