script gen post action (#3480)

This commit is contained in:
Shuchang Zheng
2025-09-19 08:50:21 -07:00
committed by GitHub
parent b4669f7477
commit c5280782b0
17 changed files with 536 additions and 264 deletions

View File

@@ -2,20 +2,11 @@
"""
Generate a runnable Skyvern workflow script.
Example
-------
generated_code = generate_workflow_script(
file_name="workflow.py",
workflow_run_request=workflow_run_request,
workflow=workflow,
tasks=tasks,
actions_by_task=actions_by_task,
)
Path("workflow.py").write_text(src)
"""
from __future__ import annotations
import asyncio
import hashlib
import keyword
import re
@@ -1602,7 +1593,7 @@ def _build_run_fn(blocks: list[dict[str, Any]], wf_req: dict[str, Any]) -> Funct
# --------------------------------------------------------------------- #
async def generate_workflow_script(
async def generate_workflow_script_python_code(
*,
file_name: str,
workflow_run_request: dict[str, Any],
@@ -1614,6 +1605,7 @@ async def generate_workflow_script(
run_id: str | None = None,
script_id: str | None = None,
script_revision_id: str | None = None,
draft: bool = False,
) -> str:
"""
Build a LibCST Module and emit .code (PEP-8-formatted source).
@@ -1685,16 +1677,15 @@ async def generate_workflow_script(
if script_id and script_revision_id and organization_id:
try:
block_name = task.get("label") or task.get("title") or task.get("task_id") or f"task_{idx}"
block_description = f"Generated block for task: {block_name}"
temp_module = cst.Module(body=[block_fn_def])
block_code = temp_module.code
await create_script_block(
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_name=block_name,
block_description=block_description,
block_label=block_name,
update=draft,
)
except Exception as e:
LOG.error("Failed to create script block", error=str(e), exc_info=True)
@@ -1737,15 +1728,13 @@ async def generate_workflow_script(
task_v2_block_code = temp_module.code
block_name = task_v2.get("label") or task_v2.get("title") or f"task_v2_{idx}"
block_description = f"Generated task_v2 block with child functions: {block_name}"
await create_script_block(
await create_or_update_script_block(
block_code=task_v2_block_code,
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
block_name=block_name,
block_description=block_description,
block_label=block_name,
)
except Exception as e:
LOG.error("Failed to create task_v2 script block", error=str(e), exc_info=True)
@@ -1805,13 +1794,13 @@ async def generate_workflow_script(
start_block_module = cst.Module(body=start_block_body)
start_block_code = start_block_module.code
await create_script_block(
await create_or_update_script_block(
block_code=start_block_code,
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
block_name=settings.WORKFLOW_START_BLOCK_LABEL,
block_description="Start block containing imports, model classes, and run function",
block_label=settings.WORKFLOW_START_BLOCK_LABEL,
update=draft,
)
except Exception as e:
LOG.error("Failed to create __start_block__", error=str(e), exc_info=True)
@@ -1830,69 +1819,92 @@ async def generate_workflow_script(
return module.code
async def create_script_block(
async def create_or_update_script_block(
block_code: str | bytes,
script_revision_id: str,
script_id: str,
organization_id: str,
block_name: str,
block_description: str | None = None,
block_label: str,
update: bool = False,
) -> None:
"""
Create a script block in the database and save the block code to a script file.
If update is True, the script block will be updated instead of created.
Args:
block_code: The code to save
script_revision_id: The script revision ID
script_id: The script ID
organization_id: The organization ID
block_name: Optional custom name for the block (defaults to function name)
block_description: Optional description for the block
block_label: Optional custom name for the block (defaults to function name)
update: Whether to update the script block instead of creating a new one
"""
block_code_bytes = block_code if isinstance(block_code, bytes) else block_code.encode("utf-8")
try:
# Step 3: Create script block in database
script_block = await app.DATABASE.create_script_block(
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
script_block_label=block_name,
)
script_block = None
if update:
script_block = await app.DATABASE.get_script_block_by_label(
organization_id=organization_id,
script_revision_id=script_revision_id,
script_block_label=block_label,
)
if not script_block:
script_block = await app.DATABASE.create_script_block(
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
script_block_label=block_label,
)
# Step 4: Create script file for the block
# Generate a unique filename for the block
file_name = f"{block_name}.skyvern"
file_name = f"{block_label}.skyvern"
file_path = f"blocks/{file_name}"
# Create artifact and upload to S3
artifact_id = await app.ARTIFACT_MANAGER.create_script_file_artifact(
organization_id=organization_id,
script_id=script_id,
script_version=1, # Assuming version 1 for now
file_path=file_path,
data=block_code_bytes,
)
artifact_id = None
if update and script_block.script_file_id:
script_file = await app.DATABASE.get_script_file_by_id(
script_revision_id,
script_block.script_file_id,
organization_id,
)
if script_file and script_file.artifact_id:
artifact = await app.DATABASE.get_artifact_by_id(script_file.artifact_id, organization_id)
if artifact:
asyncio.create_task(app.STORAGE.store_artifact(artifact, block_code_bytes))
else:
LOG.error("Script file or artifact not found", script_file_id=script_block.script_file_id)
else:
artifact_id = await app.ARTIFACT_MANAGER.create_script_file_artifact(
organization_id=organization_id,
script_id=script_id,
script_version=1, # Assuming version 1 for now
file_path=file_path,
data=block_code_bytes,
)
# Create script file record
script_file = await app.DATABASE.create_script_file(
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
file_path=file_path,
file_name=file_name,
file_type="file",
content_hash=f"sha256:{hashlib.sha256(block_code_bytes).hexdigest()}",
file_size=len(block_code_bytes),
mime_type="text/x-python",
artifact_id=artifact_id,
)
# Create script file record
script_file = await app.DATABASE.create_script_file(
script_revision_id=script_revision_id,
script_id=script_id,
organization_id=organization_id,
file_path=file_path,
file_name=file_name,
file_type="file",
content_hash=f"sha256:{hashlib.sha256(block_code_bytes).hexdigest()}",
file_size=len(block_code_bytes),
mime_type="text/x-python",
artifact_id=artifact_id,
)
# update script block with script file id
await app.DATABASE.update_script_block(
script_block_id=script_block.script_block_id,
organization_id=organization_id,
script_file_id=script_file.file_id,
)
# update script block with script file id
await app.DATABASE.update_script_block(
script_block_id=script_block.script_block_id,
organization_id=organization_id,
script_file_id=script_file.file_id,
)
except Exception as e:
# Log error but don't fail the entire generation process

View File

@@ -199,7 +199,7 @@ class SkyvernPage:
finally:
skyvern_page._record(call)
# Auto-create action after execution
await skyvern_page._create_action_before_execution(
await skyvern_page._create_action_after_execution(
action_type=action,
intention=intention,
status=action_status,
@@ -222,7 +222,7 @@ class SkyvernPage:
timeout=timeout,
)
async def _create_action_before_execution(
async def _create_action_after_execution(
self,
action_type: ActionType,
intention: str = "",
@@ -295,6 +295,7 @@ class SkyvernPage:
created_action = await app.DATABASE.create_action(action)
context.action_order += 1
return created_action
except Exception: