Backend: unified /runs URL (#3898)
This commit is contained in:
@@ -1494,6 +1494,34 @@ class AgentDB:
|
||||
LOG.error("SQLAlchemyError", exc_info=True)
|
||||
raise
|
||||
|
||||
async def get_workflow_for_workflow_run(
|
||||
self,
|
||||
workflow_run_id: str,
|
||||
organization_id: str | None = None,
|
||||
exclude_deleted: bool = True,
|
||||
) -> Workflow | None:
|
||||
try:
|
||||
get_workflow_query = select(WorkflowModel)
|
||||
|
||||
if exclude_deleted:
|
||||
get_workflow_query = get_workflow_query.filter(WorkflowModel.deleted_at.is_(None))
|
||||
if organization_id:
|
||||
get_workflow_query = get_workflow_query.filter_by(organization_id=organization_id)
|
||||
|
||||
get_workflow_query = get_workflow_query.join(
|
||||
WorkflowRunModel,
|
||||
WorkflowRunModel.workflow_id == WorkflowModel.workflow_id,
|
||||
)
|
||||
|
||||
get_workflow_query = get_workflow_query.filter(WorkflowRunModel.workflow_run_id == workflow_run_id)
|
||||
async with self.Session() as session:
|
||||
if workflow := (await session.scalars(get_workflow_query)).first():
|
||||
return convert_to_workflow(workflow, self.debug_enabled)
|
||||
return None
|
||||
except SQLAlchemyError:
|
||||
LOG.error("SQLAlchemyError", exc_info=True)
|
||||
raise
|
||||
|
||||
async def get_workflow_versions_by_permanent_id(
|
||||
self,
|
||||
workflow_permanent_id: str,
|
||||
|
||||
@@ -81,6 +81,7 @@ from skyvern.forge.sdk.workflow.models.workflow import (
|
||||
WorkflowRun,
|
||||
WorkflowRunResponseBase,
|
||||
WorkflowRunStatus,
|
||||
WorkflowRunWithWorkflowResponse,
|
||||
)
|
||||
from skyvern.schemas.artifacts import EntityType, entity_type_to_param
|
||||
from skyvern.schemas.runs import (
|
||||
@@ -203,7 +204,7 @@ async def run_task(
|
||||
failure_reason=task_v1_response.failure_reason,
|
||||
created_at=task_v1_response.created_at,
|
||||
modified_at=task_v1_response.modified_at,
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/tasks/{task_v1_response.task_id}",
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{task_v1_response.task_id}",
|
||||
run_request=TaskRunRequest(
|
||||
engine=run_request.engine,
|
||||
prompt=task_v1_response.navigation_goal,
|
||||
@@ -266,7 +267,7 @@ async def run_task(
|
||||
failure_reason=None,
|
||||
created_at=task_v2.created_at,
|
||||
modified_at=task_v2.modified_at,
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/workflows/{task_v2.workflow_permanent_id}/{task_v2.workflow_run_id}",
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{task_v2.workflow_run_id}",
|
||||
run_request=TaskRunRequest(
|
||||
engine=RunEngine.skyvern_v2,
|
||||
prompt=task_v2.prompt,
|
||||
@@ -367,7 +368,7 @@ async def run_workflow(
|
||||
run_request=workflow_run_request,
|
||||
downloaded_files=None,
|
||||
recording_url=None,
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/workflows/{workflow_run.workflow_permanent_id}/{workflow_run.workflow_run_id}",
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{workflow_run.workflow_run_id}",
|
||||
run_with=workflow_run.run_with,
|
||||
ai_fallback=workflow_run.ai_fallback,
|
||||
)
|
||||
@@ -1118,7 +1119,7 @@ async def run_block(
|
||||
run_request=block_run_request,
|
||||
downloaded_files=None,
|
||||
recording_url=None,
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/workflows/{workflow_run.workflow_permanent_id}/{workflow_run.workflow_run_id}",
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{workflow_run.workflow_run_id}",
|
||||
)
|
||||
|
||||
|
||||
@@ -1808,6 +1809,42 @@ async def get_workflow_run_with_workflow_id(
|
||||
return return_dict
|
||||
|
||||
|
||||
@base_router.get(
|
||||
"/workflows/runs/{workflow_run_id}",
|
||||
include_in_schema=False,
|
||||
)
|
||||
@base_router.get(
|
||||
"/workflows/runs/{workflow_run_id}/",
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def get_workflow_and_run_from_workflow_run_id(
|
||||
workflow_run_id: str,
|
||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||
) -> WorkflowRunWithWorkflowResponse:
|
||||
workflow = await app.WORKFLOW_SERVICE.get_workflow_by_workflow_run_id(
|
||||
workflow_run_id=workflow_run_id,
|
||||
organization_id=current_org.organization_id,
|
||||
)
|
||||
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow run not found {workflow_run_id}",
|
||||
)
|
||||
|
||||
workflow_run_status_api_response = await get_workflow_run_with_workflow_id(
|
||||
workflow_id=workflow.workflow_permanent_id,
|
||||
workflow_run_id=workflow_run_id,
|
||||
current_org=current_org,
|
||||
)
|
||||
|
||||
workflow_run_status_api_response["workflow"] = workflow
|
||||
|
||||
response = WorkflowRunWithWorkflowResponse.model_validate(workflow_run_status_api_response)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@legacy_base_router.get(
|
||||
"/workflows/{workflow_id}/runs/{workflow_run_id}/timeline",
|
||||
tags=["agent"],
|
||||
|
||||
@@ -238,6 +238,6 @@ async def login(
|
||||
browser_session_id=login_request.browser_session_id,
|
||||
max_screenshot_scrolls=login_request.max_screenshot_scrolling_times,
|
||||
),
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/workflows/{workflow_run.workflow_permanent_id}/{workflow_run.workflow_run_id}",
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{workflow_run.workflow_run_id}",
|
||||
browser_session_id=login_request.browser_session_id,
|
||||
)
|
||||
|
||||
@@ -3187,8 +3187,7 @@ class HumanInteractionBlock(BaseTaskBlock):
|
||||
organization_id=organization_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}/runs/{workflow_run_id}/overview"
|
||||
body = f"{self.body}\n\nKindly visit {app_url}\n\n{self.instructions}\n\n"
|
||||
subject = f"{self.subject} - Workflow Run ID: {workflow_run_id}"
|
||||
|
||||
|
||||
@@ -201,3 +201,7 @@ class WorkflowRunResponseBase(BaseModel):
|
||||
browser_address: str | None = None
|
||||
script_run: ScriptRunResponse | None = None
|
||||
errors: list[dict[str, Any]] | None = None
|
||||
|
||||
|
||||
class WorkflowRunWithWorkflowResponse(WorkflowRunResponseBase):
|
||||
workflow: Workflow
|
||||
|
||||
@@ -26,6 +26,7 @@ from skyvern.exceptions import (
|
||||
ScriptTerminationException,
|
||||
SkyvernException,
|
||||
WorkflowNotFound,
|
||||
WorkflowNotFoundForWorkflowRun,
|
||||
WorkflowRunNotFound,
|
||||
)
|
||||
from skyvern.forge import app
|
||||
@@ -1123,6 +1124,23 @@ class WorkflowService:
|
||||
)
|
||||
return workflows
|
||||
|
||||
async def get_workflow_by_workflow_run_id(
|
||||
self,
|
||||
workflow_run_id: str,
|
||||
organization_id: str | None = None,
|
||||
exclude_deleted: bool = True,
|
||||
) -> Workflow:
|
||||
workflow = await app.DATABASE.get_workflow_for_workflow_run(
|
||||
workflow_run_id,
|
||||
organization_id=organization_id,
|
||||
exclude_deleted=exclude_deleted,
|
||||
)
|
||||
|
||||
if not workflow:
|
||||
raise WorkflowNotFoundForWorkflowRun(workflow_run_id=workflow_run_id)
|
||||
|
||||
return workflow
|
||||
|
||||
async def get_block_outputs_for_debug_session(
|
||||
self,
|
||||
workflow_permanent_id: str,
|
||||
@@ -2049,10 +2067,8 @@ class WorkflowService:
|
||||
return
|
||||
|
||||
# build new schema for backward compatible webhook payload
|
||||
app_url = (
|
||||
f"{settings.SKYVERN_APP_URL.rstrip('/')}/workflows/"
|
||||
f"{workflow_run.workflow_permanent_id}/{workflow_run.workflow_run_id}"
|
||||
)
|
||||
app_url = f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{workflow_run.workflow_run_id}"
|
||||
|
||||
workflow_run_response = WorkflowRunResponse(
|
||||
run_id=workflow_run.workflow_run_id,
|
||||
run_type=RunType.workflow_run,
|
||||
|
||||
Reference in New Issue
Block a user