batch input field processing for input actions when caching and running workflows with code (#4250)

This commit is contained in:
Shuchang Zheng
2025-12-16 10:08:19 +08:00
committed by GitHub
parent 1e5b8b36c1
commit 65b12f7ade
7 changed files with 320 additions and 57 deletions

View File

@@ -72,6 +72,8 @@ class SkyvernContext:
{"loop_value": "str", "output_parameter": "the key of the parameter", "output_value": Any}
"""
generate_script: bool = True
action_ai_overrides: dict[str, dict[int, str]] = field(default_factory=dict)
action_counters: dict[str, int] = field(default_factory=dict)
def __repr__(self) -> str:
return f"SkyvernContext(request_id={self.request_id}, organization_id={self.organization_id}, task_id={self.task_id}, step_id={self.step_id}, workflow_id={self.workflow_id}, workflow_run_id={self.workflow_run_id}, task_v2_id={self.task_v2_id}, max_steps_override={self.max_steps_override}, run_id={self.run_id})"

View File

@@ -709,7 +709,7 @@ class WorkflowService:
browser_profile_id=browser_profile_id,
block_labels=block_labels,
block_outputs=block_outputs,
workflow_script=workflow_script,
script=workflow_script,
)
if refreshed_workflow_run := await app.DATABASE.get_workflow_run(
@@ -758,93 +758,89 @@ class WorkflowService:
browser_profile_id: str | None = None,
block_labels: list[str] | None = None,
block_outputs: dict[str, Any] | None = None,
workflow_script: WorkflowScript | None = None,
script: Script | None = None,
) -> tuple[WorkflowRun, set[str]]:
organization_id = organization.organization_id
workflow_run_id = workflow_run.workflow_run_id
top_level_blocks = workflow.workflow_definition.blocks
all_blocks = get_all_blocks(top_level_blocks)
# Load script blocks if workflow_script is provided
# Load script blocks if script is provided
script_blocks_by_label: dict[str, Any] = {}
loaded_script_module = None
blocks_to_update: set[str] = set()
is_script_run = self.should_run_script(workflow, workflow_run)
if workflow_script:
if script:
LOG.info(
"Loading script blocks for workflow execution",
workflow_run_id=workflow_run_id,
script_id=workflow_script.script_id,
script_id=script.script_id,
script_revision_id=script.script_revision_id,
)
context = skyvern_context.ensure_context()
context.script_id = script.script_id
context.script_revision_id = script.script_revision_id
try:
# Load script blocks from database
script = await app.DATABASE.get_script(
script_id=workflow_script.script_id,
script_blocks = await app.DATABASE.get_script_blocks_by_script_revision_id(
script_revision_id=script.script_revision_id,
organization_id=organization_id,
)
if script:
script_blocks = await app.DATABASE.get_script_blocks_by_script_revision_id(
# Create mapping from block label to script block
for script_block in script_blocks:
if script_block.run_signature:
script_blocks_by_label[script_block.script_block_label] = script_block
if is_script_run:
# load the script files
script_files = await app.DATABASE.get_script_files(
script_revision_id=script.script_revision_id,
organization_id=organization_id,
)
await script_service.load_scripts(script, script_files)
# Create mapping from block label to script block
for script_block in script_blocks:
if script_block.run_signature:
script_blocks_by_label[script_block.script_block_label] = script_block
if is_script_run:
# load the script files
script_files = await app.DATABASE.get_script_files(
script_revision_id=script.script_revision_id,
organization_id=organization_id,
script_path = os.path.join(settings.TEMP_PATH, script.script_id, "main.py")
if os.path.exists(script_path):
# setup script run
parameter_tuples = await app.DATABASE.get_workflow_run_parameters(
workflow_run_id=workflow_run.workflow_run_id
)
await script_service.load_scripts(script, script_files)
script_parameters = {wf_param.key: run_param.value for wf_param, run_param in parameter_tuples}
script_path = os.path.join(settings.TEMP_PATH, workflow_script.script_id, "main.py")
if os.path.exists(script_path):
# setup script run
parameter_tuples = await app.DATABASE.get_workflow_run_parameters(
workflow_run_id=workflow_run.workflow_run_id
spec = importlib.util.spec_from_file_location("user_script", script_path)
if spec and spec.loader:
loaded_script_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(loaded_script_module)
await skyvern.setup(
script_parameters,
generated_parameter_cls=loaded_script_module.GeneratedWorkflowParameters,
)
script_parameters = {
wf_param.key: run_param.value for wf_param, run_param in parameter_tuples
}
spec = importlib.util.spec_from_file_location("user_script", script_path)
if spec and spec.loader:
loaded_script_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(loaded_script_module)
await skyvern.setup(
script_parameters,
generated_parameter_cls=loaded_script_module.GeneratedWorkflowParameters,
)
LOG.info(
"Successfully loaded script module",
script_id=workflow_script.script_id,
block_count=len(script_blocks_by_label),
)
else:
LOG.warning(
"Script file not found at path",
script_path=script_path,
script_id=workflow_script.script_id,
LOG.info(
"Successfully loaded script module",
script_id=script.script_id,
block_count=len(script_blocks_by_label),
)
else:
LOG.warning(
"Script file not found at path",
script_path=script_path,
script_id=script.script_id,
)
except Exception as e:
LOG.warning(
"Failed to load script blocks, will fallback to normal execution",
error=str(e),
exc_info=True,
workflow_run_id=workflow_run_id,
script_id=workflow_script.script_id,
script_id=script.script_id,
)
script_blocks_by_label = {}
loaded_script_module = None
# Mark workflow as running with appropriate engine
run_with = "code" if workflow_script and is_script_run and script_blocks_by_label else "agent"
run_with = "code" if script and is_script_run and script_blocks_by_label else "agent"
await self.mark_workflow_run_as_running(workflow_run_id=workflow_run_id, run_with=run_with)
if block_labels and len(block_labels):