Fix ForLoop blocks not executing with cached scripts (#SKY-7751) (#4630)

This commit is contained in:
pedrohsdb
2026-02-04 16:33:42 -08:00
committed by GitHub
parent 734e0e6398
commit 7654dc6f32
2 changed files with 76 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
# Script Generation Context
## Key Constants
- `SCRIPT_TASK_BLOCKS` - Block types that have task_id and actions (task, navigation, extraction, etc.)
- `BLOCK_TYPES_THAT_SHOULD_BE_CACHED` in `workflow/service.py` - Block types eligible for caching (includes for_loop)
## Script Block Requirements for `run_with: code`
For a workflow to execute with cached scripts (`run_with: code`), ALL top-level blocks must have:
1. A `script_block` database entry
2. A non-null `run_signature` field
Without these, the system falls back to `run_with: agent`.
## Adding New Cacheable Block Types
When adding a new block type that should support cached execution:
1. Add to `BLOCK_TYPES_THAT_SHOULD_BE_CACHED` in `workflow/service.py`
2. Add handling in `generate_workflow_script_python_code()` with BOTH:
- `create_or_update_script_block()` - stores metadata in database
- `append_block_code(block_code)` - adds code to generated script output
3. Ensure `run_signature` is set (the code statement to execute the block)
**CRITICAL**: Every block type needs BOTH database entry AND script output. Missing `append_block_code()` causes runtime failures even if database entries exist.
## Block Processing Order in generate_script.py
1. `task_v1_blocks` - Blocks in `SCRIPT_TASK_BLOCKS`
2. `task_v2_blocks` - task_v2 blocks with child blocks
3. `for_loop_blocks` - ForLoop container blocks
4. `__start_block__` - Workflow entry point

View File

@@ -2305,6 +2305,50 @@ async def generate_workflow_script_python_code(
append_block_code(block_code)
# Handle for_loop blocks
# ForLoop blocks need script_block entries with run_signature so they can be executed via cached scripts
for_loop_blocks = [block for block in blocks if block["block_type"] == "for_loop"]
for for_loop_block in for_loop_blocks:
for_loop_label = for_loop_block.get("label") or f"for_loop_{for_loop_block.get('workflow_run_block_id')}"
cached_source = cached_blocks.get(for_loop_label)
use_cached = cached_source is not None and for_loop_label not in updated_block_labels
block_workflow_run_id = for_loop_block.get("workflow_run_id") or run_id
block_workflow_run_block_id = for_loop_block.get("workflow_run_block_id")
if use_cached:
assert cached_source is not None
block_code = cached_source.code
run_signature = cached_source.run_signature
block_workflow_run_id = cached_source.workflow_run_id
block_workflow_run_block_id = cached_source.workflow_run_block_id
else:
# Build the for loop statement
for_loop_stmt = _build_for_loop_statement(for_loop_label, for_loop_block)
temp_module = cst.Module(body=[for_loop_stmt])
block_code = temp_module.code
run_signature = block_code.strip()
if script_id and script_revision_id and organization_id:
try:
await create_or_update_script_block(
block_code=block_code,
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
block_label=for_loop_label,
update=pending,
run_signature=run_signature,
workflow_run_id=block_workflow_run_id,
workflow_run_block_id=block_workflow_run_block_id,
input_fields=None,
)
except Exception as e:
LOG.error("Failed to create for_loop script block", error=str(e), exc_info=True)
append_block_code(block_code)
# --- runner ---------------------------------------------------------
run_fn = _build_run_fn(blocks, workflow_run_request)