support new tab magic link logic (#3797)
This commit is contained in:
@@ -2185,6 +2185,7 @@ class ForgeAgent:
|
||||
complete_criterion=task.complete_criterion.strip() if task.complete_criterion else None,
|
||||
terminate_criterion=task.terminate_criterion.strip() if task.terminate_criterion else None,
|
||||
parse_select_feature_enabled=context.enable_parse_select_in_extract,
|
||||
has_magic_link_page=context.has_magic_link_page(task.task_id),
|
||||
)
|
||||
|
||||
# Store static prompt for caching and return dynamic prompt
|
||||
@@ -2214,6 +2215,7 @@ class ForgeAgent:
|
||||
complete_criterion=task.complete_criterion.strip() if task.complete_criterion else None,
|
||||
terminate_criterion=task.terminate_criterion.strip() if task.terminate_criterion else None,
|
||||
parse_select_feature_enabled=context.enable_parse_select_in_extract,
|
||||
has_magic_link_page=context.has_magic_link_page(task.task_id),
|
||||
)
|
||||
|
||||
return full_prompt, use_caching
|
||||
@@ -3273,10 +3275,13 @@ class ForgeAgent:
|
||||
if not otp_value or otp_value.get_otp_type() != OTPType.MAGIC_LINK:
|
||||
return []
|
||||
|
||||
# TODO: not sure whether all magic links can directly login + navigate to the homepage
|
||||
# always open a new tab to navigate to the magic link
|
||||
page = await browser_state.new_page()
|
||||
context = skyvern_context.ensure_context()
|
||||
context.add_magic_link_page(task.task_id, page)
|
||||
|
||||
return [
|
||||
GotoUrlAction(
|
||||
action_type=ActionType.GOTO_URL,
|
||||
reasoning="Navigating to the magic link URL to verify the login",
|
||||
intention="Navigating to the magic link URL to verify the login",
|
||||
url=otp_value.value,
|
||||
@@ -3286,6 +3291,7 @@ class ForgeAgent:
|
||||
step_id=step.step_id,
|
||||
step_order=step.order,
|
||||
action_order=0,
|
||||
is_magic_link=True,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Reply in JSON format with the following keys:
|
||||
"user_detail_query": str, // Think of this value as a Jeopardy question and the intention behind the action. Ask the user for the details you need for executing this action. Ask the question even if the details are disclosed in user goal or user details. If it's a text field, ask for the text. If it's a file upload, ask for the file. If it's a dropdown, ask for the relevant information. If you are clicking on something specific, ask about what the intention is behind the click and what to click on. If you're downloading a file and you have multiple options, ask the user which one to download. Examples are: "What product ID should I input into the search bar?", "What file should I upload?", "What is the previous insurance provider of the user?", "Which invoice should I download?", "Does the user have any pets?". If the action doesn't require any user details, describe the intention behind the action.
|
||||
"user_detail_answer": str, // The answer to the `user_detail_query`. The source of this answer can be user goal or user details.
|
||||
"confidence_float": float, // The confidence of the action. Pick a number between 0.0 and 1.0. 0.0 means no confidence, 1.0 means full confidence
|
||||
"action_type": str, // It's a string enum: "CLICK", "INPUT_TEXT", "UPLOAD_FILE", "SELECT_OPTION", "WAIT", "SOLVE_CAPTCHA", "COMPLETE", "TERMINATE". "CLICK" is an element you'd like to click. "INPUT_TEXT" is an element you'd like to input text into. "UPLOAD_FILE" is an element you'd like to upload a file into. "SELECT_OPTION" is an element you'd like to select an option from. "WAIT" action should be used if there are no actions to take and there is some indication on screen that waiting could yield more actions. "WAIT" should not be used if there are actions to take. "SOLVE_CAPTCHA" should be used if there's a captcha to solve on the screen. "COMPLETE" is used when the {{"complete criterion has been met" if complete_criterion else "user goal has been achieved"}} AND if there's any data extraction goal, you should be able to get data from the page. Never return a COMPLETE action unless the {{ "complete criterion is met" if complete_criterion else "user goal is achieved" }}. "TERMINATE" is used to terminate the whole task with a failure when it doesn't seem like the user goal can be achieved. Do not use "TERMINATE" if waiting could lead the user towards the goal. Only return "TERMINATE" if you are on a page where the user goal cannot be achieved. All other actions are ignored when "TERMINATE" is returned.
|
||||
"action_type": str, // It's a string enum: "CLICK", "INPUT_TEXT", "UPLOAD_FILE", "SELECT_OPTION", "WAIT", "SOLVE_CAPTCHA", "COMPLETE", "TERMINATE"{{', "CLOSE_PAGE"' if has_magic_link_page else ""}}. "CLICK" is an element you'd like to click. "INPUT_TEXT" is an element you'd like to input text into. "UPLOAD_FILE" is an element you'd like to upload a file into. "SELECT_OPTION" is an element you'd like to select an option from. "WAIT" action should be used if there are no actions to take and there is some indication on screen that waiting could yield more actions. "WAIT" should not be used if there are actions to take. "SOLVE_CAPTCHA" should be used if there's a captcha to solve on the screen. "COMPLETE" is used when the {{"complete criterion has been met" if complete_criterion else "user goal has been achieved"}} AND if there's any data extraction goal, you should be able to get data from the page. Never return a COMPLETE action unless the {{ "complete criterion is met" if complete_criterion else "user goal is achieved" }}. "TERMINATE" is used to terminate the whole task with a failure when it doesn't seem like the user goal can be achieved. Do not use "TERMINATE" if waiting could lead the user towards the goal. Only return "TERMINATE" if you are on a page where the user goal cannot be achieved. All other actions are ignored when "TERMINATE" is returned.{{' "CLOSE_PAGE" is used to close the current page when it is impossible to achieve the user goal on the current page.' if has_magic_link_page else ''}}
|
||||
"id": str, // The id of the element to take action on. The id has to be one from the elements list
|
||||
"text": str, // Text for INPUT_TEXT action only
|
||||
"file_url": str, // The url of the file to upload if applicable. This field must be present for UPLOAD_FILE but can also be present for CLICK only if the click is to upload the file. It should be null otherwise.
|
||||
|
||||
@@ -3,7 +3,7 @@ from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from playwright.async_api import Frame
|
||||
from playwright.async_api import Frame, Page
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -46,6 +46,15 @@ class SkyvernContext:
|
||||
script_run_parameters: dict[str, Any] = field(default_factory=dict)
|
||||
script_mode: bool = False
|
||||
ai_mode_override: str | None = None
|
||||
|
||||
# magic link handling
|
||||
# task_id is the key, page is the value
|
||||
# we only consider the page is a magic link page in the same task scope
|
||||
# for example, login block has a magic link page,
|
||||
# but it will only be considered as a magic link page in the login block scope
|
||||
# next blocks won't consider the page as a magic link page
|
||||
magic_link_pages: dict[str, Page] = field(default_factory=dict)
|
||||
|
||||
"""
|
||||
Example output value:
|
||||
{"loop_value": "str", "output_parameter": "the key of the parameter", "output_value": Any}
|
||||
@@ -62,6 +71,19 @@ class SkyvernContext:
|
||||
if task_id in self.totp_codes:
|
||||
self.totp_codes.pop(task_id)
|
||||
|
||||
def add_magic_link_page(self, task_id: str, page: Page) -> None:
|
||||
self.magic_link_pages[task_id] = page
|
||||
|
||||
def has_magic_link_page(self, task_id: str) -> bool:
|
||||
if task_id not in self.magic_link_pages:
|
||||
return False
|
||||
|
||||
page = self.magic_link_pages[task_id]
|
||||
if page.is_closed():
|
||||
self.magic_link_pages.pop(task_id)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
_context: ContextVar[SkyvernContext | None] = ContextVar(
|
||||
"Global context",
|
||||
|
||||
Reference in New Issue
Block a user