mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2025-09-14 09:19:40 +00:00
fix workflow run webhook status (#2458)
This commit is contained in:
parent
e6bb20f720
commit
04c47b7e79
2 changed files with 80 additions and 77 deletions
|
@ -8,7 +8,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
||||||
|
|
||||||
from skyvern.config import settings
|
from skyvern.config import settings
|
||||||
from skyvern.exceptions import WorkflowParameterNotFound
|
from skyvern.exceptions import WorkflowParameterNotFound, WorkflowRunNotFound
|
||||||
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
|
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
|
||||||
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType, TaskType
|
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType, TaskType
|
||||||
from skyvern.forge.sdk.db.exceptions import NotFoundError
|
from skyvern.forge.sdk.db.exceptions import NotFoundError
|
||||||
|
@ -1462,7 +1462,7 @@ class AgentDB:
|
||||||
|
|
||||||
async def update_workflow_run(
|
async def update_workflow_run(
|
||||||
self, workflow_run_id: str, status: WorkflowRunStatus, failure_reason: str | None = None
|
self, workflow_run_id: str, status: WorkflowRunStatus, failure_reason: str | None = None
|
||||||
) -> WorkflowRun | None:
|
) -> WorkflowRun:
|
||||||
async with self.Session() as session:
|
async with self.Session() as session:
|
||||||
workflow_run = (
|
workflow_run = (
|
||||||
await session.scalars(select(WorkflowRunModel).filter_by(workflow_run_id=workflow_run_id))
|
await session.scalars(select(WorkflowRunModel).filter_by(workflow_run_id=workflow_run_id))
|
||||||
|
@ -1474,11 +1474,8 @@ class AgentDB:
|
||||||
await session.refresh(workflow_run)
|
await session.refresh(workflow_run)
|
||||||
await save_workflow_run_logs(workflow_run_id)
|
await save_workflow_run_logs(workflow_run_id)
|
||||||
return convert_to_workflow_run(workflow_run)
|
return convert_to_workflow_run(workflow_run)
|
||||||
LOG.error(
|
else:
|
||||||
"WorkflowRun not found, nothing to update",
|
raise WorkflowRunNotFound(workflow_run_id)
|
||||||
workflow_run_id=workflow_run_id,
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def get_all_runs(
|
async def get_all_runs(
|
||||||
self, organization_id: str, page: int = 1, page_size: int = 10, status: list[WorkflowRunStatus] | None = None
|
self, organization_id: str, page: int = 1, page_size: int = 10, status: list[WorkflowRunStatus] | None = None
|
||||||
|
|
|
@ -193,7 +193,7 @@ class WorkflowService:
|
||||||
if isinstance(e, SkyvernException):
|
if isinstance(e, SkyvernException):
|
||||||
failure_reason = f"Setup workflow failed due to an SkyvernException({e.__class__.__name__}): {str(e)}"
|
failure_reason = f"Setup workflow failed due to an SkyvernException({e.__class__.__name__}): {str(e)}"
|
||||||
|
|
||||||
await self.mark_workflow_run_as_failed(
|
workflow_run = await self.mark_workflow_run_as_failed(
|
||||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
@ -219,7 +219,7 @@ class WorkflowService:
|
||||||
workflow = await self.get_workflow_by_permanent_id(workflow_permanent_id=workflow_run.workflow_permanent_id)
|
workflow = await self.get_workflow_by_permanent_id(workflow_permanent_id=workflow_run.workflow_permanent_id)
|
||||||
|
|
||||||
# Set workflow run status to running, create workflow run parameters
|
# Set workflow run status to running, create workflow run parameters
|
||||||
await self.mark_workflow_run_as_running(workflow_run_id=workflow_run.workflow_run_id)
|
workflow_run = await self.mark_workflow_run_as_running(workflow_run_id=workflow_run.workflow_run_id)
|
||||||
|
|
||||||
# Get all context parameters from the workflow definition
|
# Get all context parameters from the workflow definition
|
||||||
context_parameters = [
|
context_parameters = [
|
||||||
|
@ -266,7 +266,7 @@ class WorkflowService:
|
||||||
exception_message = f"unexpected SkyvernException({e.__class__.__name__}): {str(e)}"
|
exception_message = f"unexpected SkyvernException({e.__class__.__name__}): {str(e)}"
|
||||||
|
|
||||||
failure_reason = f"Failed to initialize workflow run context. failure reason: {exception_message}"
|
failure_reason = f"Failed to initialize workflow run context. failure reason: {exception_message}"
|
||||||
await self.mark_workflow_run_as_failed(
|
workflow_run = await self.mark_workflow_run_as_failed(
|
||||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||||
)
|
)
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
|
@ -284,11 +284,12 @@ class WorkflowService:
|
||||||
block_result = None
|
block_result = None
|
||||||
for block_idx, block in enumerate(blocks):
|
for block_idx, block in enumerate(blocks):
|
||||||
try:
|
try:
|
||||||
refreshed_workflow_run = await app.DATABASE.get_workflow_run(
|
if refreshed_workflow_run := await app.DATABASE.get_workflow_run(
|
||||||
workflow_run_id=workflow_run.workflow_run_id,
|
workflow_run_id=workflow_run.workflow_run_id,
|
||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
)
|
):
|
||||||
if refreshed_workflow_run and refreshed_workflow_run.status == WorkflowRunStatus.canceled:
|
workflow_run = refreshed_workflow_run
|
||||||
|
if workflow_run.status == WorkflowRunStatus.canceled:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Workflow run is canceled, stopping execution inside workflow execution loop",
|
"Workflow run is canceled, stopping execution inside workflow execution loop",
|
||||||
workflow_run_id=workflow_run.workflow_run_id,
|
workflow_run_id=workflow_run.workflow_run_id,
|
||||||
|
@ -306,7 +307,7 @@ class WorkflowService:
|
||||||
)
|
)
|
||||||
return workflow_run
|
return workflow_run
|
||||||
|
|
||||||
if refreshed_workflow_run and refreshed_workflow_run.status == WorkflowRunStatus.timed_out:
|
if workflow_run.status == WorkflowRunStatus.timed_out:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Workflow run is timed out, stopping execution inside workflow execution loop",
|
"Workflow run is timed out, stopping execution inside workflow execution loop",
|
||||||
workflow_run_id=workflow_run.workflow_run_id,
|
workflow_run_id=workflow_run.workflow_run_id,
|
||||||
|
@ -351,7 +352,9 @@ class WorkflowService:
|
||||||
block_type_var=block.block_type,
|
block_type_var=block.block_type,
|
||||||
block_label=block.label,
|
block_label=block.label,
|
||||||
)
|
)
|
||||||
await self.mark_workflow_run_as_canceled(workflow_run_id=workflow_run.workflow_run_id)
|
workflow_run = await self.mark_workflow_run_as_canceled(
|
||||||
|
workflow_run_id=workflow_run.workflow_run_id
|
||||||
|
)
|
||||||
# We're not sending a webhook here because the workflow run is manually marked as canceled.
|
# We're not sending a webhook here because the workflow run is manually marked as canceled.
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
workflow=workflow,
|
workflow=workflow,
|
||||||
|
@ -376,7 +379,7 @@ class WorkflowService:
|
||||||
failure_reason = (
|
failure_reason = (
|
||||||
f"{block.block_type} block failed. failure reason: {block_result.failure_reason}"
|
f"{block.block_type} block failed. failure reason: {block_result.failure_reason}"
|
||||||
)
|
)
|
||||||
await self.mark_workflow_run_as_failed(
|
workflow_run = await self.mark_workflow_run_as_failed(
|
||||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||||
)
|
)
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
|
@ -412,7 +415,7 @@ class WorkflowService:
|
||||||
|
|
||||||
if not block.continue_on_failure:
|
if not block.continue_on_failure:
|
||||||
failure_reason = f"{block.block_type} block terminated. Reason: {block_result.failure_reason}"
|
failure_reason = f"{block.block_type} block terminated. Reason: {block_result.failure_reason}"
|
||||||
await self.mark_workflow_run_as_terminated(
|
workflow_run = await self.mark_workflow_run_as_terminated(
|
||||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||||
)
|
)
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
|
@ -448,7 +451,7 @@ class WorkflowService:
|
||||||
|
|
||||||
if not block.continue_on_failure:
|
if not block.continue_on_failure:
|
||||||
failure_reason = f"{block.block_type} block timed out. Reason: {block_result.failure_reason}"
|
failure_reason = f"{block.block_type} block timed out. Reason: {block_result.failure_reason}"
|
||||||
await self.mark_workflow_run_as_failed(
|
workflow_run = await self.mark_workflow_run_as_failed(
|
||||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||||
)
|
)
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
|
@ -485,7 +488,7 @@ class WorkflowService:
|
||||||
exception_message = f"unexpected SkyvernException({e.__class__.__name__}): {str(e)}"
|
exception_message = f"unexpected SkyvernException({e.__class__.__name__}): {str(e)}"
|
||||||
|
|
||||||
failure_reason = f"{block.block_type} block failed. failure reason: {exception_message}"
|
failure_reason = f"{block.block_type} block failed. failure reason: {exception_message}"
|
||||||
await self.mark_workflow_run_as_failed(
|
workflow_run = await self.mark_workflow_run_as_failed(
|
||||||
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
workflow_run_id=workflow_run.workflow_run_id, failure_reason=failure_reason
|
||||||
)
|
)
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
|
@ -497,22 +500,23 @@ class WorkflowService:
|
||||||
)
|
)
|
||||||
return workflow_run
|
return workflow_run
|
||||||
|
|
||||||
refreshed_workflow_run = await app.DATABASE.get_workflow_run(
|
if refreshed_workflow_run := await app.DATABASE.get_workflow_run(
|
||||||
workflow_run_id=workflow_run.workflow_run_id,
|
workflow_run_id=workflow_run.workflow_run_id,
|
||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
)
|
):
|
||||||
if refreshed_workflow_run and refreshed_workflow_run.status not in (
|
workflow_run = refreshed_workflow_run
|
||||||
|
if workflow_run.status not in (
|
||||||
WorkflowRunStatus.canceled,
|
WorkflowRunStatus.canceled,
|
||||||
WorkflowRunStatus.failed,
|
WorkflowRunStatus.failed,
|
||||||
WorkflowRunStatus.terminated,
|
WorkflowRunStatus.terminated,
|
||||||
WorkflowRunStatus.timed_out,
|
WorkflowRunStatus.timed_out,
|
||||||
):
|
):
|
||||||
await self.mark_workflow_run_as_completed(workflow_run_id=workflow_run.workflow_run_id)
|
workflow_run = await self.mark_workflow_run_as_completed(workflow_run_id=workflow_run.workflow_run_id)
|
||||||
else:
|
else:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Workflow run is already timed_out, canceled, failed, or terminated, not marking as completed",
|
"Workflow run is already timed_out, canceled, failed, or terminated, not marking as completed",
|
||||||
workflow_run_id=workflow_run.workflow_run_id,
|
workflow_run_id=workflow_run.workflow_run_id,
|
||||||
workflow_run_status=refreshed_workflow_run.status if refreshed_workflow_run else None,
|
workflow_run_status=workflow_run.status if workflow_run else None,
|
||||||
)
|
)
|
||||||
await self.clean_up_workflow(
|
await self.clean_up_workflow(
|
||||||
workflow=workflow,
|
workflow=workflow,
|
||||||
|
@ -731,72 +735,74 @@ class WorkflowService:
|
||||||
parent_workflow_run_id=parent_workflow_run_id,
|
parent_workflow_run_id=parent_workflow_run_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def mark_workflow_run_as_completed(self, workflow_run_id: str) -> None:
|
async def mark_workflow_run_as_completed(self, workflow_run_id: str) -> WorkflowRun:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Marking workflow run {workflow_run_id} as completed",
|
f"Marking workflow run {workflow_run_id} as completed",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
workflow_status="completed",
|
workflow_status="completed",
|
||||||
)
|
)
|
||||||
await app.DATABASE.update_workflow_run(
|
return await app.DATABASE.update_workflow_run(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
status=WorkflowRunStatus.completed,
|
status=WorkflowRunStatus.completed,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def mark_workflow_run_as_failed(self, workflow_run_id: str, failure_reason: str | None) -> None:
|
async def mark_workflow_run_as_failed(self, workflow_run_id: str, failure_reason: str | None) -> WorkflowRun:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Marking workflow run {workflow_run_id} as failed",
|
f"Marking workflow run {workflow_run_id} as failed",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
workflow_status="failed",
|
workflow_status="failed",
|
||||||
failure_reason=failure_reason,
|
failure_reason=failure_reason,
|
||||||
)
|
)
|
||||||
await app.DATABASE.update_workflow_run(
|
return await app.DATABASE.update_workflow_run(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
status=WorkflowRunStatus.failed,
|
status=WorkflowRunStatus.failed,
|
||||||
failure_reason=failure_reason,
|
failure_reason=failure_reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def mark_workflow_run_as_running(self, workflow_run_id: str) -> None:
|
async def mark_workflow_run_as_running(self, workflow_run_id: str) -> WorkflowRun:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Marking workflow run {workflow_run_id} as running",
|
f"Marking workflow run {workflow_run_id} as running",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
workflow_status="running",
|
workflow_status="running",
|
||||||
)
|
)
|
||||||
await app.DATABASE.update_workflow_run(
|
return await app.DATABASE.update_workflow_run(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
status=WorkflowRunStatus.running,
|
status=WorkflowRunStatus.running,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def mark_workflow_run_as_terminated(self, workflow_run_id: str, failure_reason: str | None) -> None:
|
async def mark_workflow_run_as_terminated(self, workflow_run_id: str, failure_reason: str | None) -> WorkflowRun:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Marking workflow run {workflow_run_id} as terminated",
|
f"Marking workflow run {workflow_run_id} as terminated",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
workflow_status="terminated",
|
workflow_status="terminated",
|
||||||
failure_reason=failure_reason,
|
failure_reason=failure_reason,
|
||||||
)
|
)
|
||||||
await app.DATABASE.update_workflow_run(
|
return await app.DATABASE.update_workflow_run(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
status=WorkflowRunStatus.terminated,
|
status=WorkflowRunStatus.terminated,
|
||||||
failure_reason=failure_reason,
|
failure_reason=failure_reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def mark_workflow_run_as_canceled(self, workflow_run_id: str) -> None:
|
async def mark_workflow_run_as_canceled(self, workflow_run_id: str) -> WorkflowRun:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Marking workflow run {workflow_run_id} as canceled",
|
f"Marking workflow run {workflow_run_id} as canceled",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
workflow_status="canceled",
|
workflow_status="canceled",
|
||||||
)
|
)
|
||||||
await app.DATABASE.update_workflow_run(
|
return await app.DATABASE.update_workflow_run(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
status=WorkflowRunStatus.canceled,
|
status=WorkflowRunStatus.canceled,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def mark_workflow_run_as_timed_out(self, workflow_run_id: str, failure_reason: str | None = None) -> None:
|
async def mark_workflow_run_as_timed_out(
|
||||||
|
self, workflow_run_id: str, failure_reason: str | None = None
|
||||||
|
) -> WorkflowRun:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Marking workflow run {workflow_run_id} as timed out",
|
f"Marking workflow run {workflow_run_id} as timed out",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
workflow_status="timed_out",
|
workflow_status="timed_out",
|
||||||
)
|
)
|
||||||
await app.DATABASE.update_workflow_run(
|
return await app.DATABASE.update_workflow_run(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
status=WorkflowRunStatus.timed_out,
|
status=WorkflowRunStatus.timed_out,
|
||||||
failure_reason=failure_reason,
|
failure_reason=failure_reason,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue