script gen - support skyvern.loop & cleaner interfaces for generated code (no need to pass context.parameters, implicit template rendering) (#3542)

This commit is contained in:
Shuchang Zheng
2025-09-26 23:27:29 -07:00
committed by GitHub
parent 8c54475fda
commit 90096bc453
7 changed files with 336 additions and 161 deletions

View File

@@ -1,5 +1,6 @@
from contextvars import ContextVar
from dataclasses import dataclass, field
from typing import Any
from zoneinfo import ZoneInfo
from playwright.async_api import Frame
@@ -28,13 +29,25 @@ class SkyvernContext:
frame_index_map: dict[Frame, int] = field(default_factory=dict)
dropped_css_svg_element_map: dict[str, bool] = field(default_factory=dict)
max_screenshot_scrolls: int | None = None
# feature flags
enable_parse_select_in_extract: bool = False
use_prompt_caching: bool = False
cached_static_prompt: str | None = None
# script run context
script_id: str | None = None
script_revision_id: str | None = None
action_order: int = 0
prompt: str | None = None
enable_parse_select_in_extract: bool = False
use_prompt_caching: bool = False
cached_static_prompt: str | None = None
parent_workflow_run_block_id: str | None = None
loop_metadata: dict[str, Any] | None = None
loop_output_values: list[dict[str, Any]] | None = None
script_run_parameters: dict[str, Any] = field(default_factory=dict)
"""
Example output value:
{"loop_value": "str", "output_parameter": "the key of the parameter", "output_value": Any}
"""
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

@@ -918,14 +918,14 @@ class ForLoopBlock(Block):
return context_parameters
async def get_loop_over_parameter_values(
async def get_values_from_loop_variable_reference(
self,
workflow_run_context: WorkflowRunContext,
workflow_run_id: str,
workflow_run_block_id: str,
organization_id: str | None = None,
) -> list[Any]:
# parse the value from self.loop_variable_reference and then from self.loop_over
parameter_value = None
if self.loop_variable_reference:
LOG.debug("Processing loop variable reference", loop_variable_reference=self.loop_variable_reference)
@@ -1029,6 +1029,26 @@ class ForLoopBlock(Block):
raise FailedToFormatJinjaStyleParameter(value_template, str(e))
parameter_value = json.loads(value_json)
if isinstance(parameter_value, list):
return parameter_value
else:
return [parameter_value]
async def get_loop_over_parameter_values(
self,
workflow_run_context: WorkflowRunContext,
workflow_run_id: str,
workflow_run_block_id: str,
organization_id: str | None = None,
) -> list[Any]:
# parse the value from self.loop_variable_reference and then from self.loop_over
if self.loop_variable_reference:
return await self.get_values_from_loop_variable_reference(
workflow_run_context,
workflow_run_id,
workflow_run_block_id,
organization_id,
)
elif self.loop_over is not None:
if isinstance(self.loop_over, WorkflowParameter):
parameter_value = workflow_run_context.get_value(self.loop_over.key)
@@ -1165,6 +1185,7 @@ class ForLoopBlock(Block):
for loop_idx, loop_over_value in enumerate(loop_over_values):
LOG.info("Starting loop iteration", loop_idx=loop_idx, loop_over_value=loop_over_value)
# context parameter has been deprecated. However, it's still used by task v2 - we should migrate away from it.
context_parameters_with_value = self.get_loop_block_context_parameters(workflow_run_id, loop_over_value)
for context_parameter in context_parameters_with_value:
workflow_run_context.set_value(context_parameter.key, context_parameter.value)