add validate browser (#481)

This commit is contained in:
LawyZheng
2024-06-17 11:22:36 +08:00
committed by GitHub
parent 10612f02fd
commit df2c55ba86
2 changed files with 47 additions and 7 deletions

View File

@@ -5,7 +5,7 @@ import tempfile
import time import time
import uuid import uuid
from datetime import datetime from datetime import datetime
from typing import Any, Awaitable, Protocol from typing import Any, Awaitable, Callable, Protocol
import structlog import structlog
from playwright._impl._errors import TimeoutError from playwright._impl._errors import TimeoutError
@@ -21,6 +21,7 @@ from skyvern.exceptions import (
UnknownErrorWhileCreatingBrowserContext, UnknownErrorWhileCreatingBrowserContext,
) )
from skyvern.forge.sdk.core.skyvern_context import current from skyvern.forge.sdk.core.skyvern_context import current
from skyvern.forge.sdk.schemas.tasks import ProxyLocation
from skyvern.forge.sdk.settings_manager import SettingsManager from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -34,6 +35,7 @@ class BrowserContextCreator(Protocol):
class BrowserContextFactory: class BrowserContextFactory:
_creators: dict[str, BrowserContextCreator] = {} _creators: dict[str, BrowserContextCreator] = {}
_validator: Callable[[Page], Awaitable[bool]] | None = None
@staticmethod @staticmethod
def get_subdir() -> str: def get_subdir() -> str:
@@ -101,6 +103,16 @@ class BrowserContextFactory:
except Exception as e: except Exception as e:
raise UnknownErrorWhileCreatingBrowserContext(browser_type, e) from e raise UnknownErrorWhileCreatingBrowserContext(browser_type, e) from e
@classmethod
def set_validate_browser_context(cls, validator: Callable[[Page], Awaitable[bool]]) -> None:
cls._validator = validator
@classmethod
async def validate_browser_context(cls, page: Page) -> bool:
if cls._validator is None:
return True
return await cls._validator(page)
class BrowserArtifacts(BaseModel): class BrowserArtifacts(BaseModel):
video_path: str | None = None video_path: str | None = None
@@ -155,7 +167,12 @@ class BrowserState:
if page != self.page: if page != self.page:
await page.close() await page.close()
async def check_and_fix_state(self, url: str | None = None) -> None: async def check_and_fix_state(
self,
url: str | None = None,
proxy_location: ProxyLocation | None = None,
task_id: str | None = None,
) -> None:
if self.pw is None: if self.pw is None:
LOG.info("Starting playwright") LOG.info("Starting playwright")
self.pw = await async_playwright().start() self.pw = await async_playwright().start()
@@ -165,7 +182,12 @@ class BrowserState:
( (
browser_context, browser_context,
browser_artifacts, browser_artifacts,
) = await BrowserContextFactory.create_browser_context(self.pw, url=url) ) = await BrowserContextFactory.create_browser_context(
self.pw,
url=url,
proxy_location=proxy_location,
task_id=task_id,
)
self.browser_context = browser_context self.browser_context = browser_context
self.browser_artifacts = browser_artifacts self.browser_artifacts = browser_artifacts
LOG.info("browser context is created") LOG.info("browser context is created")
@@ -216,9 +238,27 @@ class BrowserState:
if self.browser_artifacts.video_path is None: if self.browser_artifacts.video_path is None:
self.browser_artifacts.video_path = await self.page.video.path() if self.page and self.page.video else None self.browser_artifacts.video_path = await self.page.video.path() if self.page and self.page.video else None
async def get_or_create_page(self, url: str | None = None) -> Page: async def get_or_create_page(
await self.check_and_fix_state(url) self,
url: str | None = None,
proxy_location: ProxyLocation | None = None,
task_id: str | None = None,
) -> Page:
if self.page is not None:
return self.page
await self.check_and_fix_state(url=url, proxy_location=proxy_location, task_id=task_id)
assert self.page is not None assert self.page is not None
if not await BrowserContextFactory.validate_browser_context(self.page):
await self._close_all_other_pages()
if self.browser_context is not None:
await self.browser_context.close()
self.browser_context = None
self.page = None
await self.check_and_fix_state(url=url, proxy_location=proxy_location, task_id=task_id)
assert self.page is not None
return self.page return self.page
async def close(self, close_browser_on_completion: bool = True) -> None: async def close(self, close_browser_on_completion: bool = True) -> None:

View File

@@ -60,7 +60,7 @@ class BrowserManager:
# The URL here is only used when creating a new page, and not when using an existing page. # The URL here is only used when creating a new page, and not when using an existing page.
# This will make sure browser_state.page is not None. # This will make sure browser_state.page is not None.
await browser_state.get_or_create_page(task.url) await browser_state.get_or_create_page(url=task.url, proxy_location=task.proxy_location, task_id=task.task_id)
self.pages[task.task_id] = browser_state self.pages[task.task_id] = browser_state
if task.workflow_run_id: if task.workflow_run_id:
@@ -78,7 +78,7 @@ class BrowserManager:
# The URL here is only used when creating a new page, and not when using an existing page. # The URL here is only used when creating a new page, and not when using an existing page.
# This will make sure browser_state.page is not None. # This will make sure browser_state.page is not None.
await browser_state.get_or_create_page(url) await browser_state.get_or_create_page(url=url, proxy_location=workflow_run.proxy_location)
self.pages[workflow_run.workflow_run_id] = browser_state self.pages[workflow_run.workflow_run_id] = browser_state
return browser_state return browser_state