Make SkyvernPage extend Playwright (#3934)
This commit is contained in:
committed by
GitHub
parent
2e3879bc37
commit
23ebd72d4a
@@ -123,8 +123,6 @@ class ScriptSkyvernPage(SkyvernPage):
|
||||
action: ActionType,
|
||||
*args: Any,
|
||||
prompt: str = "",
|
||||
data: str | dict[str, Any] = "",
|
||||
intention: str = "",
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""
|
||||
@@ -150,9 +148,11 @@ class ScriptSkyvernPage(SkyvernPage):
|
||||
}
|
||||
|
||||
# Backward compatibility: use intention if provided and prompt is empty
|
||||
intention = kwargs.get("intention", None)
|
||||
if intention and not prompt:
|
||||
prompt = intention
|
||||
|
||||
data = kwargs.get("data", None)
|
||||
meta = ActionMetadata(prompt, data)
|
||||
call = ActionCall(action, args, kwargs, meta)
|
||||
|
||||
@@ -199,7 +199,6 @@ class ScriptSkyvernPage(SkyvernPage):
|
||||
action_type=action,
|
||||
intention=prompt,
|
||||
status=action_status,
|
||||
data=data,
|
||||
kwargs=kwargs,
|
||||
call_result=call.result,
|
||||
)
|
||||
@@ -262,7 +261,6 @@ class ScriptSkyvernPage(SkyvernPage):
|
||||
action_type: ActionType,
|
||||
intention: str = "",
|
||||
status: ActionStatus = ActionStatus.pending,
|
||||
data: str | dict[str, Any] = "",
|
||||
kwargs: dict[str, Any] | None = None,
|
||||
call_result: Any | None = None,
|
||||
) -> Action | None:
|
||||
@@ -396,7 +394,7 @@ class ScriptSkyvernPage(SkyvernPage):
|
||||
# If screenshot creation fails, don't block execution
|
||||
pass
|
||||
|
||||
async def goto(self, url: str, timeout: float = settings.BROWSER_LOADING_TIMEOUT_MS) -> None:
|
||||
async def goto(self, url: str, **kwargs: Any) -> None:
|
||||
url = render_template(url)
|
||||
url = prepend_scheme_and_validate_url(url)
|
||||
|
||||
@@ -405,10 +403,8 @@ class ScriptSkyvernPage(SkyvernPage):
|
||||
if context and context.script_mode:
|
||||
print(f"🌐 Navigating to: {url}")
|
||||
|
||||
await self.page.goto(
|
||||
url,
|
||||
timeout=timeout,
|
||||
)
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
await self.page.goto(url, timeout=timeout, **kwargs)
|
||||
|
||||
if context and context.script_mode:
|
||||
print(" ✓ Page loaded")
|
||||
|
||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import copy
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
from typing import Any, Callable, Literal, overload
|
||||
|
||||
import structlog
|
||||
@@ -19,10 +18,6 @@ from skyvern.webeye.actions.action_types import ActionType
|
||||
LOG = structlog.get_logger()
|
||||
|
||||
|
||||
class Driver(StrEnum):
|
||||
PLAYWRIGHT = "playwright"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ActionMetadata:
|
||||
prompt: str = ""
|
||||
@@ -41,7 +36,7 @@ class ActionCall:
|
||||
error: Exception | None = None # populated if failed
|
||||
|
||||
|
||||
class SkyvernPage:
|
||||
class SkyvernPage(Page):
|
||||
"""
|
||||
A lightweight adapter for the selected driver that:
|
||||
1. Executes actual browser commands
|
||||
@@ -54,21 +49,32 @@ class SkyvernPage:
|
||||
page: Page,
|
||||
ai: SkyvernPageAi,
|
||||
) -> None:
|
||||
super().__init__(page)
|
||||
self.page = page
|
||||
self.current_label: str | None = None
|
||||
self._ai = ai
|
||||
|
||||
def __getattribute__(self, name: str) -> Any:
|
||||
page = object.__getattribute__(self, "page")
|
||||
if hasattr(page, name):
|
||||
for cls in type(self).__mro__:
|
||||
if cls is Page:
|
||||
break
|
||||
if name in cls.__dict__:
|
||||
return object.__getattribute__(self, name)
|
||||
return getattr(page, name)
|
||||
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
async def _decorate_call(
|
||||
self,
|
||||
fn: Callable,
|
||||
action: ActionType,
|
||||
*args: Any,
|
||||
prompt: str = "",
|
||||
data: str | dict[str, Any] = "",
|
||||
intention: str = "", # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
return await fn(self, *args, prompt=prompt, data=data, intention=intention, **kwargs)
|
||||
return await fn(self, *args, prompt=prompt, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def action_wrap(
|
||||
@@ -79,20 +85,17 @@ class SkyvernPage:
|
||||
skyvern_page: SkyvernPage,
|
||||
*args: Any,
|
||||
prompt: str = "",
|
||||
data: str | dict[str, Any] = "",
|
||||
intention: str = "", # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
return await skyvern_page._decorate_call(
|
||||
fn, action, *args, prompt=prompt, data=data, intention=intention, **kwargs
|
||||
)
|
||||
return await skyvern_page._decorate_call(fn, action, *args, prompt=prompt, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
async def goto(self, url: str, timeout: float = settings.BROWSER_LOADING_TIMEOUT_MS) -> None:
|
||||
await self.page.goto(url, timeout=timeout)
|
||||
async def goto(self, url: str, **kwargs: Any) -> None:
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
await self.page.goto(url, timeout=timeout, **kwargs)
|
||||
|
||||
######### Public Interfaces #########
|
||||
|
||||
@@ -103,9 +106,6 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str | None: ...
|
||||
|
||||
@@ -115,9 +115,6 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str | None: ...
|
||||
|
||||
@@ -128,9 +125,6 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str | None:
|
||||
"""Click an element using a CSS selector, AI-powered prompt matching, or both.
|
||||
@@ -144,8 +138,7 @@ class SkyvernPage:
|
||||
selector: CSS selector for the target element.
|
||||
prompt: Natural language description of which element to click.
|
||||
ai: AI behavior mode. Defaults to "fallback" which tries selector first, then AI.
|
||||
data: Additional context data for AI processing.
|
||||
timeout: Maximum time to wait for the click action in milliseconds.
|
||||
**kwargs: All Playwright click parameters (timeout, force, modifiers, etc.)
|
||||
|
||||
Returns:
|
||||
The selector string that was successfully used to click the element, or None.
|
||||
@@ -163,12 +156,16 @@ class SkyvernPage:
|
||||
```
|
||||
"""
|
||||
# Backward compatibility
|
||||
intention = kwargs.pop("intention", None)
|
||||
if intention is not None and prompt is None:
|
||||
prompt = intention
|
||||
|
||||
if not selector and not prompt:
|
||||
raise ValueError("Missing input: pass a selector and/or a prompt.")
|
||||
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
data = kwargs.pop("data", None)
|
||||
|
||||
context = skyvern_context.current()
|
||||
if context and context.ai_mode_override:
|
||||
ai = context.ai_mode_override
|
||||
@@ -206,8 +203,8 @@ class SkyvernPage:
|
||||
)
|
||||
|
||||
if selector:
|
||||
locator = self.page.locator(selector, **kwargs)
|
||||
await locator.click(timeout=timeout)
|
||||
locator = self.page.locator(selector)
|
||||
await locator.click(timeout=timeout, **kwargs)
|
||||
|
||||
return selector
|
||||
|
||||
@@ -219,11 +216,9 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
totp_identifier: str | None = None,
|
||||
totp_url: str | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str: ...
|
||||
|
||||
@overload
|
||||
@@ -234,11 +229,9 @@ class SkyvernPage:
|
||||
value: str | None = None,
|
||||
selector: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
totp_identifier: str | None = None,
|
||||
totp_url: str | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str: ...
|
||||
|
||||
@action_wrap(ActionType.INPUT_TEXT)
|
||||
@@ -249,11 +242,9 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
totp_identifier: str | None = None,
|
||||
totp_url: str | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
"""Fill an input field using a CSS selector, AI-powered prompt matching, or both.
|
||||
|
||||
@@ -267,8 +258,6 @@ class SkyvernPage:
|
||||
value: The text value to input into the field.
|
||||
prompt: Natural language description of which field to fill and what value.
|
||||
ai: AI behavior mode. Defaults to "fallback" which tries selector first, then AI.
|
||||
data: Additional context data for AI processing.
|
||||
timeout: Maximum time to wait for the fill action in milliseconds.
|
||||
totp_identifier: TOTP identifier for time-based one-time password fields.
|
||||
totp_url: URL to fetch TOTP codes from for authentication.
|
||||
|
||||
@@ -293,12 +282,16 @@ class SkyvernPage:
|
||||
"""
|
||||
|
||||
# Backward compatibility
|
||||
intention = kwargs.pop("intention", None)
|
||||
if intention is not None and prompt is None:
|
||||
prompt = intention
|
||||
|
||||
if not selector and not prompt:
|
||||
raise ValueError("Missing input: pass a selector and/or a prompt.")
|
||||
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
data = kwargs.pop("data", None)
|
||||
|
||||
return await self._input_text(
|
||||
selector=selector,
|
||||
value=value or "",
|
||||
@@ -317,19 +310,21 @@ class SkyvernPage:
|
||||
value: str,
|
||||
ai: str | None = "fallback",
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
totp_identifier: str | None = None,
|
||||
totp_url: str | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
# Backward compatibility
|
||||
intention = kwargs.pop("intention", None)
|
||||
if intention is not None and prompt is None:
|
||||
prompt = intention
|
||||
|
||||
if not selector and not prompt:
|
||||
raise ValueError("Missing input: pass a selector and/or a prompt.")
|
||||
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
data = kwargs.pop("data", None)
|
||||
|
||||
return await self._input_text(
|
||||
selector=selector,
|
||||
value=value,
|
||||
@@ -419,8 +414,7 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
**kwargs: Any,
|
||||
) -> str: ...
|
||||
|
||||
@overload
|
||||
@@ -431,8 +425,7 @@ class SkyvernPage:
|
||||
files: str | None = None,
|
||||
selector: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
**kwargs: Any,
|
||||
) -> str: ...
|
||||
|
||||
@action_wrap(ActionType.UPLOAD_FILE)
|
||||
@@ -443,17 +436,19 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
# Backward compatibility
|
||||
intention = kwargs.pop("intention", None)
|
||||
if intention is not None and prompt is None:
|
||||
prompt = intention
|
||||
|
||||
if not selector and not prompt:
|
||||
raise ValueError("Missing input: pass a selector and/or a prompt.")
|
||||
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
data = kwargs.pop("data", None)
|
||||
|
||||
context = skyvern_context.current()
|
||||
if context and context.ai_mode_override:
|
||||
ai = context.ai_mode_override
|
||||
@@ -466,7 +461,7 @@ class SkyvernPage:
|
||||
try:
|
||||
file_path = await download_file(files)
|
||||
locator = self.page.locator(selector)
|
||||
await locator.set_input_files(file_path)
|
||||
await locator.set_input_files(file_path, **kwargs)
|
||||
except Exception as e:
|
||||
error_to_raise = e
|
||||
selector = None
|
||||
@@ -501,7 +496,7 @@ class SkyvernPage:
|
||||
|
||||
file_path = await download_file(files)
|
||||
locator = self.page.locator(selector)
|
||||
await locator.set_input_files(file_path, timeout=timeout)
|
||||
await locator.set_input_files(file_path, timeout=timeout, **kwargs)
|
||||
return files
|
||||
|
||||
@overload
|
||||
@@ -512,9 +507,6 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str | None: ...
|
||||
|
||||
@@ -526,9 +518,6 @@ class SkyvernPage:
|
||||
value: str | None = None,
|
||||
selector: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str | None: ...
|
||||
|
||||
@@ -540,9 +529,6 @@ class SkyvernPage:
|
||||
*,
|
||||
prompt: str | None = None,
|
||||
ai: str | None = "fallback",
|
||||
data: str | dict[str, Any] | None = None,
|
||||
timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> str | None:
|
||||
"""Select an option from a dropdown using a CSS selector, AI-powered prompt matching, or both.
|
||||
@@ -557,8 +543,6 @@ class SkyvernPage:
|
||||
value: The option value to select.
|
||||
prompt: Natural language description of which option to select.
|
||||
ai: AI behavior mode. Defaults to "fallback" which tries selector first, then AI.
|
||||
data: Additional context data for AI processing.
|
||||
timeout: Maximum time to wait for the select action in milliseconds.
|
||||
|
||||
Returns:
|
||||
The value that was successfully selected.
|
||||
@@ -581,12 +565,16 @@ class SkyvernPage:
|
||||
"""
|
||||
|
||||
# Backward compatibility
|
||||
intention = kwargs.pop("intention", None)
|
||||
if intention is not None and prompt is None:
|
||||
prompt = intention
|
||||
|
||||
if not selector and not prompt:
|
||||
raise ValueError("Missing input: pass a selector and/or a prompt.")
|
||||
|
||||
timeout = kwargs.pop("timeout", settings.BROWSER_ACTION_TIMEOUT_MS)
|
||||
data = kwargs.pop("data", None)
|
||||
|
||||
context = skyvern_context.current()
|
||||
if context and context.ai_mode_override:
|
||||
ai = context.ai_mode_override
|
||||
@@ -631,46 +619,30 @@ class SkyvernPage:
|
||||
async def wait(
|
||||
self,
|
||||
seconds: float,
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
await asyncio.sleep(seconds)
|
||||
|
||||
@action_wrap(ActionType.NULL_ACTION)
|
||||
async def null_action(
|
||||
self, prompt: str | None = None, data: str | dict[str, Any] | None = None, intention: str | None = None
|
||||
) -> None:
|
||||
async def null_action(self, **kwargs: Any) -> None:
|
||||
return
|
||||
|
||||
@action_wrap(ActionType.SOLVE_CAPTCHA)
|
||||
async def solve_captcha(
|
||||
self, prompt: str | None = None, data: str | dict[str, Any] | None = None, intention: str | None = None
|
||||
) -> None:
|
||||
async def solve_captcha(self, prompt: str | None = None) -> None:
|
||||
raise NotImplementedError("Solve captcha is not supported outside server context")
|
||||
|
||||
@action_wrap(ActionType.TERMINATE)
|
||||
async def terminate(
|
||||
self,
|
||||
errors: list[str],
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None,
|
||||
) -> None:
|
||||
async def terminate(self, errors: list[str], **kwargs: Any) -> None:
|
||||
# TODO: update the workflow run status to terminated
|
||||
return
|
||||
|
||||
@action_wrap(ActionType.COMPLETE)
|
||||
async def complete(
|
||||
self, prompt: str | None = None, data: str | dict[str, Any] | None = None, intention: str | None = None
|
||||
) -> None:
|
||||
async def complete(self, prompt: str | None = None) -> None:
|
||||
"""Stub for complete. Override in subclasses for specific behavior."""
|
||||
|
||||
@action_wrap(ActionType.RELOAD_PAGE)
|
||||
async def reload_page(
|
||||
self, prompt: str | None = None, data: str | dict[str, Any] | None = None, intention: str | None = None
|
||||
) -> None:
|
||||
await self.page.reload()
|
||||
async def reload_page(self, **kwargs: Any) -> None:
|
||||
await self.page.reload(**kwargs)
|
||||
return
|
||||
|
||||
@action_wrap(ActionType.EXTRACT)
|
||||
@@ -680,7 +652,7 @@ class SkyvernPage:
|
||||
schema: dict[str, Any] | list | str | None = None,
|
||||
error_code_mapping: dict[str, str] | None = None,
|
||||
intention: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any] | list | str | None:
|
||||
"""Extract structured data from the page using AI.
|
||||
|
||||
@@ -689,7 +661,6 @@ class SkyvernPage:
|
||||
schema: JSON Schema defining the structure of data to extract.
|
||||
error_code_mapping: Mapping of error codes to custom error messages.
|
||||
intention: Additional context about the extraction intent.
|
||||
data: Additional context data for AI processing.
|
||||
|
||||
Returns:
|
||||
Extracted data matching the provided schema, or None if extraction fails.
|
||||
@@ -711,12 +682,11 @@ class SkyvernPage:
|
||||
# Returns: {"name": "...", "price": 29.99}
|
||||
```
|
||||
"""
|
||||
data = kwargs.pop("data", None)
|
||||
return await self._ai.ai_extract(prompt, schema, error_code_mapping, intention, data)
|
||||
|
||||
@action_wrap(ActionType.VERIFICATION_CODE)
|
||||
async def verification_code(
|
||||
self, prompt: str | None = None, data: str | dict[str, Any] | None = None, intention: str | None = None
|
||||
) -> None:
|
||||
async def verification_code(self, prompt: str | None = None) -> None:
|
||||
return
|
||||
|
||||
@action_wrap(ActionType.SCROLL)
|
||||
@@ -724,9 +694,7 @@ class SkyvernPage:
|
||||
self,
|
||||
scroll_x: int,
|
||||
scroll_y: int,
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
await self.page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})")
|
||||
|
||||
@@ -736,9 +704,7 @@ class SkyvernPage:
|
||||
keys: list[str],
|
||||
hold: bool = False,
|
||||
duration: float = 0,
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
await handler_utils.keypress(self.page, keys, hold=hold, duration=duration)
|
||||
|
||||
@@ -747,9 +713,7 @@ class SkyvernPage:
|
||||
self,
|
||||
x: int,
|
||||
y: int,
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
await self.page.mouse.move(x, y)
|
||||
|
||||
@@ -759,9 +723,7 @@ class SkyvernPage:
|
||||
start_x: int,
|
||||
start_y: int,
|
||||
path: list[tuple[int, int]],
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
await handler_utils.drag(self.page, start_x, start_y, path)
|
||||
|
||||
@@ -771,9 +733,7 @@ class SkyvernPage:
|
||||
x: int,
|
||||
y: int,
|
||||
direction: Literal["down", "up"],
|
||||
prompt: str | None = None,
|
||||
data: str | dict[str, Any] | None = None,
|
||||
intention: str | None = None, # backward compatibility
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
await handler_utils.left_mouse(self.page, x, y, direction)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user