run script with workflow block run, task, step and artifacts built (#3234)
This commit is contained in:
@@ -872,6 +872,12 @@ class ForgeAgent:
|
||||
step_order=step.order,
|
||||
step_retry=step.retry_index,
|
||||
)
|
||||
|
||||
# Update context with step_id for auto action/screenshot creation
|
||||
context = skyvern_context.current()
|
||||
if context:
|
||||
context.step_id = step.step_id
|
||||
|
||||
step = await self.update_step(step=step, status=StepStatus.running)
|
||||
await app.AGENT_FUNCTION.prepare_step_execution(
|
||||
organization=organization, task=task, step=step, browser_state=browser_state
|
||||
|
||||
@@ -11,6 +11,7 @@ class SkyvernContext:
|
||||
organization_id: str | None = None
|
||||
organization_name: str | None = None
|
||||
task_id: str | None = None
|
||||
step_id: str | None = None
|
||||
workflow_id: str | None = None
|
||||
workflow_permanent_id: str | None = None
|
||||
workflow_run_id: str | None = None
|
||||
@@ -30,7 +31,7 @@ class SkyvernContext:
|
||||
script_revision_id: str | None = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"SkyvernContext(request_id={self.request_id}, organization_id={self.organization_id}, task_id={self.task_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})"
|
||||
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})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
@@ -139,11 +139,12 @@ class AgentDB:
|
||||
self,
|
||||
url: str,
|
||||
title: str | None,
|
||||
complete_criterion: str | None,
|
||||
terminate_criterion: str | None,
|
||||
navigation_goal: str | None,
|
||||
data_extraction_goal: str | None,
|
||||
navigation_payload: dict[str, Any] | list | str | None,
|
||||
status: str = "created",
|
||||
complete_criterion: str | None = None,
|
||||
terminate_criterion: str | None = None,
|
||||
webhook_callback_url: str | None = None,
|
||||
totp_verification_url: str | None = None,
|
||||
totp_identifier: str | None = None,
|
||||
@@ -166,7 +167,7 @@ class AgentDB:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
new_task = TaskModel(
|
||||
status="created",
|
||||
status=status,
|
||||
task_type=task_type,
|
||||
url=url,
|
||||
title=title,
|
||||
|
||||
@@ -94,7 +94,7 @@ from skyvern.forge.sdk.workflow.models.workflow import (
|
||||
WorkflowRunStatus,
|
||||
)
|
||||
from skyvern.schemas.runs import ProxyLocation, RunStatus, RunType, WorkflowRunRequest, WorkflowRunResponse
|
||||
from skyvern.schemas.scripts import FileEncoding, ScriptFileCreate
|
||||
from skyvern.schemas.scripts import FileEncoding, Script, ScriptFileCreate
|
||||
from skyvern.schemas.workflows import (
|
||||
BLOCK_YAML_TYPES,
|
||||
BlockStatus,
|
||||
@@ -277,6 +277,24 @@ class WorkflowService:
|
||||
workflow_run = await self.get_workflow_run(workflow_run_id=workflow_run_id, organization_id=organization_id)
|
||||
workflow = await self.get_workflow_by_permanent_id(workflow_permanent_id=workflow_run.workflow_permanent_id)
|
||||
|
||||
# Check if there's a related workflow script that should be used instead
|
||||
workflow_script = await self._get_workflow_script(workflow, workflow_run)
|
||||
if workflow_script is not None:
|
||||
LOG.info(
|
||||
"Found related workflow script, running script instead of workflow",
|
||||
workflow_run_id=workflow_run_id,
|
||||
workflow_id=workflow.workflow_id,
|
||||
organization_id=organization_id,
|
||||
workflow_script_id=workflow_script.script_id,
|
||||
)
|
||||
return await self._execute_workflow_script(
|
||||
script_id=workflow_script.script_id,
|
||||
workflow=workflow,
|
||||
workflow_run=workflow_run,
|
||||
api_key=api_key,
|
||||
organization=organization,
|
||||
)
|
||||
|
||||
# Set workflow run status to running, create workflow run parameters
|
||||
workflow_run = await self.mark_workflow_run_as_running(workflow_run_id=workflow_run.workflow_run_id)
|
||||
|
||||
@@ -2269,6 +2287,109 @@ class WorkflowService:
|
||||
|
||||
return result
|
||||
|
||||
async def _get_workflow_script(self, workflow: Workflow, workflow_run: WorkflowRun) -> Script | None:
|
||||
"""
|
||||
Check if there's a related workflow script that should be used instead of running the workflow.
|
||||
Returns True if a script should be used, False otherwise.
|
||||
"""
|
||||
if not workflow.generate_script:
|
||||
return None
|
||||
# Only check for scripts if the workflow has a cache_key
|
||||
cache_key = workflow.cache_key or ""
|
||||
|
||||
try:
|
||||
# Render the cache_key_value from workflow run parameters (same logic as generate_script_for_workflow)
|
||||
parameter_tuples = await app.DATABASE.get_workflow_run_parameters(
|
||||
workflow_run_id=workflow_run.workflow_run_id
|
||||
)
|
||||
parameters = {wf_param.key: run_param.value for wf_param, run_param in parameter_tuples}
|
||||
|
||||
jinja_sandbox_env = SandboxedEnvironment()
|
||||
rendered_cache_key_value = jinja_sandbox_env.from_string(cache_key).render(parameters)
|
||||
|
||||
# Check if there are existing cached scripts for this workflow + cache_key_value
|
||||
existing_scripts = await app.DATABASE.get_workflow_scripts_by_cache_key_value(
|
||||
organization_id=workflow.organization_id,
|
||||
workflow_permanent_id=workflow.workflow_permanent_id,
|
||||
cache_key_value=rendered_cache_key_value,
|
||||
)
|
||||
|
||||
if existing_scripts:
|
||||
LOG.info(
|
||||
"Found cached script for workflow",
|
||||
workflow_id=workflow.workflow_id,
|
||||
cache_key_value=rendered_cache_key_value,
|
||||
workflow_run_id=workflow_run.workflow_run_id,
|
||||
script_count=len(existing_scripts),
|
||||
)
|
||||
return existing_scripts[0]
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to check for workflow script, proceeding with normal workflow execution",
|
||||
workflow_id=workflow.workflow_id,
|
||||
workflow_run_id=workflow_run.workflow_run_id,
|
||||
error=str(e),
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
|
||||
async def _execute_workflow_script(
|
||||
self, script_id: str, workflow: Workflow, workflow_run: WorkflowRun, api_key: str, organization: Organization
|
||||
) -> WorkflowRun:
|
||||
"""
|
||||
Execute the related workflow script instead of running the workflow blocks.
|
||||
"""
|
||||
|
||||
try:
|
||||
# Set workflow run status to running
|
||||
workflow_run = await self.mark_workflow_run_as_running(workflow_run_id=workflow_run.workflow_run_id)
|
||||
|
||||
# Render the cache_key_value to find the right script
|
||||
parameter_tuples = await app.DATABASE.get_workflow_run_parameters(
|
||||
workflow_run_id=workflow_run.workflow_run_id
|
||||
)
|
||||
parameters = {wf_param.key: run_param.value for wf_param, run_param in parameter_tuples}
|
||||
|
||||
# Execute the script using script_service
|
||||
await script_service.execute_script(
|
||||
script_id=script_id,
|
||||
organization_id=organization.organization_id,
|
||||
parameters=parameters,
|
||||
workflow_run_id=workflow_run.workflow_run_id,
|
||||
background_tasks=None, # Execute synchronously
|
||||
)
|
||||
|
||||
# Mark workflow run as completed
|
||||
workflow_run = await self.mark_workflow_run_as_completed(workflow_run_id=workflow_run.workflow_run_id)
|
||||
|
||||
LOG.info(
|
||||
"Successfully executed workflow script",
|
||||
workflow_run_id=workflow_run.workflow_run_id,
|
||||
script_id=script_id,
|
||||
organization_id=organization.organization_id,
|
||||
)
|
||||
|
||||
return workflow_run
|
||||
|
||||
except Exception as e:
|
||||
LOG.error(
|
||||
"Failed to execute workflow script, marking workflow run as failed",
|
||||
workflow_run_id=workflow_run.workflow_run_id,
|
||||
error=str(e),
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
# Mark workflow run as failed
|
||||
failure_reason = f"Failed to execute workflow script: {str(e)}"
|
||||
workflow_run = await self.mark_workflow_run_as_failed(
|
||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||
)
|
||||
|
||||
return workflow_run
|
||||
|
||||
async def generate_script_for_workflow(
|
||||
self,
|
||||
workflow: Workflow,
|
||||
|
||||
Reference in New Issue
Block a user