Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UrlBlock - a block to navigate to a url #1461

Merged
merged 2 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
81 changes: 81 additions & 0 deletions skyvern/forge/sdk/workflow/models/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from skyvern.exceptions import (
ContextParameterValueNotFound,
DisabledBlockExecutionError,
FailedToNavigateToUrl,
MissingBrowserState,
MissingBrowserStatePage,
SkyvernException,
Expand Down Expand Up @@ -83,6 +84,7 @@ class BlockType(StrEnum):
LOGIN = "login"
WAIT = "wait"
FILE_DOWNLOAD = "file_download"
GOTO_URL = "goto_url"


class BlockStatus(StrEnum):
Expand Down Expand Up @@ -1799,6 +1801,84 @@ class LoginBlock(BaseTaskBlock):

class FileDownloadBlock(BaseTaskBlock):
block_type: Literal[BlockType.FILE_DOWNLOAD] = BlockType.FILE_DOWNLOAD


class UrlBlock(Block):
block_type: Literal[BlockType.GOTO_URL] = BlockType.GOTO_URL
url: str

async def execute(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using prepend_scheme_and_validate_url to validate the URL before navigation to ensure it is well-formed.

self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
) -> BlockResult:
workflow_run = await app.DATABASE.get_workflow_run(workflow_run_id=workflow_run_id, organization_id=organization_id)
if not workflow_run:
return await self.build_block_result(
success=False,
failure_reason="Workflow run not found",
output_parameter_value=None,
status=BlockStatus.failed,
workflow_run_block_id=workflow_run_block_id,
organization_id=organization_id,
)
# create a new browser state
browser_state: BrowserState | None = None
# the first task block will create the browser state and do the navigation
try:
browser_state = await app.BROWSER_MANAGER.get_or_create_for_workflow_run(
workflow_run=workflow_run, url=self.url,
)
except Exception as e:
LOG.exception(
"Failed to get browser state for url block",
workflow_run_id=workflow_run_id,
)
return await self.build_block_result(
success=False,
failure_reason=f"Skyvern browser has failed to navigate to url {self.url}",
output_parameter_value=None,
status=BlockStatus.failed,
workflow_run_block_id=workflow_run_block_id,
organization_id=organization_id,
)

# navigate to the url
working_page = await browser_state.get_working_page()
if not working_page:
LOG.error("Failed to get working page for url block", url=self.url, workflow_run_id=workflow_run_id)
return await self.build_block_result(
success=False,
failure_reason=f"Skyvern browser has failed to navigate to url {self.url}. The page might have been closed.",
output_parameter_value=None,
status=BlockStatus.failed,
workflow_run_block_id=workflow_run_block_id,
organization_id=organization_id,
)

try:
await browser_state.navigate_to_url(page=working_page, url=self.url)
except Exception as e:
LOG.exception(
"Failed to navigate to url",
url=self.url,
workflow_run_id=workflow_run_id,
)
return await self.build_block_result(
success=False,
failure_reason=f"Skyvern browser has failed to navigate to url {self.url}",
output_parameter_value=None,
status=BlockStatus.failed,
workflow_run_block_id=workflow_run_block_id,
organization_id=organization_id,
)

return await self.build_block_result(
success=True,
failure_reason=None,
output_parameter_value=None,
status=BlockStatus.completed,
workflow_run_block_id=workflow_run_block_id,
organization_id=organization_id,
)


BlockSubclasses = Union[
Expand All @@ -1817,5 +1897,6 @@ class FileDownloadBlock(BaseTaskBlock):
LoginBlock,
WaitBlock,
FileDownloadBlock,
UrlBlock,
]
BlockTypeVar = Annotated[BlockSubclasses, Field(discriminator="block_type")]
6 changes: 6 additions & 0 deletions skyvern/forge/sdk/workflow/models/yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,11 @@ class FileDownloadBlockYAML(BlockYAML):
totp_verification_url: str | None = None
totp_identifier: str | None = None
cache_actions: bool = False


class UrlBlockYAML(BlockYAML):
block_type: Literal[BlockType.GOTO_URL] = BlockType.GOTO_URL # type: ignore
url: str


PARAMETER_YAML_SUBCLASSES = (
Expand Down Expand Up @@ -337,6 +342,7 @@ class FileDownloadBlockYAML(BlockYAML):
| LoginBlockYAML
| WaitBlockYAML
| FileDownloadBlockYAML
| UrlBlockYAML
)
BLOCK_YAML_TYPES = Annotated[BLOCK_YAML_SUBCLASSES, Field(discriminator="block_type")]

Expand Down
Loading