214 lines
7.6 KiB
Python
214 lines
7.6 KiB
Python
from typing import Any
|
|
|
|
import structlog
|
|
from fastapi import Depends, HTTPException, status
|
|
|
|
from skyvern.core.script_generations.real_skyvern_page_ai import RealSkyvernPageAi
|
|
from skyvern.core.script_generations.script_skyvern_page import ScriptSkyvernPage
|
|
from skyvern.forge import app
|
|
from skyvern.forge.sdk.api.files import validate_download_url
|
|
from skyvern.forge.sdk.core import skyvern_context
|
|
from skyvern.forge.sdk.core.skyvern_context import SkyvernContext
|
|
from skyvern.forge.sdk.routes.routers import base_router
|
|
from skyvern.forge.sdk.schemas.organizations import Organization
|
|
from skyvern.forge.sdk.schemas.sdk_actions import (
|
|
RunSdkActionRequest,
|
|
RunSdkActionResponse,
|
|
)
|
|
from skyvern.forge.sdk.services import org_auth_service
|
|
from skyvern.forge.sdk.workflow.models.workflow import (
|
|
WorkflowRequestBody,
|
|
WorkflowRunStatus,
|
|
)
|
|
from skyvern.schemas.workflows import BlockType, WorkflowStatus
|
|
|
|
LOG = structlog.get_logger()
|
|
|
|
|
|
@base_router.post(
|
|
"/sdk/run_action",
|
|
response_model=RunSdkActionResponse,
|
|
summary="Run an SDK action",
|
|
description="Execute a single SDK action with the specified parameters",
|
|
tags=["SDK"],
|
|
openapi_extra={
|
|
"x-fern-sdk-method-name": "run_sdk_action",
|
|
},
|
|
)
|
|
@base_router.post("/sdk/run_action/", include_in_schema=False)
|
|
async def run_sdk_action(
|
|
action_request: RunSdkActionRequest,
|
|
organization: Organization = Depends(org_auth_service.get_current_org),
|
|
) -> RunSdkActionResponse:
|
|
"""Execute a single SDK action with the specified parameters."""
|
|
LOG.info(
|
|
"Running SDK action",
|
|
organization_id=organization.organization_id,
|
|
action_type=action_request.action.type,
|
|
)
|
|
|
|
organization_id = organization.organization_id
|
|
browser_session_id = action_request.browser_session_id
|
|
browser_address = action_request.browser_address
|
|
action = action_request.action
|
|
|
|
# Use existing workflow_run_id if provided, otherwise create a new one
|
|
if action_request.workflow_run_id:
|
|
workflow_run = await app.DATABASE.get_workflow_run(
|
|
workflow_run_id=action_request.workflow_run_id,
|
|
organization_id=organization_id,
|
|
)
|
|
if not workflow_run:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Workflow run {action_request.workflow_run_id} not found",
|
|
)
|
|
workflow = await app.DATABASE.get_workflow(
|
|
workflow_id=workflow_run.workflow_id,
|
|
organization_id=organization_id,
|
|
)
|
|
if not workflow:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Workflow {workflow_run.workflow_id} not found",
|
|
)
|
|
else:
|
|
workflow = await app.WORKFLOW_SERVICE.create_empty_workflow(
|
|
organization,
|
|
title="SDK Workflow",
|
|
status=WorkflowStatus.auto_generated,
|
|
)
|
|
workflow_run = await app.WORKFLOW_SERVICE.setup_workflow_run(
|
|
request_id=None,
|
|
workflow_request=WorkflowRequestBody(
|
|
browser_session_id=browser_session_id,
|
|
browser_address=browser_address,
|
|
),
|
|
workflow_permanent_id=workflow.workflow_permanent_id,
|
|
organization=organization,
|
|
version=None,
|
|
)
|
|
workflow_run = await app.DATABASE.update_workflow_run(
|
|
workflow_run_id=workflow_run.workflow_run_id,
|
|
status=WorkflowRunStatus.completed,
|
|
)
|
|
|
|
task = await app.DATABASE.create_task(
|
|
organization_id=organization_id,
|
|
url=action_request.url,
|
|
navigation_goal=action.get_navigation_goal(),
|
|
navigation_payload=action.get_navigation_payload(),
|
|
data_extraction_goal=None,
|
|
title=f"SDK Action Task: {action_request.action.type}",
|
|
workflow_run_id=workflow_run.workflow_run_id,
|
|
browser_session_id=browser_session_id,
|
|
browser_address=browser_address,
|
|
)
|
|
|
|
step = await app.DATABASE.create_step(
|
|
task.task_id,
|
|
order=0,
|
|
retry_index=0,
|
|
organization_id=organization.organization_id,
|
|
)
|
|
|
|
await app.DATABASE.create_workflow_run_block(
|
|
workflow_run_id=workflow_run.workflow_run_id,
|
|
organization_id=organization_id,
|
|
block_type=BlockType.ACTION,
|
|
task_id=task.task_id,
|
|
)
|
|
|
|
await app.WORKFLOW_CONTEXT_MANAGER.initialize_workflow_run_context(
|
|
organization,
|
|
workflow_run.workflow_run_id,
|
|
workflow.title,
|
|
workflow.workflow_id,
|
|
workflow.workflow_permanent_id,
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
)
|
|
|
|
context = skyvern_context.ensure_context()
|
|
skyvern_context.set(
|
|
SkyvernContext(
|
|
request_id=context.request_id,
|
|
organization_id=task.organization_id,
|
|
task_id=task.task_id,
|
|
step_id=step.step_id,
|
|
browser_session_id=browser_session_id,
|
|
max_screenshot_scrolls=task.max_screenshot_scrolls,
|
|
workflow_id=workflow.workflow_id,
|
|
workflow_run_id=workflow_run.workflow_run_id,
|
|
)
|
|
)
|
|
result: Any | None = None
|
|
try:
|
|
scraped_page = await ScriptSkyvernPage.create_scraped_page(browser_session_id=browser_session_id)
|
|
page = await scraped_page._browser_state.must_get_working_page()
|
|
page_ai = RealSkyvernPageAi(scraped_page, page)
|
|
|
|
if action.type == "ai_click":
|
|
result = await page_ai.ai_click(
|
|
selector=action.selector,
|
|
intention=action.intention,
|
|
data=action.data,
|
|
timeout=action.timeout,
|
|
)
|
|
elif action.type == "ai_input_text":
|
|
result = await page_ai.ai_input_text(
|
|
selector=action.selector,
|
|
value=action.value,
|
|
intention=action.intention,
|
|
data=action.data,
|
|
totp_identifier=action.totp_identifier,
|
|
totp_url=action.totp_url,
|
|
timeout=action.timeout,
|
|
)
|
|
elif action.type == "ai_select_option":
|
|
result = await page_ai.ai_select_option(
|
|
selector=action.selector,
|
|
value=action.value,
|
|
intention=action.intention,
|
|
data=action.data,
|
|
timeout=action.timeout,
|
|
)
|
|
elif action.type == "ai_upload_file":
|
|
if action.file_url and not validate_download_url(action.file_url):
|
|
raise HTTPException(status_code=400, detail="Unsupported file url")
|
|
result = await page_ai.ai_upload_file(
|
|
selector=action.selector,
|
|
files=action.file_url,
|
|
intention=action.intention,
|
|
data=action.data,
|
|
timeout=action.timeout,
|
|
)
|
|
elif action.type == "ai_act":
|
|
await page_ai.ai_act(
|
|
prompt=action.intention,
|
|
)
|
|
result = None
|
|
elif action.type == "extract":
|
|
extract_result = await page_ai.ai_extract(
|
|
prompt=action.prompt,
|
|
schema=action.extract_schema,
|
|
error_code_mapping=action.error_code_mapping,
|
|
intention=action.intention,
|
|
data=action.data,
|
|
)
|
|
result = extract_result
|
|
elif action.type == "locate_element":
|
|
xpath_result = await page_ai.ai_locate_element(
|
|
prompt=action.prompt,
|
|
)
|
|
result = xpath_result
|
|
finally:
|
|
skyvern_context.reset()
|
|
|
|
return RunSdkActionResponse(
|
|
workflow_run_id=workflow_run.workflow_run_id,
|
|
result=result,
|
|
)
|