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( 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]: ) -> tuple[SkyvernPage, RunContext]:
# transform any secrets/credential parameters. For example, if there's only one credential in the parameters: {"cred_12345": "cred_12345"}, # 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"}} # 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 comes from app.WORKFLOW_CONTEXT_MANAGER.get_workflow_run_context(workflow_run_id)
context = skyvern_context.current() context = skyvern_context.current()
if context and context.organization_id and context.workflow_run_id: 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) workflow_run_context = app.WORKFLOW_CONTEXT_MANAGER.get_workflow_run_context(context.workflow_run_id)
parameters_in_workflow_context = workflow_run_context.parameters parameters_in_workflow_context = workflow_run_context.parameters
for key in parameters: for key in parameters:
@@ -23,7 +26,7 @@ async def setup(
parameter = parameters_in_workflow_context[key] parameter = parameters_in_workflow_context[key]
if parameter.workflow_parameter_type == WorkflowParameterType.CREDENTIAL_ID: if parameter.workflow_parameter_type == WorkflowParameterType.CREDENTIAL_ID:
parameters[key] = workflow_run_context.values[key] 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( run_context = RunContext(
parameters=parameters, parameters=parameters,
page=skyvern_page, page=skyvern_page,

View File

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

View File

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

View File

@@ -167,6 +167,7 @@ async def execute_script(
organization_id: str, organization_id: str,
parameters: dict[str, Any] | None = None, parameters: dict[str, Any] | None = None,
workflow_run_id: str | None = None, workflow_run_id: str | None = None,
browser_session_id: str | None = None,
background_tasks: BackgroundTasks | None = None, background_tasks: BackgroundTasks | None = None,
) -> None: ) -> None:
# TODO: assume the script only has one ScriptFile called main.py # 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} 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) 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: if background_tasks:
# Execute asynchronously in background # Execute asynchronously in background
background_tasks.add_task( 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: else:
# Execute synchronously # Execute synchronously
script_path = os.path.join(script.script_id, "main.py")
if os.path.exists(script_path): if os.path.exists(script_path):
await run_script( 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: else:
LOG.error("Script main.py not found", script_path=script_path, script_id=script_id) 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, parameters: dict[str, Any] | None = None,
organization_id: str | None = None, organization_id: str | None = None,
workflow_run_id: str | None = None, workflow_run_id: str | None = None,
browser_session_id: str | None = None,
) -> None: ) -> None:
# register the script run # register the script run
context = skyvern_context.current() context = skyvern_context.current()
if not context: if not context:
context = skyvern_context.ensure_context() context = skyvern_context.ensure_context()
skyvern_context.set(skyvern_context.SkyvernContext()) skyvern_context.set(skyvern_context.SkyvernContext())
context.browser_session_id = browser_session_id
if workflow_run_id and organization_id: if workflow_run_id and organization_id:
workflow_run = await app.DATABASE.get_workflow_run( workflow_run = await app.DATABASE.get_workflow_run(
workflow_run_id=workflow_run_id, organization_id=organization_id workflow_run_id=workflow_run_id, organization_id=organization_id