add recording_url and downloaded_files to the RunRepsonse for task v1 and task v2 (#2360)

This commit is contained in:
Shuchang Zheng
2025-05-16 00:03:10 -07:00
committed by GitHub
parent 455f560fa1
commit 09ed1c1dff
3 changed files with 63 additions and 51 deletions

View File

@@ -212,37 +212,7 @@ async def get_task_v1(
current_org: Organization = Depends(org_auth_service.get_current_org), current_org: Organization = Depends(org_auth_service.get_current_org),
) -> TaskResponse: ) -> TaskResponse:
analytics.capture("skyvern-oss-agent-task-get") analytics.capture("skyvern-oss-agent-task-get")
task_obj = await app.DATABASE.get_task(task_id, organization_id=current_org.organization_id) return await task_v1_service.get_task_v1_response(task_id=task_id, organization_id=current_org.organization_id)
if not task_obj:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Task not found {task_id}",
)
# get latest step
latest_step = await app.DATABASE.get_latest_step(task_id, organization_id=current_org.organization_id)
if not latest_step:
return await app.agent.build_task_response(task=task_obj)
failure_reason: str | None = None
if task_obj.status == TaskStatus.failed and (latest_step.output or task_obj.failure_reason):
failure_reason = ""
if task_obj.failure_reason:
failure_reason += task_obj.failure_reason or ""
if latest_step.output is not None and latest_step.output.actions_and_results is not None:
action_results_string: list[str] = []
for action, results in latest_step.output.actions_and_results:
if len(results) == 0:
continue
if results[-1].success:
continue
action_results_string.append(f"{action.action_type} action failed.")
if len(action_results_string) > 0:
failure_reason += "(Exceptions: " + str(action_results_string) + ")"
return await app.agent.build_task_response(
task=task_obj, last_step=latest_step, failure_reason=failure_reason, need_browser_log=True
)
@legacy_base_router.post( @legacy_base_router.post(

View File

@@ -6,7 +6,7 @@ from skyvern.forge import app
from skyvern.forge.sdk.schemas.tasks import TaskStatus from skyvern.forge.sdk.schemas.tasks import TaskStatus
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus
from skyvern.schemas.runs import RunEngine, RunResponse, RunType, TaskRunRequest, TaskRunResponse from skyvern.schemas.runs import RunEngine, RunResponse, RunType, TaskRunRequest, TaskRunResponse
from skyvern.services import task_v2_service, workflow_service from skyvern.services import task_v1_service, task_v2_service, workflow_service
async def get_run_response(run_id: str, organization_id: str | None = None) -> RunResponse | None: async def get_run_response(run_id: str, organization_id: str | None = None) -> RunResponse | None:
@@ -20,34 +20,40 @@ async def get_run_response(run_id: str, organization_id: str | None = None) -> R
or run.task_run_type == RunType.anthropic_cua or run.task_run_type == RunType.anthropic_cua
): ):
# fetch task v1 from db and transform to task run response # fetch task v1 from db and transform to task run response
task_v1 = await app.DATABASE.get_task(run.run_id, organization_id=organization_id) try:
if not task_v1: task_v1_response = await task_v1_service.get_task_v1_response(
task_id=run.run_id, organization_id=organization_id
)
except TaskNotFound:
return None return None
run_engine = RunEngine.skyvern_v1 run_engine = RunEngine.skyvern_v1
if run.task_run_type == RunType.openai_cua: if run.task_run_type == RunType.openai_cua:
run_engine = RunEngine.openai_cua run_engine = RunEngine.openai_cua
elif run.task_run_type == RunType.anthropic_cua: elif run.task_run_type == RunType.anthropic_cua:
run_engine = RunEngine.anthropic_cua run_engine = RunEngine.anthropic_cua
return TaskRunResponse( return TaskRunResponse(
run_id=run.run_id, run_id=run.run_id,
run_type=run.task_run_type, run_type=run.task_run_type,
status=str(task_v1.status), status=str(task_v1_response.status),
output=task_v1.extracted_information, output=task_v1_response.extracted_information,
failure_reason=task_v1.failure_reason, failure_reason=task_v1_response.failure_reason,
created_at=task_v1.created_at, created_at=task_v1_response.created_at,
modified_at=task_v1.modified_at, modified_at=task_v1_response.modified_at,
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/tasks/{task_v1.task_id}", app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/tasks/{task_v1_response.task_id}",
recording_url=task_v1_response.recording_url,
downloaded_files=task_v1_response.downloaded_files,
run_request=TaskRunRequest( run_request=TaskRunRequest(
engine=run_engine, engine=run_engine,
prompt=task_v1.navigation_goal, prompt=task_v1_response.request.navigation_goal,
url=task_v1.url, url=task_v1_response.request.url,
webhook_url=task_v1.webhook_callback_url, webhook_url=task_v1_response.request.webhook_callback_url,
totp_identifier=task_v1.totp_identifier, totp_identifier=task_v1_response.request.totp_identifier,
totp_url=task_v1.totp_verification_url, totp_url=task_v1_response.request.totp_verification_url,
proxy_location=task_v1.proxy_location, proxy_location=task_v1_response.request.proxy_location,
max_steps=task_v1.max_steps_per_run, max_steps=task_v1_response.max_steps_per_run,
data_extraction_schema=task_v1.extracted_information_schema, data_extraction_schema=task_v1_response.request.extracted_information_schema,
error_code_mapping=task_v1.error_code_mapping, error_code_mapping=task_v1_response.request.error_code_mapping,
), ),
) )
elif run.task_run_type == RunType.task_v2: elif run.task_run_type == RunType.task_v2:
@@ -56,7 +62,9 @@ async def get_run_response(run_id: str, organization_id: str | None = None) -> R
return None return None
workflow_run = None workflow_run = None
if task_v2.workflow_run_id: if task_v2.workflow_run_id:
workflow_run = await app.DATABASE.get_workflow_run(task_v2.workflow_run_id, organization_id=organization_id) workflow_run = await workflow_service.get_workflow_run_response(
task_v2.workflow_run_id, organization_id=organization_id
)
return TaskRunResponse( return TaskRunResponse(
run_id=run.run_id, run_id=run.run_id,
run_type=run.task_run_type, run_type=run.task_run_type,
@@ -65,6 +73,8 @@ async def get_run_response(run_id: str, organization_id: str | None = None) -> R
failure_reason=workflow_run.failure_reason if workflow_run else None, failure_reason=workflow_run.failure_reason if workflow_run else None,
created_at=task_v2.created_at, created_at=task_v2.created_at,
modified_at=task_v2.modified_at, modified_at=task_v2.modified_at,
recording_url=workflow_run.recording_url if workflow_run else None,
downloaded_files=workflow_run.downloaded_files if workflow_run else None,
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/{task_v2.workflow_permanent_id}/{task_v2.workflow_run_id}", app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/{task_v2.workflow_permanent_id}/{task_v2.workflow_run_id}",
run_request=TaskRunRequest( run_request=TaskRunRequest(
engine=RunEngine.skyvern_v2, engine=RunEngine.skyvern_v2,

View File

@@ -5,6 +5,7 @@ from fastapi import BackgroundTasks, HTTPException, Request
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from skyvern.config import settings from skyvern.config import settings
from skyvern.exceptions import TaskNotFound
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.prompts import prompt_engine from skyvern.forge.prompts import prompt_engine
from skyvern.forge.sdk.api.llm.exceptions import LLMProviderError from skyvern.forge.sdk.api.llm.exceptions import LLMProviderError
@@ -12,7 +13,7 @@ from skyvern.forge.sdk.core.hashing import generate_url_hash
from skyvern.forge.sdk.executor.factory import AsyncExecutorFactory from skyvern.forge.sdk.executor.factory import AsyncExecutorFactory
from skyvern.forge.sdk.schemas.organizations import Organization from skyvern.forge.sdk.schemas.organizations import Organization
from skyvern.forge.sdk.schemas.task_generations import TaskGeneration, TaskGenerationBase from skyvern.forge.sdk.schemas.task_generations import TaskGeneration, TaskGenerationBase
from skyvern.forge.sdk.schemas.tasks import Task, TaskRequest from skyvern.forge.sdk.schemas.tasks import Task, TaskRequest, TaskResponse, TaskStatus
from skyvern.schemas.runs import RunEngine, RunType from skyvern.schemas.runs import RunEngine, RunType
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -114,3 +115,34 @@ async def run_task(
api_key=x_api_key, api_key=x_api_key,
) )
return created_task return created_task
async def get_task_v1_response(task_id: str, organization_id: str | None = None) -> TaskResponse:
task_obj = await app.DATABASE.get_task(task_id, organization_id=organization_id)
if not task_obj:
raise TaskNotFound(task_id=task_id)
# get latest step
latest_step = await app.DATABASE.get_latest_step(task_id, organization_id=organization_id)
if not latest_step:
return await app.agent.build_task_response(task=task_obj)
failure_reason: str | None = None
if task_obj.status == TaskStatus.failed and (latest_step.output or task_obj.failure_reason):
failure_reason = ""
if task_obj.failure_reason:
failure_reason += task_obj.failure_reason or ""
if latest_step.output is not None and latest_step.output.actions_and_results is not None:
action_results_string: list[str] = []
for action, results in latest_step.output.actions_and_results:
if len(results) == 0:
continue
if results[-1].success:
continue
action_results_string.append(f"{action.action_type} action failed.")
if len(action_results_string) > 0:
failure_reason += "(Exceptions: " + str(action_results_string) + ")"
return await app.agent.build_task_response(
task=task_obj, last_step=latest_step, failure_reason=failure_reason, need_browser_log=True
)