SDK: download files (#4196)
This commit is contained in:
committed by
GitHub
parent
b30f3b09c8
commit
4665f8907d
@@ -160,6 +160,24 @@ await skyvern.login({
|
||||
onepassword_item_id: "1PASSWORD ITEM ID"
|
||||
});
|
||||
"""
|
||||
FILE_DOWNLOAD_CODE_SAMPLE_PYTHON = """from skyvern import Skyvern
|
||||
|
||||
skyvern = Skyvern(api_key="YOUR_API_KEY")
|
||||
await skyvern.file_download(
|
||||
url="https://example.com/downloads",
|
||||
navigation_goal="Navigate to the downloads page and click the 'Download PDF' button",
|
||||
download_suffix="report.pdf"
|
||||
)
|
||||
"""
|
||||
FILE_DOWNLOAD_CODE_SAMPLE_TS = """import { SkyvernClient } from "@skyvern/client";
|
||||
|
||||
const skyvern = new SkyvernClient({ apiKey: "YOUR_API_KEY" });
|
||||
await skyvern.fileDownload({
|
||||
url: "https://example.com/downloads",
|
||||
navigation_goal: "Navigate to the downloads page and click the 'Download PDF' button",
|
||||
download_suffix: "report.pdf"
|
||||
});
|
||||
"""
|
||||
|
||||
# Workflows
|
||||
CREATE_WORKFLOW_CODE_SAMPLE_CURL = """curl -X POST https://api.skyvern.com/v1/workflows \
|
||||
|
||||
@@ -8,6 +8,8 @@ from skyvern.exceptions import MissingBrowserAddressError
|
||||
from skyvern.forge import app
|
||||
from skyvern.forge.sdk.core import skyvern_context
|
||||
from skyvern.forge.sdk.routes.code_samples import (
|
||||
FILE_DOWNLOAD_CODE_SAMPLE_PYTHON,
|
||||
FILE_DOWNLOAD_CODE_SAMPLE_TS,
|
||||
LOGIN_CODE_SAMPLE_BITWARDEN_PYTHON,
|
||||
LOGIN_CODE_SAMPLE_BITWARDEN_TS,
|
||||
LOGIN_CODE_SAMPLE_ONEPASSWORD_PYTHON,
|
||||
@@ -19,12 +21,13 @@ from skyvern.forge.sdk.routes.routers import base_router
|
||||
from skyvern.forge.sdk.schemas.organizations import Organization
|
||||
from skyvern.forge.sdk.services import org_auth_service
|
||||
from skyvern.forge.sdk.workflow.models.parameter import WorkflowParameterType
|
||||
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRequestBody
|
||||
from skyvern.schemas.run_blocks import CredentialType, LoginRequest
|
||||
from skyvern.forge.sdk.workflow.models.workflow import Workflow, WorkflowRequestBody
|
||||
from skyvern.schemas.run_blocks import BaseRunBlockRequest, CredentialType, FileDownloadRequest, LoginRequest
|
||||
from skyvern.schemas.runs import ProxyLocation, RunType, WorkflowRunRequest, WorkflowRunResponse
|
||||
from skyvern.schemas.workflows import (
|
||||
AzureVaultCredentialParameterYAML,
|
||||
BitwardenLoginCredentialParameterYAML,
|
||||
FileDownloadBlockYAML,
|
||||
LoginBlockYAML,
|
||||
OnePasswordCredentialParameterYAML,
|
||||
WorkflowCreateYAMLRequest,
|
||||
@@ -43,6 +46,80 @@ If you fail to login to find the login page or can't login after several trials,
|
||||
If login is completed, you're successful."""
|
||||
|
||||
|
||||
def _validate_url(url: str | None) -> str | None:
|
||||
if not url:
|
||||
return None
|
||||
try:
|
||||
return prepend_scheme_and_validate_url(url)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
|
||||
async def _run_workflow_and_build_response(
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
new_workflow: Workflow,
|
||||
workflow_id: str,
|
||||
organization: Organization,
|
||||
run_block_request: BaseRunBlockRequest,
|
||||
webhook_url: str | None,
|
||||
totp_verification_url: str | None,
|
||||
totp_identifier: str | None,
|
||||
x_api_key: str | None,
|
||||
) -> WorkflowRunResponse:
|
||||
context = skyvern_context.ensure_context()
|
||||
request_id = context.request_id
|
||||
legacy_workflow_request = WorkflowRequestBody(
|
||||
proxy_location=run_block_request.proxy_location,
|
||||
webhook_callback_url=webhook_url,
|
||||
totp_identifier=totp_identifier,
|
||||
totp_verification_url=totp_verification_url,
|
||||
browser_session_id=run_block_request.browser_session_id,
|
||||
browser_profile_id=run_block_request.browser_profile_id,
|
||||
browser_address=run_block_request.browser_address,
|
||||
max_screenshot_scrolls=run_block_request.max_screenshot_scrolling_times,
|
||||
extra_http_headers=run_block_request.extra_http_headers,
|
||||
)
|
||||
|
||||
try:
|
||||
workflow_run = await workflow_service.run_workflow(
|
||||
workflow_id=workflow_id,
|
||||
organization=organization,
|
||||
workflow_request=legacy_workflow_request,
|
||||
template=False,
|
||||
version=None,
|
||||
api_key=x_api_key or None,
|
||||
request_id=request_id,
|
||||
request=request,
|
||||
background_tasks=background_tasks,
|
||||
)
|
||||
except MissingBrowserAddressError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
return WorkflowRunResponse(
|
||||
run_id=workflow_run.workflow_run_id,
|
||||
run_type=RunType.workflow_run,
|
||||
status=str(workflow_run.status),
|
||||
failure_reason=workflow_run.failure_reason,
|
||||
created_at=workflow_run.created_at,
|
||||
modified_at=workflow_run.modified_at,
|
||||
run_request=WorkflowRunRequest(
|
||||
workflow_id=new_workflow.workflow_id,
|
||||
title=new_workflow.title,
|
||||
proxy_location=run_block_request.proxy_location,
|
||||
webhook_url=webhook_url,
|
||||
totp_url=totp_verification_url,
|
||||
totp_identifier=totp_identifier,
|
||||
browser_session_id=run_block_request.browser_session_id,
|
||||
browser_profile_id=run_block_request.browser_profile_id,
|
||||
max_screenshot_scrolls=run_block_request.max_screenshot_scrolling_times,
|
||||
),
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{workflow_run.workflow_run_id}",
|
||||
browser_session_id=run_block_request.browser_session_id,
|
||||
browser_profile_id=run_block_request.browser_profile_id,
|
||||
)
|
||||
|
||||
|
||||
@base_router.post(
|
||||
"/run/tasks/login",
|
||||
tags=["Agent"],
|
||||
@@ -72,14 +149,9 @@ async def login(
|
||||
organization: Organization = Depends(org_auth_service.get_current_org),
|
||||
x_api_key: Annotated[str | None, Header()] = None,
|
||||
) -> WorkflowRunResponse:
|
||||
try:
|
||||
url = prepend_scheme_and_validate_url(login_request.url) if login_request.url else None
|
||||
totp_verification_url = (
|
||||
prepend_scheme_and_validate_url(login_request.totp_url) if login_request.totp_url else None
|
||||
)
|
||||
webhook_url = prepend_scheme_and_validate_url(login_request.webhook_url) if login_request.webhook_url else None
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
url = _validate_url(login_request.url)
|
||||
totp_verification_url = _validate_url(login_request.totp_url)
|
||||
webhook_url = _validate_url(login_request.webhook_url)
|
||||
|
||||
# 1. create empty workflow with a credential parameter
|
||||
new_workflow = await app.WORKFLOW_SERVICE.create_empty_workflow(
|
||||
@@ -196,54 +268,104 @@ async def login(
|
||||
|
||||
# 3. create and run workflow with the credential
|
||||
workflow_id = new_workflow.workflow_permanent_id
|
||||
context = skyvern_context.ensure_context()
|
||||
request_id = context.request_id
|
||||
legacy_workflow_request = WorkflowRequestBody(
|
||||
proxy_location=login_request.proxy_location,
|
||||
webhook_callback_url=webhook_url,
|
||||
totp_identifier=resolved_totp_identifier,
|
||||
return await _run_workflow_and_build_response(
|
||||
request=request,
|
||||
background_tasks=background_tasks,
|
||||
new_workflow=new_workflow,
|
||||
workflow_id=workflow_id,
|
||||
organization=organization,
|
||||
run_block_request=login_request,
|
||||
webhook_url=webhook_url,
|
||||
totp_verification_url=totp_verification_url,
|
||||
browser_session_id=login_request.browser_session_id,
|
||||
browser_profile_id=login_request.browser_profile_id,
|
||||
browser_address=login_request.browser_address,
|
||||
max_screenshot_scrolls=login_request.max_screenshot_scrolling_times,
|
||||
extra_http_headers=login_request.extra_http_headers,
|
||||
totp_identifier=resolved_totp_identifier,
|
||||
x_api_key=x_api_key,
|
||||
)
|
||||
|
||||
try:
|
||||
workflow_run = await workflow_service.run_workflow(
|
||||
workflow_id=workflow_id,
|
||||
organization=organization,
|
||||
workflow_request=legacy_workflow_request,
|
||||
template=False,
|
||||
version=None,
|
||||
api_key=x_api_key or None,
|
||||
request_id=request_id,
|
||||
request=request,
|
||||
background_tasks=background_tasks,
|
||||
)
|
||||
except MissingBrowserAddressError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
return WorkflowRunResponse(
|
||||
run_id=workflow_run.workflow_run_id,
|
||||
run_type=RunType.workflow_run,
|
||||
status=str(workflow_run.status),
|
||||
failure_reason=workflow_run.failure_reason,
|
||||
created_at=workflow_run.created_at,
|
||||
modified_at=workflow_run.modified_at,
|
||||
run_request=WorkflowRunRequest(
|
||||
workflow_id=new_workflow.workflow_id,
|
||||
title=new_workflow.title,
|
||||
proxy_location=login_request.proxy_location,
|
||||
webhook_url=webhook_url,
|
||||
totp_url=totp_verification_url,
|
||||
totp_identifier=resolved_totp_identifier,
|
||||
browser_session_id=login_request.browser_session_id,
|
||||
browser_profile_id=login_request.browser_profile_id,
|
||||
max_screenshot_scrolls=login_request.max_screenshot_scrolling_times,
|
||||
),
|
||||
app_url=f"{settings.SKYVERN_APP_URL.rstrip('/')}/runs/{workflow_run.workflow_run_id}",
|
||||
browser_session_id=login_request.browser_session_id,
|
||||
browser_profile_id=login_request.browser_profile_id,
|
||||
@base_router.post(
|
||||
"/run/tasks/file_download",
|
||||
tags=["Agent"],
|
||||
response_model=WorkflowRunResponse,
|
||||
openapi_extra={
|
||||
"x-fern-sdk-method-name": "file_download",
|
||||
"x-fern-examples": [
|
||||
{
|
||||
"code-samples": [
|
||||
{"sdk": "python", "code": FILE_DOWNLOAD_CODE_SAMPLE_PYTHON},
|
||||
{"sdk": "typescript", "code": FILE_DOWNLOAD_CODE_SAMPLE_TS},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
description="Download a file from a website by navigating and clicking download buttons",
|
||||
summary="File Download Task",
|
||||
)
|
||||
async def file_download(
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
file_download_request: FileDownloadRequest,
|
||||
organization: Organization = Depends(org_auth_service.get_current_org),
|
||||
x_api_key: Annotated[str | None, Header()] = None,
|
||||
) -> WorkflowRunResponse:
|
||||
url = _validate_url(file_download_request.url)
|
||||
totp_verification_url = _validate_url(file_download_request.totp_url)
|
||||
webhook_url = _validate_url(file_download_request.webhook_url)
|
||||
|
||||
# 1. create empty workflow
|
||||
new_workflow = await app.WORKFLOW_SERVICE.create_empty_workflow(
|
||||
organization,
|
||||
"File Download",
|
||||
proxy_location=file_download_request.proxy_location,
|
||||
max_screenshot_scrolling_times=file_download_request.max_screenshot_scrolling_times,
|
||||
extra_http_headers=file_download_request.extra_http_headers,
|
||||
status=WorkflowStatus.auto_generated,
|
||||
)
|
||||
|
||||
# 2. add a file download block to the workflow
|
||||
label = "file_download"
|
||||
file_download_block_yaml = FileDownloadBlockYAML(
|
||||
label=label,
|
||||
title=label,
|
||||
url=url,
|
||||
navigation_goal=file_download_request.navigation_goal,
|
||||
max_steps_per_run=file_download_request.max_steps_per_run or 10,
|
||||
parameter_keys=file_download_request.parameter_keys or [],
|
||||
totp_verification_url=totp_verification_url,
|
||||
totp_identifier=file_download_request.totp_identifier,
|
||||
download_suffix=file_download_request.download_suffix,
|
||||
download_timeout=file_download_request.download_timeout,
|
||||
)
|
||||
yaml_blocks = [file_download_block_yaml]
|
||||
workflow_definition_yaml = WorkflowDefinitionYAML(
|
||||
parameters=[],
|
||||
blocks=yaml_blocks,
|
||||
)
|
||||
workflow_create_request = WorkflowCreateYAMLRequest(
|
||||
title=new_workflow.title,
|
||||
description=new_workflow.description,
|
||||
proxy_location=file_download_request.proxy_location or ProxyLocation.RESIDENTIAL,
|
||||
workflow_definition=workflow_definition_yaml,
|
||||
status=new_workflow.status,
|
||||
max_screenshot_scrolls=file_download_request.max_screenshot_scrolling_times,
|
||||
)
|
||||
workflow = await app.WORKFLOW_SERVICE.create_workflow_from_request(
|
||||
organization=organization,
|
||||
request=workflow_create_request,
|
||||
workflow_permanent_id=new_workflow.workflow_permanent_id,
|
||||
)
|
||||
LOG.info("Workflow created", workflow_id=workflow.workflow_id)
|
||||
|
||||
# 3. create and run workflow
|
||||
workflow_id = new_workflow.workflow_permanent_id
|
||||
return await _run_workflow_and_build_response(
|
||||
request=request,
|
||||
background_tasks=background_tasks,
|
||||
new_workflow=new_workflow,
|
||||
workflow_id=workflow_id,
|
||||
organization=organization,
|
||||
run_block_request=file_download_request,
|
||||
webhook_url=webhook_url,
|
||||
totp_verification_url=totp_verification_url,
|
||||
totp_identifier=file_download_request.totp_identifier,
|
||||
x_api_key=x_api_key,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user