Fix script generation race condition causing hardcoded parameter values (#SKY-7653) (#4570)

This commit is contained in:
pedrohsdb
2026-01-28 12:05:17 -08:00
committed by GitHub
parent 20cbdcb401
commit 57c3c07593
4 changed files with 65 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ import structlog
from pydantic import BaseModel
from skyvern.forge import app
from skyvern.forge.sdk.core import skyvern_context
from skyvern.forge.sdk.prompting import PromptEngine
from skyvern.webeye.actions.actions import ActionType
@@ -64,6 +65,21 @@ async def generate_workflow_parameters_schema(
elif action_type == ActionType.SELECT_OPTION:
value = action.get("option", "")
# Skip actions without data - addresses race condition where script generation
# runs before action data is fully saved to database (SKY-7653)
if not value:
LOG.debug(
"Skipping action without data during field mapping generation",
action_type=action_type,
action_id=action.get("action_id", ""),
task_id=task_id,
)
# Mark that we had incomplete actions - triggers finalize regeneration
ctx = skyvern_context.current()
if ctx:
ctx.script_gen_had_incomplete_actions = True
continue
# Check if this action has an existing field name that must be preserved
existing_field_name = existing_field_assignments.get(action_counter)

View File

@@ -75,6 +75,10 @@ class SkyvernContext:
action_ai_overrides: dict[str, dict[int, str]] = field(default_factory=dict)
action_counters: dict[str, int] = field(default_factory=dict)
# Track if script generation skipped any actions due to missing data (race condition)
# Used to determine if finalize regeneration is needed at workflow completion
script_gen_had_incomplete_actions: bool = False
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

@@ -849,6 +849,7 @@ class WorkflowService:
workflow_run=workflow_run,
block_labels=block_labels,
blocks_to_update=blocks_to_update,
finalize=True, # Force regeneration to ensure field mappings have complete action data
)
# Execute finally block if configured. Skip for: canceled (user explicitly stopped)
@@ -3352,10 +3353,48 @@ class WorkflowService:
workflow_run: WorkflowRun,
block_labels: list[str] | None = None,
blocks_to_update: set[str] | None = None,
finalize: bool = False,
) -> None:
"""
Generate or regenerate workflow script if needed.
Args:
workflow: The workflow definition
workflow_run: The workflow run instance
block_labels: Optional list of specific block labels to generate
blocks_to_update: Set of block labels that need regeneration
finalize: If True, check if any actions were skipped during script generation
due to missing data (race condition). Only regenerate if needed.
This fixes SKY-7653 while avoiding unnecessary regeneration costs.
"""
code_gen = workflow_run.code_gen
blocks_to_update = set(blocks_to_update or [])
# When finalizing, only regenerate if script generation had incomplete actions.
# This addresses the race condition (SKY-7653) while avoiding unnecessary
# regeneration costs when the script is already complete.
if finalize:
current_context = skyvern_context.current()
if current_context and current_context.script_gen_had_incomplete_actions:
LOG.info(
"Finalize: regenerating script due to incomplete actions during generation",
workflow_run_id=workflow_run.workflow_run_id,
)
task_block_labels = {
block.label
for block in workflow.workflow_definition.blocks
if block.label and block.block_type in BLOCK_TYPES_THAT_SHOULD_BE_CACHED
}
blocks_to_update.update(task_block_labels)
blocks_to_update.add(settings.WORKFLOW_START_BLOCK_LABEL)
# Reset flag after triggering regeneration to prevent stale state
current_context.script_gen_had_incomplete_actions = False
else:
LOG.debug(
"Finalize: skipping regeneration - no incomplete actions detected",
workflow_run_id=workflow_run.workflow_run_id,
)
LOG.info(
"Generate script?",
block_labels=block_labels,

View File

@@ -967,10 +967,12 @@ async def run_task_v2_helper(
context=context,
screenshots=completion_screenshots,
)
await app.WORKFLOW_SERVICE.generate_script_if_needed(
workflow=workflow,
workflow_run=workflow_run,
)
if task_v2.run_with == "code":
await app.WORKFLOW_SERVICE.generate_script_if_needed(
workflow=workflow,
workflow_run=workflow_run,
finalize=True, # Force regeneration to ensure field mappings have complete action data
)
break
# total step number validation