From 472b8b4452497a42869ade9b895eee0a3f3631a5 Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Sun, 24 Aug 2025 17:34:31 -0700 Subject: [PATCH] Script action - store text and option (#3289) --- .../core/script_generations/skyvern_page.py | 25 +++++++++++++++++-- skyvern/forge/sdk/db/client.py | 2 +- skyvern/forge/sdk/db/utils.py | 7 ++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/skyvern/core/script_generations/skyvern_page.py b/skyvern/core/script_generations/skyvern_page.py index 79231cbe..7cc31088 100644 --- a/skyvern/core/script_generations/skyvern_page.py +++ b/skyvern/core/script_generations/skyvern_page.py @@ -20,7 +20,7 @@ from skyvern.forge.sdk.core import skyvern_context from skyvern.utils.prompt_engine import load_prompt_with_elements from skyvern.webeye.actions import handler_utils from skyvern.webeye.actions.action_types import ActionType -from skyvern.webeye.actions.actions import Action, ActionStatus +from skyvern.webeye.actions.actions import Action, ActionStatus, SelectOption from skyvern.webeye.browser_factory import BrowserState from skyvern.webeye.scraper.scraper import ScrapedPage, scrape_website @@ -164,6 +164,7 @@ class SkyvernPage: intention=intention, status=action_status, data=data, + kwargs=kwargs, ) # Auto-create screenshot artifact after execution @@ -174,7 +175,10 @@ class SkyvernPage: return decorator async def goto(self, url: str, timeout: float = settings.BROWSER_LOADING_TIMEOUT_MS) -> None: - await self.page.goto(url, timeout=timeout) + await self.page.goto( + url, + timeout=timeout, + ) async def _create_action_before_execution( self, @@ -182,6 +186,7 @@ class SkyvernPage: intention: str = "", status: ActionStatus = ActionStatus.pending, data: str | dict[str, Any] = "", + kwargs: dict[str, Any] | None = None, ) -> Action | None: """Create an action record in the database before execution if task_id and step_id are available.""" try: @@ -190,7 +195,20 @@ class SkyvernPage: return None # Create action record. TODO: store more action fields + kwargs = kwargs or {} + text = kwargs.get("text") + option_value = kwargs.get("option") + select_option = SelectOption(value=option_value) if option_value else None + response: str | None = kwargs.get("response") + if not response: + if action_type == ActionType.INPUT_TEXT: + response = text + elif action_type == ActionType.SELECT_OPTION: + if select_option: + response = select_option.value + action = Action( + element_id="", action_type=action_type, status=status, organization_id=context.organization_id, @@ -201,6 +219,9 @@ class SkyvernPage: action_order=0, # Will be updated by the system if needed intention=intention, reasoning=f"Auto-generated action for {action_type.value}", + text=text, + option=select_option, + response=response, ) created_action = await app.DATABASE.create_action(action) diff --git a/skyvern/forge/sdk/db/client.py b/skyvern/forge/sdk/db/client.py index c3c97094..d6ea1b2f 100644 --- a/skyvern/forge/sdk/db/client.py +++ b/skyvern/forge/sdk/db/client.py @@ -474,7 +474,7 @@ class AgentDB: .order_by(ActionModel.created_at.desc()) ) actions = (await session.scalars(query)).all() - return [Action.model_validate(action) for action in actions] + return [hydrate_action(action, empty_element_id=True) for action in actions] except SQLAlchemyError: LOG.error("SQLAlchemyError", exc_info=True) diff --git a/skyvern/forge/sdk/db/utils.py b/skyvern/forge/sdk/db/utils.py index 02d36434..f507c7fc 100644 --- a/skyvern/forge/sdk/db/utils.py +++ b/skyvern/forge/sdk/db/utils.py @@ -555,12 +555,15 @@ def convert_to_script_block(script_block_model: ScriptBlockModel) -> ScriptBlock ) -def hydrate_action(action_model: ActionModel) -> Action: +def hydrate_action(action_model: ActionModel, empty_element_id: bool = False) -> 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 + element_id = action_model.element_id + if empty_element_id: + element_id = element_id or "" action_data = { "action_type": action_model.action_type, "status": action_model.status, @@ -576,7 +579,7 @@ def hydrate_action(action_model: ActionModel) -> Action: "reasoning": action_model.reasoning, "intention": action_model.intention, "response": action_model.response, - "element_id": action_model.element_id, + "element_id": element_id, "skyvern_element_hash": action_model.skyvern_element_hash, "skyvern_element_data": action_model.skyvern_element_data, "created_at": action_model.created_at,