block level script run (#3710)
This commit is contained in:
@@ -47,7 +47,15 @@ from skyvern.forge.sdk.workflow.models.block import (
|
||||
from skyvern.forge.sdk.workflow.models.parameter import PARAMETER_TYPE, OutputParameter, ParameterType
|
||||
from skyvern.forge.sdk.workflow.models.workflow import Workflow
|
||||
from skyvern.schemas.runs import RunEngine
|
||||
from skyvern.schemas.scripts import CreateScriptResponse, FileEncoding, FileNode, ScriptFileCreate, ScriptStatus
|
||||
from skyvern.schemas.scripts import (
|
||||
CreateScriptResponse,
|
||||
FileEncoding,
|
||||
FileNode,
|
||||
Script,
|
||||
ScriptFile,
|
||||
ScriptFileCreate,
|
||||
ScriptStatus,
|
||||
)
|
||||
from skyvern.schemas.workflows import BlockStatus, BlockType, FileStorageType, FileType
|
||||
|
||||
LOG = structlog.get_logger()
|
||||
@@ -258,35 +266,18 @@ async def create_script(
|
||||
raise HTTPException(status_code=500, detail="Failed to create script")
|
||||
|
||||
|
||||
async def execute_script(
|
||||
script_id: str,
|
||||
organization_id: str,
|
||||
parameters: dict[str, Any] | None = None,
|
||||
workflow_run_id: str | None = None,
|
||||
browser_session_id: str | None = None,
|
||||
background_tasks: BackgroundTasks | None = None,
|
||||
async def load_scripts(
|
||||
script: Script,
|
||||
script_files: list[ScriptFile],
|
||||
) -> None:
|
||||
# step 1: get the script revision
|
||||
script = await app.DATABASE.get_script(
|
||||
script_id=script_id,
|
||||
organization_id=organization_id,
|
||||
)
|
||||
if not script:
|
||||
raise ScriptNotFound(script_id=script_id)
|
||||
|
||||
# step 2: get the script files
|
||||
script_files = await app.DATABASE.get_script_files(
|
||||
script_revision_id=script.script_revision_id, organization_id=organization_id
|
||||
)
|
||||
|
||||
# step 3: copy the script files to the local directory
|
||||
organization_id = script.organization_id
|
||||
for file in script_files:
|
||||
# retrieve the artifact
|
||||
if not file.artifact_id:
|
||||
continue
|
||||
artifact = await app.DATABASE.get_artifact_by_id(file.artifact_id, organization_id)
|
||||
if not artifact:
|
||||
LOG.error("Artifact not found", artifact_id=file.artifact_id, script_id=script_id)
|
||||
LOG.error("Artifact not found", artifact_id=file.artifact_id, script_id=script.script_id)
|
||||
continue
|
||||
file_content = await app.ARTIFACT_MANAGER.retrieve_artifact(artifact)
|
||||
if not file_content:
|
||||
@@ -313,6 +304,31 @@ async def execute_script(
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(file_content)
|
||||
|
||||
|
||||
async def execute_script(
|
||||
script_id: str,
|
||||
organization_id: str,
|
||||
parameters: dict[str, Any] | None = None,
|
||||
workflow_run_id: str | None = None,
|
||||
browser_session_id: str | None = None,
|
||||
background_tasks: BackgroundTasks | None = None,
|
||||
) -> None:
|
||||
# step 1: get the script revision
|
||||
script = await app.DATABASE.get_script(
|
||||
script_id=script_id,
|
||||
organization_id=organization_id,
|
||||
)
|
||||
if not script:
|
||||
raise ScriptNotFound(script_id=script_id)
|
||||
|
||||
# step 2: get the script files
|
||||
script_files = await app.DATABASE.get_script_files(
|
||||
script_revision_id=script.script_revision_id, organization_id=organization_id
|
||||
)
|
||||
|
||||
# step 3: copy the script files to the local directory
|
||||
await load_scripts(script, script_files)
|
||||
|
||||
# step 4: execute the script
|
||||
if workflow_run_id and not parameters:
|
||||
parameter_tuples = await app.DATABASE.get_workflow_run_parameters(workflow_run_id=workflow_run_id)
|
||||
@@ -320,6 +336,7 @@ async def execute_script(
|
||||
LOG.info("Script run Parameters is using workflow run parameters", parameters=parameters)
|
||||
|
||||
script_path = os.path.join(settings.TEMP_PATH, script.script_id, "main.py")
|
||||
|
||||
if background_tasks:
|
||||
# Execute asynchronously in background
|
||||
background_tasks.add_task(
|
||||
@@ -329,6 +346,7 @@ async def execute_script(
|
||||
organization_id=organization_id,
|
||||
workflow_run_id=workflow_run_id,
|
||||
browser_session_id=browser_session_id,
|
||||
script_id=script_id,
|
||||
)
|
||||
else:
|
||||
# Execute synchronously
|
||||
@@ -681,17 +699,7 @@ async def _fallback_to_ai_run(
|
||||
organization_id=organization_id,
|
||||
status=StepStatus.failed,
|
||||
)
|
||||
# 2. create a new step for ai run
|
||||
ai_step = await app.DATABASE.create_step(
|
||||
task_id=task_id,
|
||||
organization_id=organization_id,
|
||||
order=previous_step.order + 1,
|
||||
retry_index=0,
|
||||
)
|
||||
context.step_id = ai_step.step_id
|
||||
ai_step_id = ai_step.step_id
|
||||
# 3. build the task block
|
||||
# 4. run execute_step
|
||||
# 2. run execute_step
|
||||
organization = await app.DATABASE.get_organization(organization_id=organization_id)
|
||||
if not organization:
|
||||
raise Exception(f"Organization is missing organization_id={organization_id}")
|
||||
@@ -717,8 +725,29 @@ async def _fallback_to_ai_run(
|
||||
workflow_permanent_id=workflow_permanent_id,
|
||||
workflow_run_id=workflow_run_id,
|
||||
)
|
||||
if workflow_run_block_id:
|
||||
await _update_workflow_block(
|
||||
workflow_run_block_id,
|
||||
BlockStatus.failed,
|
||||
task_id=task_id,
|
||||
task_status=TaskStatus.failed,
|
||||
failure_reason=str(error),
|
||||
step_id=script_step_id,
|
||||
step_status=StepStatus.failed,
|
||||
label=cache_key,
|
||||
)
|
||||
return
|
||||
|
||||
# 2. create a new step for ai run
|
||||
ai_step = await app.DATABASE.create_step(
|
||||
task_id=task_id,
|
||||
organization_id=organization_id,
|
||||
order=previous_step.order + 1,
|
||||
retry_index=0,
|
||||
)
|
||||
context.step_id = ai_step.step_id
|
||||
ai_step_id = ai_step.step_id
|
||||
|
||||
# get the output_paramter
|
||||
output_parameter = workflow.get_output_parameter(cache_key)
|
||||
if not output_parameter:
|
||||
@@ -777,11 +806,13 @@ async def _fallback_to_ai_run(
|
||||
|
||||
# Update block status to completed if workflow block was created
|
||||
if workflow_run_block_id:
|
||||
# refresh the task
|
||||
refreshed_task = await app.DATABASE.get_task(task_id=task_id, organization_id=organization_id)
|
||||
if refreshed_task:
|
||||
task = refreshed_task
|
||||
await _update_workflow_block(
|
||||
workflow_run_block_id,
|
||||
BlockStatus.completed,
|
||||
task_id=context.task_id,
|
||||
step_id=context.step_id,
|
||||
BlockStatus(task.status.value),
|
||||
label=cache_key,
|
||||
)
|
||||
|
||||
@@ -1581,7 +1612,6 @@ async def run_script(
|
||||
user_script = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(user_script)
|
||||
|
||||
# Call run_workflow from the imported module
|
||||
if hasattr(user_script, "run_workflow"):
|
||||
# If parameters is None, pass an empty dict
|
||||
if parameters:
|
||||
|
||||
Reference in New Issue
Block a user