run script with browser session (#3368)

This commit is contained in:
Shuchang Zheng
2025-09-04 21:24:51 -07:00
committed by GitHub
parent 2c607037aa
commit 0fceaac8c8
4 changed files with 37 additions and 11 deletions

View File

@@ -9,13 +9,16 @@ from skyvern.forge.sdk.workflow.models.parameter import WorkflowParameterType
async def setup(
parameters: dict[str, Any], generated_parameter_cls: type[BaseModel] | None = None
parameters: dict[str, Any],
generated_parameter_cls: type[BaseModel] | None = None,
browser_session_id: str | None = None,
) -> tuple[SkyvernPage, RunContext]:
# transform any secrets/credential parameters. For example, if there's only one credential in the parameters: {"cred_12345": "cred_12345"},
# it should be transformed to {"cred_12345": {"username": "secret_5fBoa_username", "password": "secret_5fBoa_password"}}
# context comes from app.WORKFLOW_CONTEXT_MANAGER.get_workflow_run_context(workflow_run_id)
context = skyvern_context.current()
if context and context.organization_id and context.workflow_run_id:
browser_session_id = browser_session_id or context.browser_session_id
workflow_run_context = app.WORKFLOW_CONTEXT_MANAGER.get_workflow_run_context(context.workflow_run_id)
parameters_in_workflow_context = workflow_run_context.parameters
for key in parameters:
@@ -23,7 +26,7 @@ async def setup(
parameter = parameters_in_workflow_context[key]
if parameter.workflow_parameter_type == WorkflowParameterType.CREDENTIAL_ID:
parameters[key] = workflow_run_context.values[key]
skyvern_page = await SkyvernPage.create()
skyvern_page = await SkyvernPage.create(browser_session_id=browser_session_id)
run_context = RunContext(
parameters=parameters,
page=skyvern_page,

View File

@@ -71,7 +71,7 @@ class SkyvernPage:
self._record = recorder or (lambda ac: None)
@classmethod
async def _get_or_create_browser_state(cls) -> BrowserState:
async def _get_or_create_browser_state(cls, browser_session_id: str | None = None) -> BrowserState:
context = skyvern_context.current()
if context and context.workflow_run_id and context.organization_id:
workflow_run = await app.DATABASE.get_workflow_run(
@@ -79,12 +79,13 @@ class SkyvernPage:
)
if workflow_run:
browser_state = await app.BROWSER_MANAGER.get_or_create_for_workflow_run(
workflow_run=workflow_run, browser_session_id=None
workflow_run=workflow_run,
browser_session_id=browser_session_id,
)
else:
raise WorkflowRunNotFound(workflow_run_id=context.workflow_run_id)
else:
browser_state = await app.BROWSER_MANAGER.get_or_create_for_script()
browser_state = await app.BROWSER_MANAGER.get_or_create_for_script(browser_session_id=browser_session_id)
return browser_state
@classmethod
@@ -103,10 +104,13 @@ class SkyvernPage:
return browser_state
@classmethod
async def create(cls) -> SkyvernPage:
async def create(
cls,
browser_session_id: str | None = None,
) -> SkyvernPage:
# initialize browser state
# TODO: add workflow_run_id or eventually script_id/script_run_id
browser_state = await cls._get_or_create_browser_state()
browser_state = await cls._get_or_create_browser_state(browser_session_id=browser_session_id)
scraped_page = await scrape_website(
browser_state=browser_state,
url="",

View File

@@ -2438,7 +2438,13 @@ class WorkflowService:
return None, rendered_cache_key_value
async def _execute_workflow_script(
self, script_id: str, workflow: Workflow, workflow_run: WorkflowRun, api_key: str, organization: Organization
self,
script_id: str,
workflow: Workflow,
workflow_run: WorkflowRun,
api_key: str,
organization: Organization,
browser_session_id: str | None = None,
) -> WorkflowRun:
"""
Execute the related workflow script instead of running the workflow blocks.
@@ -2458,6 +2464,7 @@ class WorkflowService:
organization_id=organization.organization_id,
parameters=parameters,
workflow_run_id=workflow_run.workflow_run_id,
browser_session_id=browser_session_id,
background_tasks=None, # Execute synchronously
)

View File

@@ -167,6 +167,7 @@ async def execute_script(
organization_id: str,
parameters: dict[str, Any] | None = None,
workflow_run_id: str | None = None,
browser_session_id: str | None = None,
background_tasks: BackgroundTasks | None = None,
) -> None:
# TODO: assume the script only has one ScriptFile called main.py
@@ -229,17 +230,26 @@ async def execute_script(
parameters = {wf_param.key: run_param.value for wf_param, run_param in parameter_tuples}
LOG.info("Script run Parameters is using workflow run parameters", parameters=parameters)
script_path = os.path.join(script.script_id, "main.py")
if background_tasks:
# Execute asynchronously in background
background_tasks.add_task(
run_script, parameters=parameters, organization_id=organization_id, workflow_run_id=workflow_run_id
run_script,
script_path,
parameters=parameters,
organization_id=organization_id,
workflow_run_id=workflow_run_id,
browser_session_id=browser_session_id,
)
else:
# Execute synchronously
script_path = os.path.join(script.script_id, "main.py")
if os.path.exists(script_path):
await run_script(
script_path, parameters=parameters, organization_id=organization_id, workflow_run_id=workflow_run_id
script_path,
parameters=parameters,
organization_id=organization_id,
workflow_run_id=workflow_run_id,
browser_session_id=browser_session_id,
)
else:
LOG.error("Script main.py not found", script_path=script_path, script_id=script_id)
@@ -1252,12 +1262,14 @@ async def run_script(
parameters: dict[str, Any] | None = None,
organization_id: str | None = None,
workflow_run_id: str | None = None,
browser_session_id: str | None = None,
) -> None:
# register the script run
context = skyvern_context.current()
if not context:
context = skyvern_context.ensure_context()
skyvern_context.set(skyvern_context.SkyvernContext())
context.browser_session_id = browser_session_id
if workflow_run_id and organization_id:
workflow_run = await app.DATABASE.get_workflow_run(
workflow_run_id=workflow_run_id, organization_id=organization_id