Jon/backend hitl buffs (#3826)
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
"""add fields to workflow_run_blocks table
|
||||||
|
|
||||||
|
Revision ID: d135e472622c
|
||||||
|
Revises: b80c42316c94
|
||||||
|
Create Date: 2025-10-27 18:43:20.933288+00:00
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "d135e472622c"
|
||||||
|
down_revision: Union[str, None] = "b80c42316c94"
|
||||||
|
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("instructions", sa.String(), nullable=True))
|
||||||
|
op.add_column("workflow_run_blocks", sa.Column("positive_descriptor", sa.String(), nullable=True))
|
||||||
|
op.add_column("workflow_run_blocks", sa.Column("negative_descriptor", sa.String(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column("workflow_run_blocks", "negative_descriptor")
|
||||||
|
op.drop_column("workflow_run_blocks", "positive_descriptor")
|
||||||
|
op.drop_column("workflow_run_blocks", "instructions")
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -3289,6 +3289,10 @@ class AgentDB:
|
|||||||
http_request_timeout: int | None = None,
|
http_request_timeout: int | None = None,
|
||||||
http_request_follow_redirects: bool | None = None,
|
http_request_follow_redirects: bool | None = None,
|
||||||
ai_fallback_triggered: bool | None = None,
|
ai_fallback_triggered: bool | None = None,
|
||||||
|
# human interaction block
|
||||||
|
instructions: str | None = None,
|
||||||
|
positive_descriptor: str | None = None,
|
||||||
|
negative_descriptor: str | None = None,
|
||||||
) -> WorkflowRunBlock:
|
) -> WorkflowRunBlock:
|
||||||
async with self.Session() as session:
|
async with self.Session() as session:
|
||||||
workflow_run_block = (
|
workflow_run_block = (
|
||||||
@@ -3348,6 +3352,13 @@ class AgentDB:
|
|||||||
workflow_run_block.http_request_follow_redirects = http_request_follow_redirects
|
workflow_run_block.http_request_follow_redirects = http_request_follow_redirects
|
||||||
if ai_fallback_triggered is not None:
|
if ai_fallback_triggered is not None:
|
||||||
workflow_run_block.script_run = {"ai_fallback_triggered": ai_fallback_triggered}
|
workflow_run_block.script_run = {"ai_fallback_triggered": ai_fallback_triggered}
|
||||||
|
# human interaction block fields
|
||||||
|
if instructions:
|
||||||
|
workflow_run_block.instructions = instructions
|
||||||
|
if positive_descriptor:
|
||||||
|
workflow_run_block.positive_descriptor = positive_descriptor
|
||||||
|
if negative_descriptor:
|
||||||
|
workflow_run_block.negative_descriptor = negative_descriptor
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(workflow_run_block)
|
await session.refresh(workflow_run_block)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -680,6 +680,11 @@ class WorkflowRunBlockModel(Base):
|
|||||||
http_request_timeout = Column(Integer, nullable=True)
|
http_request_timeout = Column(Integer, nullable=True)
|
||||||
http_request_follow_redirects = Column(Boolean, nullable=True)
|
http_request_follow_redirects = Column(Boolean, nullable=True)
|
||||||
|
|
||||||
|
# human interaction block
|
||||||
|
instructions = Column(String, nullable=True)
|
||||||
|
positive_descriptor = Column(String, nullable=True)
|
||||||
|
negative_descriptor = 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)
|
||||||
|
|
||||||
|
|||||||
@@ -523,6 +523,9 @@ def convert_to_workflow_run_block(
|
|||||||
body=workflow_run_block_model.body,
|
body=workflow_run_block_model.body,
|
||||||
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,
|
||||||
|
instructions=workflow_run_block_model.instructions,
|
||||||
|
positive_descriptor=workflow_run_block_model.positive_descriptor,
|
||||||
|
negative_descriptor=workflow_run_block_model.negative_descriptor,
|
||||||
)
|
)
|
||||||
if task:
|
if task:
|
||||||
if task.finished_at and task.started_at:
|
if task.finished_at and task.started_at:
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ class WorkflowRunBlock(BaseModel):
|
|||||||
subject: str | None = None
|
subject: str | None = None
|
||||||
body: str | None = None
|
body: str | None = None
|
||||||
|
|
||||||
|
# human interaction block
|
||||||
|
instructions: str | None = None
|
||||||
|
positive_descriptor: str | None = None
|
||||||
|
negative_descriptor: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class WorkflowRunTimelineType(StrEnum):
|
class WorkflowRunTimelineType(StrEnum):
|
||||||
thought = "thought"
|
thought = "thought"
|
||||||
|
|||||||
@@ -3099,6 +3099,14 @@ class HumanInteractionBlock(BaseTaskBlock):
|
|||||||
|
|
||||||
self.recipients = formatted
|
self.recipients = formatted
|
||||||
|
|
||||||
|
self.negative_descriptor = self.format_block_parameter_template_from_workflow_run_context(
|
||||||
|
self.negative_descriptor, workflow_run_context
|
||||||
|
)
|
||||||
|
|
||||||
|
self.positive_descriptor = self.format_block_parameter_template_from_workflow_run_context(
|
||||||
|
self.positive_descriptor, workflow_run_context
|
||||||
|
)
|
||||||
|
|
||||||
async def execute(
|
async def execute(
|
||||||
self,
|
self,
|
||||||
workflow_run_id: str,
|
workflow_run_id: str,
|
||||||
@@ -3110,6 +3118,31 @@ class HumanInteractionBlock(BaseTaskBlock):
|
|||||||
# avoid circular import
|
# avoid circular import
|
||||||
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus # noqa: PLC0415
|
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus # noqa: PLC0415
|
||||||
|
|
||||||
|
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.format_potential_template_parameters(workflow_run_context)
|
||||||
|
except Exception as e:
|
||||||
|
return await self.build_block_result(
|
||||||
|
success=False,
|
||||||
|
failure_reason=f"Failed to format jinja template: {str(e)}",
|
||||||
|
output_parameter_value=None,
|
||||||
|
status=BlockStatus.failed,
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
await app.DATABASE.update_workflow_run_block(
|
||||||
|
workflow_run_block_id=workflow_run_block_id,
|
||||||
|
organization_id=organization_id,
|
||||||
|
recipients=self.recipients,
|
||||||
|
subject=self.subject,
|
||||||
|
body=self.body,
|
||||||
|
instructions=self.instructions,
|
||||||
|
positive_descriptor=self.positive_descriptor,
|
||||||
|
negative_descriptor=self.negative_descriptor,
|
||||||
|
)
|
||||||
|
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Pausing workflow for human interaction",
|
"Pausing workflow for human interaction",
|
||||||
workflow_run_id=workflow_run_id,
|
workflow_run_id=workflow_run_id,
|
||||||
@@ -3138,20 +3171,6 @@ class HumanInteractionBlock(BaseTaskBlock):
|
|||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
workflow_run_context = self.get_workflow_run_context(workflow_run_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.format_potential_template_parameters(workflow_run_context)
|
|
||||||
except Exception as e:
|
|
||||||
return await self.build_block_result(
|
|
||||||
success=False,
|
|
||||||
failure_reason=f"Failed to format jinja template: {str(e)}",
|
|
||||||
output_parameter_value=None,
|
|
||||||
status=BlockStatus.failed,
|
|
||||||
workflow_run_block_id=workflow_run_block_id,
|
|
||||||
organization_id=organization_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
workflow_permanent_id = workflow_run.workflow_permanent_id
|
workflow_permanent_id = workflow_run.workflow_permanent_id
|
||||||
app_url = f"{settings.SKYVERN_APP_URL}/workflows/{workflow_permanent_id}/{workflow_run_id}/overview"
|
app_url = f"{settings.SKYVERN_APP_URL}/workflows/{workflow_permanent_id}/{workflow_run_id}/overview"
|
||||||
body = f"{self.body}\n\nKindly visit {app_url}\n\n{self.instructions}\n\n"
|
body = f"{self.body}\n\nKindly visit {app_url}\n\n{self.instructions}\n\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user