create and update workflow run block inworkflow execution (#1419)
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
"""add failure_reason to workflow_run_blocks
|
||||||
|
|
||||||
|
Revision ID: 5be249d8dc96
|
||||||
|
Revises: f81d59b4aed5
|
||||||
|
Create Date: 2024-12-20 16:37:55.955910+00:00
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "5be249d8dc96"
|
||||||
|
down_revision: Union[str, None] = "f81d59b4aed5"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column("workflow_run_blocks", sa.Column("failure_reason", sa.String(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column("workflow_run_blocks", "failure_reason")
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -2027,11 +2027,16 @@ class AgentDB:
|
|||||||
workflow_run_block_id: str,
|
workflow_run_block_id: str,
|
||||||
status: BlockStatus | None = None,
|
status: BlockStatus | None = None,
|
||||||
output: dict | list | str | None = None,
|
output: dict | list | str | None = None,
|
||||||
|
failure_reason: str | None = None,
|
||||||
|
task_id: str | None = None,
|
||||||
|
organization_id: str | None = None,
|
||||||
) -> WorkflowRunBlock:
|
) -> WorkflowRunBlock:
|
||||||
async with self.Session() as session:
|
async with self.Session() as session:
|
||||||
workflow_run_block = (
|
workflow_run_block = (
|
||||||
await session.scalars(
|
await session.scalars(
|
||||||
select(WorkflowRunBlockModel).filter_by(workflow_run_block_id=workflow_run_block_id)
|
select(WorkflowRunBlockModel)
|
||||||
|
.filter_by(workflow_run_block_id=workflow_run_block_id)
|
||||||
|
.filter_by(organization_id=organization_id)
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
if workflow_run_block:
|
if workflow_run_block:
|
||||||
@@ -2039,6 +2044,10 @@ class AgentDB:
|
|||||||
workflow_run_block.status = status
|
workflow_run_block.status = status
|
||||||
if output:
|
if output:
|
||||||
workflow_run_block.output = output
|
workflow_run_block.output = output
|
||||||
|
if task_id:
|
||||||
|
workflow_run_block.task_id = task_id
|
||||||
|
if failure_reason:
|
||||||
|
workflow_run_block.failure_reason = failure_reason
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(workflow_run_block)
|
await session.refresh(workflow_run_block)
|
||||||
else:
|
else:
|
||||||
@@ -2048,3 +2057,24 @@ class AgentDB:
|
|||||||
if task_id:
|
if task_id:
|
||||||
task = await self.get_task(task_id, organization_id=workflow_run_block.organization_id)
|
task = await self.get_task(task_id, organization_id=workflow_run_block.organization_id)
|
||||||
return convert_to_workflow_run_block(workflow_run_block, task=task)
|
return convert_to_workflow_run_block(workflow_run_block, task=task)
|
||||||
|
|
||||||
|
async def get_workflow_run_block(
|
||||||
|
self,
|
||||||
|
workflow_run_block_id: str,
|
||||||
|
organization_id: str | None = None,
|
||||||
|
) -> WorkflowRunBlock:
|
||||||
|
async with self.Session() as session:
|
||||||
|
workflow_run_block = (
|
||||||
|
await session.scalars(
|
||||||
|
select(WorkflowRunBlockModel)
|
||||||
|
.filter_by(workflow_run_block_id=workflow_run_block_id)
|
||||||
|
.filter_by(organization_id=organization_id)
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
if workflow_run_block:
|
||||||
|
task = None
|
||||||
|
task_id = workflow_run_block.task_id
|
||||||
|
if task_id:
|
||||||
|
task = await self.get_task(task_id, organization_id=organization_id)
|
||||||
|
return convert_to_workflow_run_block(workflow_run_block, task=task)
|
||||||
|
raise NotFoundError(f"WorkflowRunBlock {workflow_run_block_id} not found")
|
||||||
|
|||||||
@@ -503,6 +503,7 @@ class WorkflowRunBlockModel(Base):
|
|||||||
status = Column(String, nullable=False)
|
status = Column(String, nullable=False)
|
||||||
output = Column(JSON, nullable=True)
|
output = Column(JSON, nullable=True)
|
||||||
continue_on_failure = Column(Boolean, nullable=False, default=False)
|
continue_on_failure = Column(Boolean, nullable=False, default=False)
|
||||||
|
failure_reason = Column(String, nullable=True)
|
||||||
|
|
||||||
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||||
modified_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow, nullable=False)
|
modified_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow, nullable=False)
|
||||||
|
|||||||
@@ -386,6 +386,7 @@ def convert_to_workflow_run_block(
|
|||||||
status=BlockStatus(workflow_run_block_model.status),
|
status=BlockStatus(workflow_run_block_model.status),
|
||||||
output=workflow_run_block_model.output,
|
output=workflow_run_block_model.output,
|
||||||
continue_on_failure=workflow_run_block_model.continue_on_failure,
|
continue_on_failure=workflow_run_block_model.continue_on_failure,
|
||||||
|
failure_reason=workflow_run_block_model.failure_reason,
|
||||||
task_id=workflow_run_block_model.task_id,
|
task_id=workflow_run_block_model.task_id,
|
||||||
created_at=workflow_run_block_model.created_at,
|
created_at=workflow_run_block_model.created_at,
|
||||||
modified_at=workflow_run_block_model.modified_at,
|
modified_at=workflow_run_block_model.modified_at,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class WorkflowRunBlock(BaseModel):
|
|||||||
status: str | None = None
|
status: str | None = None
|
||||||
output: dict | list | str | None = None
|
output: dict | list | str | None = None
|
||||||
continue_on_failure: bool = False
|
continue_on_failure: bool = False
|
||||||
|
failure_reason: str | None = None
|
||||||
task_id: str | None = None
|
task_id: str | None = None
|
||||||
url: str | None = None
|
url: str | None = None
|
||||||
navigation_goal: str | None = None
|
navigation_goal: str | None = None
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ async def run_observer_cruise(
|
|||||||
break
|
break
|
||||||
|
|
||||||
# generate the extraction task
|
# generate the extraction task
|
||||||
block_result = await block.execute_safe(workflow_run_id=workflow_run_id)
|
block_result = await block.execute_safe(workflow_run_id=workflow_run_id, organization_id=organization_id)
|
||||||
|
|
||||||
# refresh workflow
|
# refresh workflow
|
||||||
yaml_blocks.extend(block_yaml_list)
|
yaml_blocks.extend(block_yaml_list)
|
||||||
@@ -534,7 +534,10 @@ async def _generate_loop_task(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# execute the extraction block
|
# execute the extraction block
|
||||||
extraction_block_result = await extraction_block_for_loop.execute_safe(workflow_run_id=workflow_run_id)
|
extraction_block_result = await extraction_block_for_loop.execute_safe(
|
||||||
|
workflow_run_id=workflow_run_id,
|
||||||
|
organization_id=observer_cruise.organization_id,
|
||||||
|
)
|
||||||
LOG.info("Extraction block result", extraction_block_result=extraction_block_result)
|
LOG.info("Extraction block result", extraction_block_result=extraction_block_result)
|
||||||
if extraction_block_result.success is False:
|
if extraction_block_result.success is False:
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|||||||
@@ -128,13 +128,27 @@ class Block(BaseModel, abc.ABC):
|
|||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
def build_block_result(
|
async def build_block_result(
|
||||||
self,
|
self,
|
||||||
success: bool,
|
success: bool,
|
||||||
failure_reason: str | None,
|
failure_reason: str | None,
|
||||||
output_parameter_value: dict[str, Any] | list | str | None = None,
|
output_parameter_value: dict[str, Any] | list | str | None = None,
|
||||||
status: BlockStatus | None = None,
|
status: BlockStatus | None = None,
|
||||||
|
workflow_run_block_id: str | None = None,
|
||||||
|
organization_id: str | None = None,
|
||||||
) -> BlockResult:
|
) -> BlockResult:
|
||||||
|
# TODO: update workflow run block status and failure reason
|
||||||
|
if isinstance(output_parameter_value, str):
|
||||||
|
output_parameter_value = {"value": output_parameter_value}
|
||||||
|
|
||||||
|
if workflow_run_block_id:
|
||||||
|
await app.DATABASE.update_workflow_run_block(
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
output=output_parameter_value,
|
||||||
|
status=status,
|
||||||
|
failure_reason=failure_reason,
|
||||||
|
organization_id=organization_id,
|
||||||
|
)
|
||||||
return BlockResult(
|
return BlockResult(
|
||||||
success=success,
|
success=success,
|
||||||
failure_reason=failure_reason,
|
failure_reason=failure_reason,
|
||||||
@@ -167,12 +181,30 @@ class Block(BaseModel, abc.ABC):
|
|||||||
return app.WORKFLOW_CONTEXT_MANAGER.aws_client
|
return app.WORKFLOW_CONTEXT_MANAGER.aws_client
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def execute_safe(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute_safe(
|
||||||
|
self,
|
||||||
|
workflow_run_id: str,
|
||||||
|
parent_workflow_run_block_id: str | None = None,
|
||||||
|
organization_id: str | None = None,
|
||||||
|
**kwargs: dict,
|
||||||
|
) -> BlockResult:
|
||||||
|
workflow_run_block_id = None
|
||||||
try:
|
try:
|
||||||
return await self.execute(workflow_run_id, **kwargs)
|
workflow_run_block = await app.DATABASE.create_workflow_run_block(
|
||||||
|
workflow_run_id=workflow_run_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
parent_workflow_run_block_id=parent_workflow_run_block_id,
|
||||||
|
label=self.label,
|
||||||
|
block_type=self.block_type,
|
||||||
|
continue_on_failure=self.continue_on_failure,
|
||||||
|
)
|
||||||
|
workflow_run_block_id = workflow_run_block.workflow_run_block_id
|
||||||
|
return await self.execute(workflow_run_id, workflow_run_block_id, organization_id=organization_id, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(
|
LOG.exception(
|
||||||
"Block execution failed",
|
"Block execution failed",
|
||||||
@@ -188,7 +220,14 @@ class Block(BaseModel, abc.ABC):
|
|||||||
failure_reason = "unexpected exception"
|
failure_reason = "unexpected exception"
|
||||||
if isinstance(e, SkyvernException):
|
if isinstance(e, SkyvernException):
|
||||||
failure_reason = f"unexpected SkyvernException({e.__class__.__name__})"
|
failure_reason = f"unexpected SkyvernException({e.__class__.__name__})"
|
||||||
return self.build_block_result(success=False, failure_reason=failure_reason, status=BlockStatus.failed)
|
|
||||||
|
return await self.build_block_result(
|
||||||
|
success=False,
|
||||||
|
failure_reason=failure_reason,
|
||||||
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
)
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_all_parameters(
|
def get_all_parameters(
|
||||||
@@ -304,7 +343,9 @@ class BaseTaskBlock(Block):
|
|||||||
|
|
||||||
return order, retry + 1
|
return order, retry + 1
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
current_retry = 0
|
current_retry = 0
|
||||||
# initial value for will_retry is True, so that the loop runs at least once
|
# initial value for will_retry is True, so that the loop runs at least once
|
||||||
@@ -350,11 +391,13 @@ class BaseTaskBlock(Block):
|
|||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context=workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context=workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO (kerem) we should always retry on terminated. We should make a distinction between retriable and
|
# TODO (kerem) we should always retry on terminated. We should make a distinction between retriable and
|
||||||
@@ -370,6 +413,11 @@ class BaseTaskBlock(Block):
|
|||||||
task_order=task_order,
|
task_order=task_order,
|
||||||
task_retry=task_retry,
|
task_retry=task_retry,
|
||||||
)
|
)
|
||||||
|
await app.DATABASE.update_workflow_run_block(
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
task_id=task.task_id,
|
||||||
|
organization_id=workflow.organization_id,
|
||||||
|
)
|
||||||
current_running_task = task
|
current_running_task = task
|
||||||
organization = await app.DATABASE.get_organization(organization_id=workflow.organization_id)
|
organization = await app.DATABASE.get_organization(organization_id=workflow.organization_id)
|
||||||
if not organization:
|
if not organization:
|
||||||
@@ -475,11 +523,13 @@ class BaseTaskBlock(Block):
|
|||||||
task_output = TaskOutput.from_task(updated_task)
|
task_output = TaskOutput.from_task(updated_task)
|
||||||
output_parameter_value = task_output.model_dump()
|
output_parameter_value = task_output.model_dump()
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, output_parameter_value)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, output_parameter_value)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=success,
|
success=success,
|
||||||
failure_reason=updated_task.failure_reason,
|
failure_reason=updated_task.failure_reason,
|
||||||
output_parameter_value=output_parameter_value,
|
output_parameter_value=output_parameter_value,
|
||||||
status=block_status_mapping[updated_task.status],
|
status=block_status_mapping[updated_task.status],
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
elif updated_task.status == TaskStatus.canceled:
|
elif updated_task.status == TaskStatus.canceled:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
@@ -490,11 +540,13 @@ class BaseTaskBlock(Block):
|
|||||||
workflow_id=workflow.workflow_id,
|
workflow_id=workflow.workflow_id,
|
||||||
organization_id=workflow.organization_id,
|
organization_id=workflow.organization_id,
|
||||||
)
|
)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=updated_task.failure_reason,
|
failure_reason=updated_task.failure_reason,
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=block_status_mapping[updated_task.status],
|
status=block_status_mapping[updated_task.status],
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
current_retry += 1
|
current_retry += 1
|
||||||
@@ -517,18 +569,22 @@ class BaseTaskBlock(Block):
|
|||||||
await self.record_output_parameter_value(
|
await self.record_output_parameter_value(
|
||||||
workflow_run_context, workflow_run_id, output_parameter_value
|
workflow_run_context, workflow_run_id, output_parameter_value
|
||||||
)
|
)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=updated_task.failure_reason,
|
failure_reason=updated_task.failure_reason,
|
||||||
output_parameter_value=output_parameter_value,
|
output_parameter_value=output_parameter_value,
|
||||||
status=block_status_mapping[updated_task.status],
|
status=block_status_mapping[updated_task.status],
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
failure_reason=current_running_task.failure_reason if current_running_task else None,
|
failure_reason=current_running_task.failure_reason if current_running_task else None,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -675,7 +731,12 @@ class ForLoopBlock(Block):
|
|||||||
return [parameter_value]
|
return [parameter_value]
|
||||||
|
|
||||||
async def execute_loop_helper(
|
async def execute_loop_helper(
|
||||||
self, workflow_run_id: str, workflow_run_context: WorkflowRunContext, loop_over_values: list[Any]
|
self,
|
||||||
|
workflow_run_id: str,
|
||||||
|
workflow_run_block_id: str,
|
||||||
|
workflow_run_context: WorkflowRunContext,
|
||||||
|
loop_over_values: list[Any],
|
||||||
|
organization_id: str | None = None,
|
||||||
) -> LoopBlockExecutedResult:
|
) -> LoopBlockExecutedResult:
|
||||||
outputs_with_loop_values: list[list[dict[str, Any]]] = []
|
outputs_with_loop_values: list[list[dict[str, Any]]] = []
|
||||||
block_outputs: list[BlockResult] = []
|
block_outputs: list[BlockResult] = []
|
||||||
@@ -698,7 +759,11 @@ class ForLoopBlock(Block):
|
|||||||
loop_block = loop_block.copy()
|
loop_block = loop_block.copy()
|
||||||
current_block = loop_block
|
current_block = loop_block
|
||||||
|
|
||||||
block_output = await loop_block.execute_safe(workflow_run_id=workflow_run_id)
|
block_output = await loop_block.execute_safe(
|
||||||
|
workflow_run_id=workflow_run_id,
|
||||||
|
parent_workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
)
|
||||||
each_loop_output_values.append(
|
each_loop_output_values.append(
|
||||||
{
|
{
|
||||||
"loop_value": loop_over_value,
|
"loop_value": loop_over_value,
|
||||||
@@ -747,15 +812,19 @@ class ForLoopBlock(Block):
|
|||||||
last_block=current_block,
|
last_block=current_block,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
try:
|
try:
|
||||||
loop_over_values = self.get_loop_over_parameter_values(workflow_run_context)
|
loop_over_values = self.get_loop_over_parameter_values(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"failed to get loop values: {str(e)}",
|
failure_reason=f"failed to get loop values: {str(e)}",
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
LOG.info(
|
LOG.info(
|
||||||
@@ -772,10 +841,12 @@ class ForLoopBlock(Block):
|
|||||||
num_loop_over_values=len(loop_over_values),
|
num_loop_over_values=len(loop_over_values),
|
||||||
)
|
)
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, [])
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, [])
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason="No iterable value found for the loop block",
|
failure_reason="No iterable value found for the loop block",
|
||||||
status=BlockStatus.terminated,
|
status=BlockStatus.terminated,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.loop_blocks or len(self.loop_blocks) == 0:
|
if not self.loop_blocks or len(self.loop_blocks) == 0:
|
||||||
@@ -786,14 +857,20 @@ class ForLoopBlock(Block):
|
|||||||
num_loop_blocks=len(self.loop_blocks),
|
num_loop_blocks=len(self.loop_blocks),
|
||||||
)
|
)
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, [])
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, [])
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False, failure_reason="No defined blocks to loop", status=BlockStatus.terminated
|
success=False,
|
||||||
|
failure_reason="No defined blocks to loop",
|
||||||
|
status=BlockStatus.terminated,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
loop_executed_result = await self.execute_loop_helper(
|
loop_executed_result = await self.execute_loop_helper(
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
workflow_run_context=workflow_run_context,
|
workflow_run_context=workflow_run_context,
|
||||||
loop_over_values=loop_over_values,
|
loop_over_values=loop_over_values,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
await self.record_output_parameter_value(
|
await self.record_output_parameter_value(
|
||||||
workflow_run_context, workflow_run_id, loop_executed_result.outputs_with_loop_values
|
workflow_run_context, workflow_run_id, loop_executed_result.outputs_with_loop_values
|
||||||
@@ -811,11 +888,13 @@ class ForLoopBlock(Block):
|
|||||||
else:
|
else:
|
||||||
block_status = BlockStatus.failed
|
block_status = BlockStatus.failed
|
||||||
|
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=success,
|
success=success,
|
||||||
failure_reason=loop_executed_result.get_failure_reason(),
|
failure_reason=loop_executed_result.get_failure_reason(),
|
||||||
output_parameter_value=loop_executed_result.outputs_with_loop_values,
|
output_parameter_value=loop_executed_result.outputs_with_loop_values,
|
||||||
status=block_status,
|
status=block_status,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -834,18 +913,22 @@ class CodeBlock(Block):
|
|||||||
def format_potential_template_parameters(self, workflow_run_context: WorkflowRunContext) -> None:
|
def format_potential_template_parameters(self, workflow_run_context: WorkflowRunContext) -> None:
|
||||||
self.code = self.format_block_parameter_template_from_workflow_run_context(self.code, workflow_run_context)
|
self.code = self.format_block_parameter_template_from_workflow_run_context(self.code, workflow_run_context)
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
raise DisabledBlockExecutionError("CodeBlock is disabled")
|
raise DisabledBlockExecutionError("CodeBlock is disabled")
|
||||||
# get workflow run context
|
# get workflow run context
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# get all parameters into a dictionary
|
# get all parameters into a dictionary
|
||||||
@@ -887,7 +970,13 @@ async def user_code():
|
|||||||
|
|
||||||
result = {"result": result_container.get("result")}
|
result = {"result": result_container.get("result")}
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result)
|
||||||
return self.build_block_result(success=True, output_parameter_value=result, status=BlockStatus.completed)
|
return await self.build_block_result(
|
||||||
|
success=True,
|
||||||
|
output_parameter_value=result,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_TEXT_PROMPT_LLM_KEY = settings.SECONDARY_LLM_KEY or settings.LLM_KEY
|
DEFAULT_TEXT_PROMPT_LLM_KEY = settings.SECONDARY_LLM_KEY or settings.LLM_KEY
|
||||||
@@ -944,17 +1033,21 @@ class TextPromptBlock(Block):
|
|||||||
LOG.info("TextPromptBlock: Received response from LLM", response=response)
|
LOG.info("TextPromptBlock: Received response from LLM", response=response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
# get workflow run context
|
# get workflow run context
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
# get all parameters into a dictionary
|
# get all parameters into a dictionary
|
||||||
parameter_values = {}
|
parameter_values = {}
|
||||||
@@ -968,8 +1061,13 @@ class TextPromptBlock(Block):
|
|||||||
|
|
||||||
response = await self.send_prompt(self.prompt, parameter_values)
|
response = await self.send_prompt(self.prompt, parameter_values)
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, response)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, response)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=True, failure_reason=None, output_parameter_value=response, status=BlockStatus.completed
|
success=True,
|
||||||
|
failure_reason=None,
|
||||||
|
output_parameter_value=response,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1000,7 +1098,9 @@ class DownloadToS3Block(Block):
|
|||||||
# Clean up the temporary file since it's created with delete=False
|
# Clean up the temporary file since it's created with delete=False
|
||||||
os.unlink(file_path)
|
os.unlink(file_path)
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
# get workflow run context
|
# get workflow run context
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
# get all parameters into a dictionary
|
# get all parameters into a dictionary
|
||||||
@@ -1017,11 +1117,13 @@ class DownloadToS3Block(Block):
|
|||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1040,8 +1142,13 @@ class DownloadToS3Block(Block):
|
|||||||
|
|
||||||
LOG.info("DownloadToS3Block: File downloaded and uploaded to S3", uri=uri)
|
LOG.info("DownloadToS3Block: File downloaded and uploaded to S3", uri=uri)
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, uri)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, uri)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=True, failure_reason=None, output_parameter_value=uri, status=BlockStatus.completed
|
success=True,
|
||||||
|
failure_reason=None,
|
||||||
|
output_parameter_value=uri,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1072,7 +1179,9 @@ class UploadToS3Block(Block):
|
|||||||
s3_key = f"{settings.ENV}/{workflow_run_id}/{uuid.uuid4()}_{Path(path).name}"
|
s3_key = f"{settings.ENV}/{workflow_run_id}/{uuid.uuid4()}_{Path(path).name}"
|
||||||
return f"s3://{s3_bucket}/{s3_key}"
|
return f"s3://{s3_bucket}/{s3_key}"
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
# get workflow run context
|
# get workflow run context
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
# get all parameters into a dictionary
|
# get all parameters into a dictionary
|
||||||
@@ -1092,11 +1201,13 @@ class UploadToS3Block(Block):
|
|||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.path or not os.path.exists(self.path):
|
if not self.path or not os.path.exists(self.path):
|
||||||
@@ -1130,8 +1241,13 @@ class UploadToS3Block(Block):
|
|||||||
|
|
||||||
LOG.info("UploadToS3Block: File(s) uploaded to S3", file_path=self.path)
|
LOG.info("UploadToS3Block: File(s) uploaded to S3", file_path=self.path)
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, s3_uris)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, s3_uris)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=True, failure_reason=None, output_parameter_value=s3_uris, status=BlockStatus.completed
|
success=True,
|
||||||
|
failure_reason=None,
|
||||||
|
output_parameter_value=s3_uris,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1386,16 +1502,20 @@ class SendEmailBlock(Block):
|
|||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
smtp_host_value, smtp_port_value, smtp_username_value, smtp_password_value = self._decrypt_smtp_parameters(
|
smtp_host_value, smtp_port_value, smtp_username_value, smtp_password_value = self._decrypt_smtp_parameters(
|
||||||
workflow_run_context
|
workflow_run_context
|
||||||
@@ -1415,8 +1535,13 @@ class SendEmailBlock(Block):
|
|||||||
LOG.error("SendEmailBlock: Failed to send email", exc_info=True)
|
LOG.error("SendEmailBlock: Failed to send email", exc_info=True)
|
||||||
result_dict = {"success": False, "error": str(e)}
|
result_dict = {"success": False, "error": str(e)}
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result_dict)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result_dict)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False, failure_reason=str(e), output_parameter_value=result_dict, status=BlockStatus.failed
|
success=False,
|
||||||
|
failure_reason=str(e),
|
||||||
|
output_parameter_value=result_dict,
|
||||||
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
if smtp_host:
|
if smtp_host:
|
||||||
@@ -1424,8 +1549,13 @@ class SendEmailBlock(Block):
|
|||||||
|
|
||||||
result_dict = {"success": True}
|
result_dict = {"success": True}
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result_dict)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result_dict)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=True, failure_reason=None, output_parameter_value=result_dict, status=BlockStatus.completed
|
success=True,
|
||||||
|
failure_reason=None,
|
||||||
|
output_parameter_value=result_dict,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1461,7 +1591,9 @@ class FileParserBlock(Block):
|
|||||||
except csv.Error as e:
|
except csv.Error as e:
|
||||||
raise InvalidFileType(file_url=file_url_used, file_type=self.file_type, error=str(e))
|
raise InvalidFileType(file_url=file_url_used, file_type=self.file_type, error=str(e))
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
if (
|
if (
|
||||||
self.file_url
|
self.file_url
|
||||||
@@ -1480,11 +1612,13 @@ class FileParserBlock(Block):
|
|||||||
try:
|
try:
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.failed,
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Download the file
|
# Download the file
|
||||||
@@ -1503,8 +1637,13 @@ class FileParserBlock(Block):
|
|||||||
parsed_data.append(row)
|
parsed_data.append(row)
|
||||||
# Record the parsed data
|
# Record the parsed data
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, parsed_data)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, parsed_data)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=True, failure_reason=None, output_parameter_value=parsed_data, status=BlockStatus.completed
|
success=True,
|
||||||
|
failure_reason=None,
|
||||||
|
output_parameter_value=parsed_data,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1520,7 +1659,9 @@ class WaitBlock(Block):
|
|||||||
) -> list[PARAMETER_TYPE]:
|
) -> list[PARAMETER_TYPE]:
|
||||||
return self.parameters
|
return self.parameters
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
# TODO: we need to support to interrupt the sleep when the workflow run failed/cancelled/terminated
|
# TODO: we need to support to interrupt the sleep when the workflow run failed/cancelled/terminated
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Going to pause the workflow for a while",
|
"Going to pause the workflow for a while",
|
||||||
@@ -1531,8 +1672,13 @@ class WaitBlock(Block):
|
|||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
result_dict = {"success": True}
|
result_dict = {"success": True}
|
||||||
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result_dict)
|
await self.record_output_parameter_value(workflow_run_context, workflow_run_id, result_dict)
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=True, failure_reason=None, output_parameter_value=result_dict, status=BlockStatus.completed
|
success=True,
|
||||||
|
failure_reason=None,
|
||||||
|
output_parameter_value=result_dict,
|
||||||
|
status=BlockStatus.completed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1545,18 +1691,27 @@ class ValidationBlock(BaseTaskBlock):
|
|||||||
) -> list[PARAMETER_TYPE]:
|
) -> list[PARAMETER_TYPE]:
|
||||||
return self.parameters
|
return self.parameters
|
||||||
|
|
||||||
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
|
async def execute(
|
||||||
|
self, workflow_run_id: str, workflow_run_block_id: str, organization_id: str | None = None, **kwargs: dict
|
||||||
|
) -> BlockResult:
|
||||||
task_order, _ = await self.get_task_order(workflow_run_id, 0)
|
task_order, _ = await self.get_task_order(workflow_run_id, 0)
|
||||||
is_first_task = task_order == 0
|
is_first_task = task_order == 0
|
||||||
if is_first_task:
|
if is_first_task:
|
||||||
return self.build_block_result(
|
return await self.build_block_result(
|
||||||
success=False,
|
success=False,
|
||||||
failure_reason="Validation block should not be the first block",
|
failure_reason="Validation block should not be the first block",
|
||||||
output_parameter_value=None,
|
output_parameter_value=None,
|
||||||
status=BlockStatus.terminated,
|
status=BlockStatus.terminated,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await super().execute(workflow_run_id=workflow_run_id, kwargs=kwargs)
|
return await super().execute(
|
||||||
|
workflow_run_id=workflow_run_id,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
kwargs=kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ActionBlock(BaseTaskBlock):
|
class ActionBlock(BaseTaskBlock):
|
||||||
|
|||||||
@@ -248,7 +248,10 @@ class WorkflowService:
|
|||||||
block_type_var=block.block_type,
|
block_type_var=block.block_type,
|
||||||
block_label=block.label,
|
block_label=block.label,
|
||||||
)
|
)
|
||||||
block_result = await block.execute_safe(workflow_run_id=workflow_run_id)
|
block_result = await block.execute_safe(
|
||||||
|
workflow_run_id=workflow_run_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
)
|
||||||
if block_result.status == BlockStatus.canceled:
|
if block_result.status == BlockStatus.canceled:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
f"Block with type {block.block_type} at index {block_idx}/{blocks_cnt -1} was canceled for workflow run {workflow_run_id}, cancelling workflow run",
|
f"Block with type {block.block_type} at index {block_idx}/{blocks_cnt -1} was canceled for workflow run {workflow_run_id}, cancelling workflow run",
|
||||||
|
|||||||
Reference in New Issue
Block a user