new create_workflow and update_workflow endpoint that supports either json or yaml format (#2408)
This commit is contained in:
@@ -26,11 +26,13 @@ from skyvern.forge.sdk.models import Step
|
||||
from skyvern.forge.sdk.routes.code_samples import (
|
||||
CANCEL_RUN_CODE_SAMPLE,
|
||||
CREATE_WORKFLOW_CODE_SAMPLE,
|
||||
CREATE_WORKFLOW_CODE_SAMPLE_PYTHON,
|
||||
DELETE_WORKFLOW_CODE_SAMPLE,
|
||||
GET_RUN_CODE_SAMPLE,
|
||||
RUN_TASK_CODE_SAMPLE,
|
||||
RUN_WORKFLOW_CODE_SAMPLE,
|
||||
UPDATE_WORKFLOW_CODE_SAMPLE,
|
||||
UPDATE_WORKFLOW_CODE_SAMPLE_PYTHON,
|
||||
)
|
||||
from skyvern.forge.sdk.routes.routers import base_router, legacy_base_router, legacy_v2_router
|
||||
from skyvern.forge.sdk.schemas.ai_suggestions import AISuggestionBase, AISuggestionRequest
|
||||
@@ -80,6 +82,7 @@ from skyvern.schemas.runs import (
|
||||
WorkflowRunRequest,
|
||||
WorkflowRunResponse,
|
||||
)
|
||||
from skyvern.schemas.workflows import WorkflowRequest
|
||||
from skyvern.services import run_service, task_v1_service, task_v2_service, workflow_service
|
||||
from skyvern.webeye.actions.actions import Action
|
||||
|
||||
@@ -437,32 +440,11 @@ async def cancel_run(
|
||||
response_model=Workflow,
|
||||
include_in_schema=False,
|
||||
)
|
||||
@base_router.post(
|
||||
"/workflows",
|
||||
response_model=Workflow,
|
||||
tags=["Workflows"],
|
||||
openapi_extra={
|
||||
"x-fern-sdk-group-name": "workflows",
|
||||
"x-fern-sdk-method-name": "create_workflow",
|
||||
"x-fern-examples": [{"code-samples": [{"sdk": "curl", "code": CREATE_WORKFLOW_CODE_SAMPLE}]}],
|
||||
},
|
||||
description="Create a new workflow",
|
||||
summary="Create a new workflow",
|
||||
responses={
|
||||
200: {"description": "Successfully created workflow"},
|
||||
422: {"description": "Invalid workflow definition"},
|
||||
},
|
||||
)
|
||||
@base_router.post(
|
||||
"/workflows/",
|
||||
response_model=Workflow,
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def create_workflow(
|
||||
async def create_workflow_legacy(
|
||||
request: Request,
|
||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||
) -> Workflow:
|
||||
analytics.capture("skyvern-oss-agent-workflow-create")
|
||||
analytics.capture("skyvern-oss-agent-workflow-create-legacy")
|
||||
raw_yaml = await request.body()
|
||||
try:
|
||||
workflow_yaml = yaml.safe_load(raw_yaml)
|
||||
@@ -481,6 +463,63 @@ async def create_workflow(
|
||||
raise FailedToCreateWorkflow(str(e))
|
||||
|
||||
|
||||
@base_router.post(
|
||||
"/workflows",
|
||||
response_model=Workflow,
|
||||
tags=["Workflows"],
|
||||
openapi_extra={
|
||||
"x-fern-sdk-group-name": "workflows",
|
||||
"x-fern-sdk-method-name": "create_workflow",
|
||||
"x-fern-examples": [
|
||||
{
|
||||
"code-samples": [
|
||||
{"sdk": "curl", "code": CREATE_WORKFLOW_CODE_SAMPLE},
|
||||
{"sdk": "python", "code": CREATE_WORKFLOW_CODE_SAMPLE_PYTHON},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
description="Create a new workflow definition",
|
||||
summary="Create a new workflow definition",
|
||||
responses={
|
||||
200: {"description": "Successfully created workflow"},
|
||||
422: {"description": "Invalid workflow definition"},
|
||||
},
|
||||
)
|
||||
@base_router.post(
|
||||
"/workflows/",
|
||||
response_model=Workflow,
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def create_workflow(
|
||||
data: WorkflowRequest,
|
||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||
) -> Workflow:
|
||||
analytics.capture("skyvern-oss-agent-workflow-create")
|
||||
try:
|
||||
if data.yaml_definition:
|
||||
workflow_json_from_yaml = yaml.safe_load(data.yaml_definition)
|
||||
workflow_definition = WorkflowCreateYAMLRequest.model_validate(workflow_json_from_yaml)
|
||||
elif data.json_definition:
|
||||
workflow_definition = data.json_definition
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Invalid workflow definition. Workflow should be provided in either yaml or json format.",
|
||||
)
|
||||
return await app.WORKFLOW_SERVICE.create_workflow_from_request(
|
||||
organization=current_org,
|
||||
request=workflow_definition,
|
||||
)
|
||||
except yaml.YAMLError:
|
||||
raise HTTPException(status_code=422, detail="Invalid YAML")
|
||||
except WorkflowParameterMissingRequiredValue as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
LOG.error("Failed to create workflow", exc_info=True, organization_id=current_org.organization_id)
|
||||
raise FailedToCreateWorkflow(str(e))
|
||||
|
||||
|
||||
@legacy_base_router.put(
|
||||
"/workflows/{workflow_id}",
|
||||
openapi_extra={
|
||||
@@ -505,38 +544,7 @@ async def create_workflow(
|
||||
response_model=Workflow,
|
||||
include_in_schema=False,
|
||||
)
|
||||
@base_router.post(
|
||||
"/workflows/{workflow_id}",
|
||||
response_model=Workflow,
|
||||
tags=["Workflows"],
|
||||
openapi_extra={
|
||||
"requestBody": {
|
||||
"content": {"application/x-yaml": {"schema": WorkflowCreateYAMLRequest.model_json_schema()}},
|
||||
"required": True,
|
||||
},
|
||||
"x-fern-sdk-group-name": "workflows",
|
||||
"x-fern-sdk-method-name": "update_workflow",
|
||||
"x-fern-examples": [{"code-samples": [{"sdk": "curl", "code": UPDATE_WORKFLOW_CODE_SAMPLE}]}],
|
||||
},
|
||||
description="Update a workflow definition",
|
||||
summary="Update a workflow definition",
|
||||
responses={
|
||||
200: {"description": "Successfully updated workflow"},
|
||||
422: {"description": "Invalid workflow definition"},
|
||||
},
|
||||
)
|
||||
@base_router.post(
|
||||
"/workflows/{workflow_id}/",
|
||||
openapi_extra={
|
||||
"requestBody": {
|
||||
"content": {"application/x-yaml": {"schema": WorkflowCreateYAMLRequest.model_json_schema()}},
|
||||
"required": True,
|
||||
},
|
||||
},
|
||||
response_model=Workflow,
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def update_workflow(
|
||||
async def update_workflow_legacy(
|
||||
request: Request,
|
||||
workflow_id: str = Path(
|
||||
..., description="The ID of the workflow to update. Workflow ID starts with `wpid_`.", examples=["wpid_123"]
|
||||
@@ -569,6 +577,78 @@ async def update_workflow(
|
||||
raise FailedToUpdateWorkflow(workflow_id, f"<{type(e).__name__}: {str(e)}>")
|
||||
|
||||
|
||||
@base_router.post(
|
||||
"/workflows/{workflow_id}",
|
||||
response_model=Workflow,
|
||||
tags=["Workflows"],
|
||||
openapi_extra={
|
||||
"x-fern-sdk-group-name": "workflows",
|
||||
"x-fern-sdk-method-name": "update_workflow",
|
||||
"x-fern-examples": [
|
||||
{
|
||||
"code-samples": [
|
||||
{"sdk": "curl", "code": UPDATE_WORKFLOW_CODE_SAMPLE},
|
||||
{"sdk": "python", "code": UPDATE_WORKFLOW_CODE_SAMPLE_PYTHON},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
description="Update a workflow definition",
|
||||
summary="Update a workflow definition",
|
||||
responses={
|
||||
200: {"description": "Successfully updated workflow"},
|
||||
422: {"description": "Invalid workflow definition"},
|
||||
},
|
||||
)
|
||||
@base_router.post(
|
||||
"/workflows/{workflow_id}/",
|
||||
openapi_extra={
|
||||
"requestBody": {
|
||||
"content": {"application/x-yaml": {"schema": WorkflowCreateYAMLRequest.model_json_schema()}},
|
||||
"required": True,
|
||||
},
|
||||
},
|
||||
response_model=Workflow,
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def update_workflow(
|
||||
data: WorkflowRequest,
|
||||
workflow_id: str = Path(
|
||||
..., description="The ID of the workflow to update. Workflow ID starts with `wpid_`.", examples=["wpid_123"]
|
||||
),
|
||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||
) -> Workflow:
|
||||
analytics.capture("skyvern-oss-agent-workflow-update")
|
||||
try:
|
||||
if data.yaml_definition:
|
||||
workflow_json_from_yaml = yaml.safe_load(data.yaml_definition)
|
||||
workflow_definition = WorkflowCreateYAMLRequest.model_validate(workflow_json_from_yaml)
|
||||
elif data.json_definition:
|
||||
workflow_definition = data.json_definition
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Invalid workflow definition. Workflow should be provided in either yaml or json format.",
|
||||
)
|
||||
return await app.WORKFLOW_SERVICE.create_workflow_from_request(
|
||||
organization=current_org,
|
||||
request=workflow_definition,
|
||||
workflow_permanent_id=workflow_id,
|
||||
)
|
||||
except yaml.YAMLError:
|
||||
raise HTTPException(status_code=422, detail="Invalid YAML")
|
||||
except WorkflowParameterMissingRequiredValue as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
LOG.exception(
|
||||
"Failed to update workflow",
|
||||
exc_info=True,
|
||||
organization_id=current_org.organization_id,
|
||||
workflow_permanent_id=workflow_id,
|
||||
)
|
||||
raise FailedToUpdateWorkflow(workflow_id, f"<{type(e).__name__}: {str(e)}>")
|
||||
|
||||
|
||||
@legacy_base_router.delete(
|
||||
"/workflows/{workflow_id}",
|
||||
tags=["agent"],
|
||||
|
||||
@@ -21,280 +21,333 @@ await skyvern.agent.cancel_run(run_id="tsk_v2_123")
|
||||
"""
|
||||
CREATE_WORKFLOW_CODE_SAMPLE = """curl -X POST https://api.skyvern.com/v1/workflows \
|
||||
--header 'x-api-key: {{x-api-key}}' \
|
||||
--header 'Content-Type: application/x-yaml' \
|
||||
--data-raw 'title: Invoice Downloading Demo (Jun 13)
|
||||
description: >-
|
||||
Login to the website, download all the invoices after a date, email the
|
||||
invoices
|
||||
--header 'Content-Type: text/plain' \
|
||||
--data-raw 'title: Contact Forms
|
||||
description: Fill the contact form on the website
|
||||
proxy_location: RESIDENTIAL
|
||||
webhook_callback_url: https://example.com/webhook
|
||||
totp_verification_url: https://example.com/totp
|
||||
persist_browser_session: false
|
||||
workflow_definition:
|
||||
parameters:
|
||||
- key: website_url
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
- key: credentials
|
||||
parameter_type: bitwarden_login_credential
|
||||
bitwarden_client_id_aws_secret_key: SECRET
|
||||
bitwarden_client_secret_aws_secret_key: SECRET
|
||||
bitwarden_master_password_aws_secret_key: SECRET
|
||||
bitwarden_collection_id: SECRET
|
||||
url_parameter_key: website_url
|
||||
- key: invoice_retrieval_start_date
|
||||
default_value: null
|
||||
- key: name
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
- key: smtp_host
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_HOST_AWS_SES
|
||||
- key: smtp_port
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_PORT_AWS_SES
|
||||
- key: smtp_username
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_USERNAME_SES
|
||||
- key: smtp_password
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_PASSWORD_SES
|
||||
- parameter_type: context
|
||||
key: order_history_url
|
||||
source_parameter_key: get_order_history_page_url_and_qualifying_order_ids_output
|
||||
- parameter_type: context
|
||||
key: order_ids
|
||||
source_parameter_key: get_order_history_page_url_and_qualifying_order_ids_output
|
||||
- parameter_type: context
|
||||
key: order_id
|
||||
source_parameter_key: order_ids
|
||||
default_value: null
|
||||
- key: additional_information
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
default_value: |-
|
||||
Message: I'd love to learn more about your...
|
||||
Phone: 123-456-7890
|
||||
Inquiry type: sales
|
||||
Optional Subject: Hello from [Company Here]
|
||||
- key: email
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
default_value: null
|
||||
blocks:
|
||||
- block_type: task
|
||||
label: login
|
||||
parameter_keys:
|
||||
- credentials
|
||||
url: website_url
|
||||
navigation_goal: >-
|
||||
If you're not on the login page, navigate to login page and login using the credentials given, and then navigate to the personal account page. First, take actions on promotional popups or cookie prompts that could prevent taking other action on the web page. Then, try to login and navigate to the personal account page. If you fail to login to find the login page or can't login after several trials, terminate. If you're on the personal account page, consider the goal is completed.
|
||||
error_code_mapping:
|
||||
stuck_with_popups: terminate and return this error if you can't close popups after several tries and can't take the necessary actions on the website because there is a blocking popup on the page
|
||||
failed_to_login: terminate and return this error if you fail logging in to the page
|
||||
- block_type: task
|
||||
label: get_order_history_page_url_and_qualifying_order_ids
|
||||
parameter_keys:
|
||||
- invoice_retrieval_start_date
|
||||
navigation_goal: Find the order history page. If there is no orders after given start date, terminate.
|
||||
data_extraction_goal: >-
|
||||
You need to extract the order history page url by looking at the current
|
||||
page you're on. You need to extract contact emails you see on the page. You also need to extract the order ids for orders that
|
||||
happened on or after invoice_retrieval_start_date. Make sure to filter
|
||||
only the orders that happened on or after invoice_retrieval_start_date. You need to compare each order's date with the invoice_download_start_date. You can only include an order in the output if the order's date is after or the same as the invoice_download_start_date.
|
||||
While comparing dates, first compare year, then month, then day. invoice_retrieval_start_date
|
||||
is in YYYY-MM-DD format. The dates on the websites may be in different formats, compare accordingly and compare year, date, and month.
|
||||
error_code_mapping:
|
||||
failed_to_find_order_history_page: return this error if you can't find the order history page on the website
|
||||
no_orders_found_after_start_date: return this error if there are no orders after the specified invoice_download_start_date
|
||||
data_schema:
|
||||
type: object
|
||||
properties:
|
||||
order_history_url:
|
||||
type: url
|
||||
description: >-
|
||||
The exact URL of the order history page. Do not make any
|
||||
assumptions. Return the URL that's passed along in this context.
|
||||
contact_emails:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Contact email for the ecommerce website you're on. If you can't find any return null
|
||||
date_comparison_scratchpad:
|
||||
type: string
|
||||
description: >-
|
||||
You are supposed to filter the orders that happened on or after the invoice_download_start_date. Think through how you will approach this task step-by-step here. Consider these before starting the comparison:
|
||||
- What format is the order date in? How can you parse it into a structured format?
|
||||
- What is the correct way to compare two dates?
|
||||
- How will you compare the order dates to the invoice_download_start_date?
|
||||
|
||||
Write out your thought process before filling out the order_ids field below. Remember, the original date may be in any format, so parse it carefully! The invoice_download_start_date will be an exact date you can directly compare against in the format YYYY-MM-DD.
|
||||
order_ids:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
order_date:
|
||||
type: iso-8601-date-string
|
||||
order_id:
|
||||
type: string
|
||||
description: >-
|
||||
Return a list of order id strings. Do not return order ids of
|
||||
orders that happened before the specified
|
||||
invoice_retrieval_start_date
|
||||
- block_type: for_loop
|
||||
label: iterate_over_order_ids
|
||||
loop_over_parameter_key: order_ids
|
||||
- label: Fill_Out_Contact_Form
|
||||
continue_on_failure: true
|
||||
loop_blocks:
|
||||
- block_type: task
|
||||
label: download_invoice_for_order
|
||||
complete_on_download: true
|
||||
continue_on_failure: true
|
||||
parameter_keys:
|
||||
- order_id
|
||||
url: order_history_url
|
||||
navigation_goal: Download the invoice of the order with the given order ID. Make sure to download the invoice for the given order id. If the element tree doesn't have a matching order id, check the screenshots. Complete if you have successfully downloaded the invoice according to action history, if you were able to download it, you'll see download_triggered=True for the last step. If you don't see a way to download an invoice, navigate to the order page if possible. If there's no way to download an invoice terminate. If the text suggests printing, you can assume you can download it. Return click action with download=True if you want to trigger a download.
|
||||
error_code_mapping:
|
||||
not_possible_to_download_invoice: return this error if the website doesn't allow downloading/viewing invoices
|
||||
cant_solve_captcha: return this error if captcha isn't solved after multiple retries
|
||||
- block_type: upload_to_s3
|
||||
label: upload_downloaded_files_to_s3
|
||||
path: SKYVERN_DOWNLOAD_DIRECTORY
|
||||
- block_type: send_email
|
||||
label: send_email
|
||||
smtp_host_secret_parameter_key: smtp_host
|
||||
smtp_port_secret_parameter_key: smtp_port
|
||||
smtp_username_secret_parameter_key: smtp_username
|
||||
smtp_password_secret_parameter_key: smtp_password
|
||||
sender: hello@skyvern.com
|
||||
recipients:
|
||||
- founders@skyvern.com
|
||||
subject: Skyvern - Downloaded Invoices Demo
|
||||
body: website_url
|
||||
file_attachments:
|
||||
- SKYVERN_DOWNLOAD_DIRECTORY
|
||||
block_type: navigation
|
||||
url: "{{website_url}}"
|
||||
title: Fill_Out_Contact_Form
|
||||
engine: skyvern-1.0
|
||||
navigation_goal: >-
|
||||
Find the contact form. Fill out the contact us form and submit it. Your
|
||||
goal is complete when the page says your message has been sent. In the
|
||||
case you can't find a contact us form, terminate.
|
||||
|
||||
|
||||
Fill out required fields as best you can using the following
|
||||
information:
|
||||
|
||||
{{name}}
|
||||
|
||||
{{email}}
|
||||
|
||||
{{additional_information}}
|
||||
error_code_mapping: null
|
||||
max_retries: 0
|
||||
max_steps_per_run: null
|
||||
complete_on_download: false
|
||||
download_suffix: null
|
||||
parameter_keys: []
|
||||
totp_identifier: null
|
||||
totp_verification_url: null
|
||||
cache_actions: false
|
||||
complete_criterion: ""
|
||||
terminate_criterion: ""
|
||||
include_action_history_in_verification: false
|
||||
- label: Extract_Email
|
||||
continue_on_failure: false
|
||||
block_type: extraction
|
||||
url: ""
|
||||
title: Extract_Email
|
||||
data_extraction_goal: "Extract a company email if available "
|
||||
data_schema: null
|
||||
max_retries: 0
|
||||
max_steps_per_run: null
|
||||
parameter_keys: []
|
||||
cache_actions: false
|
||||
'
|
||||
"""
|
||||
CREATE_WORKFLOW_CODE_SAMPLE_PYTHON = """
|
||||
from skyvern import Skyvern
|
||||
|
||||
# Create a workflow in JSON format
|
||||
workflow_definition = {
|
||||
"title": "Contact Forms Workflow",
|
||||
"description": "Fill the contact form on the website",
|
||||
"proxy_location": "RESIDENTIAL",
|
||||
"webhook_callback_url": "https://example.com/webhook",
|
||||
"totp_verification_url": "https://example.com/totp",
|
||||
"totp_identifier": "4155555555",
|
||||
"workflow_definition": {
|
||||
"parameters": [
|
||||
{
|
||||
"key": "website_url",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": None,
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": None,
|
||||
},
|
||||
{
|
||||
"key": "additional_information",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": "Message: I'd love to learn more about your...\nPhone: 123-456-7890\nInquiry type: sales\nOptional Subject: Hello from [Company Here]",
|
||||
},
|
||||
{
|
||||
"key": "email",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": None,
|
||||
},
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"label": "Fill_Out_Contact_Form",
|
||||
"continue_on_failure": True,
|
||||
"block_type": "navigation",
|
||||
"url": "{{website_url}}",
|
||||
"title": "Fill_Out_Contact_Form",
|
||||
"engine": "skyvern-1.0",
|
||||
"navigation_goal": "Find the contact form. Fill out the contact us form and submit it. Your goal is complete when the page says your message has been sent. In the case you can't find a contact us form, terminate.\n\nFill out required fields as best you can using the following information:\n{{name}}\n{{email}}\n{{additional_information}}",
|
||||
"error_code_mapping": None,
|
||||
"max_retries": 0,
|
||||
"max_steps_per_run": None,
|
||||
"complete_on_download": False,
|
||||
"download_suffix": None,
|
||||
"parameter_keys": [],
|
||||
"totp_identifier": None,
|
||||
"totp_verification_url": None,
|
||||
"cache_actions": False,
|
||||
"complete_criterion": "",
|
||||
"terminate_criterion": "",
|
||||
"include_action_history_in_verification": False,
|
||||
},
|
||||
{
|
||||
"label": "Extract_Email",
|
||||
"continue_on_failure": False,
|
||||
"block_type": "extraction",
|
||||
"url": "",
|
||||
"title": "Extract_Email",
|
||||
"data_extraction_goal": "Extract a company email if available ",
|
||||
"data_schema": None,
|
||||
"max_retries": 0,
|
||||
"max_steps_per_run": None,
|
||||
"parameter_keys": [],
|
||||
"cache_actions": False,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
skyvern = Skyvern(api_key="your_api_key")
|
||||
workflow = await skyvern.workflows.create_workflow(json_definition=workflow_definition)
|
||||
print(workflow)
|
||||
"""
|
||||
UPDATE_WORKFLOW_CODE_SAMPLE = """curl -X POST https://api.skyvern.com/v1/workflows/wpid_123 \
|
||||
--header 'x-api-key: {{x-api-key}}' \
|
||||
--header 'Content-Type: application/x-yaml' \
|
||||
--data-raw 'title: Invoice Downloading Demo (Jun 13)
|
||||
description: >-
|
||||
Login to the website, download all the invoices after a date, email the
|
||||
invoices
|
||||
--header 'Content-Type: text/plain' \
|
||||
--data-raw 'title: Contact Forms
|
||||
description: Fill the contact form on the website
|
||||
proxy_location: RESIDENTIAL
|
||||
webhook_callback_url: https://example.com/webhook
|
||||
totp_verification_url: https://example.com/totp
|
||||
persist_browser_session: false
|
||||
workflow_definition:
|
||||
parameters:
|
||||
- key: website_url
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
- key: credentials
|
||||
parameter_type: bitwarden_login_credential
|
||||
bitwarden_client_id_aws_secret_key: SECRET
|
||||
bitwarden_client_secret_aws_secret_key: SECRET
|
||||
bitwarden_master_password_aws_secret_key: SECRET
|
||||
bitwarden_collection_id: SECRET
|
||||
url_parameter_key: website_url
|
||||
- key: invoice_retrieval_start_date
|
||||
default_value: null
|
||||
- key: name
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
- key: smtp_host
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_HOST_AWS_SES
|
||||
- key: smtp_port
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_PORT_AWS_SES
|
||||
- key: smtp_username
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_USERNAME_SES
|
||||
- key: smtp_password
|
||||
parameter_type: aws_secret
|
||||
aws_key: SKYVERN_SMTP_PASSWORD_SES
|
||||
- parameter_type: context
|
||||
key: order_history_url
|
||||
source_parameter_key: get_order_history_page_url_and_qualifying_order_ids_output
|
||||
- parameter_type: context
|
||||
key: order_ids
|
||||
source_parameter_key: get_order_history_page_url_and_qualifying_order_ids_output
|
||||
- parameter_type: context
|
||||
key: order_id
|
||||
source_parameter_key: order_ids
|
||||
default_value: null
|
||||
- key: additional_information
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
default_value: |-
|
||||
Message: I'd love to learn more about your...
|
||||
Phone: 123-456-7890
|
||||
Inquiry type: sales
|
||||
Optional Subject: Hello from [Company Here]
|
||||
- key: email
|
||||
description: null
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
default_value: null
|
||||
blocks:
|
||||
- block_type: task
|
||||
label: login
|
||||
parameter_keys:
|
||||
- credentials
|
||||
url: website_url
|
||||
navigation_goal: >-
|
||||
If you're not on the login page, navigate to login page and login using the credentials given, and then navigate to the personal account page. First, take actions on promotional popups or cookie prompts that could prevent taking other action on the web page. Then, try to login and navigate to the personal account page. If you fail to login to find the login page or can't login after several trials, terminate. If you're on the personal account page, consider the goal is completed.
|
||||
error_code_mapping:
|
||||
stuck_with_popups: terminate and return this error if you can't close popups after several tries and can't take the necessary actions on the website because there is a blocking popup on the page
|
||||
failed_to_login: terminate and return this error if you fail logging in to the page
|
||||
- block_type: task
|
||||
label: get_order_history_page_url_and_qualifying_order_ids
|
||||
parameter_keys:
|
||||
- invoice_retrieval_start_date
|
||||
navigation_goal: Find the order history page. If there is no orders after given start date, terminate.
|
||||
data_extraction_goal: >-
|
||||
You need to extract the order history page url by looking at the current
|
||||
page you're on. You need to extract contact emails you see on the page. You also need to extract the order ids for orders that
|
||||
happened on or after invoice_retrieval_start_date. Make sure to filter
|
||||
only the orders that happened on or after invoice_retrieval_start_date. You need to compare each order's date with the invoice_download_start_date. You can only include an order in the output if the order's date is after or the same as the invoice_download_start_date.
|
||||
While comparing dates, first compare year, then month, then day. invoice_retrieval_start_date
|
||||
is in YYYY-MM-DD format. The dates on the websites may be in different formats, compare accordingly and compare year, date, and month.
|
||||
error_code_mapping:
|
||||
failed_to_find_order_history_page: return this error if you can't find the order history page on the website
|
||||
no_orders_found_after_start_date: return this error if there are no orders after the specified invoice_download_start_date
|
||||
data_schema:
|
||||
type: object
|
||||
properties:
|
||||
order_history_url:
|
||||
type: url
|
||||
description: >-
|
||||
The exact URL of the order history page. Do not make any
|
||||
assumptions. Return the URL that's passed along in this context.
|
||||
contact_emails:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Contact email for the ecommerce website you're on. If you can't find any return null
|
||||
date_comparison_scratchpad:
|
||||
type: string
|
||||
description: >-
|
||||
You are supposed to filter the orders that happened on or after the invoice_download_start_date. Think through how you will approach this task step-by-step here. Consider these before starting the comparison:
|
||||
- What format is the order date in? How can you parse it into a structured format?
|
||||
- What is the correct way to compare two dates?
|
||||
- How will you compare the order dates to the invoice_download_start_date?
|
||||
|
||||
Write out your thought process before filling out the order_ids field below. Remember, the original date may be in any format, so parse it carefully! The invoice_download_start_date will be an exact date you can directly compare against in the format YYYY-MM-DD.
|
||||
order_ids:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
order_date:
|
||||
type: iso-8601-date-string
|
||||
order_id:
|
||||
type: string
|
||||
description: >-
|
||||
Return a list of order id strings. Do not return order ids of
|
||||
orders that happened before the specified
|
||||
invoice_retrieval_start_date
|
||||
- block_type: for_loop
|
||||
label: iterate_over_order_ids
|
||||
loop_over_parameter_key: order_ids
|
||||
- label: Fill_Out_Contact_Form
|
||||
continue_on_failure: true
|
||||
loop_blocks:
|
||||
- block_type: task
|
||||
label: download_invoice_for_order
|
||||
complete_on_download: true
|
||||
continue_on_failure: true
|
||||
parameter_keys:
|
||||
- order_id
|
||||
url: order_history_url
|
||||
navigation_goal: Download the invoice of the order with the given order ID. Make sure to download the invoice for the given order id. If the element tree doesn't have a matching order id, check the screenshots. Complete if you have successfully downloaded the invoice according to action history, if you were able to download it, you'll see download_triggered=True for the last step. If you don't see a way to download an invoice, navigate to the order page if possible. If there's no way to download an invoice terminate. If the text suggests printing, you can assume you can download it. Return click action with download=True if you want to trigger a download.
|
||||
error_code_mapping:
|
||||
not_possible_to_download_invoice: return this error if the website doesn't allow downloading/viewing invoices
|
||||
cant_solve_captcha: return this error if captcha isn't solved after multiple retries
|
||||
- block_type: upload_to_s3
|
||||
label: upload_downloaded_files_to_s3
|
||||
path: SKYVERN_DOWNLOAD_DIRECTORY
|
||||
- block_type: send_email
|
||||
label: send_email
|
||||
smtp_host_secret_parameter_key: smtp_host
|
||||
smtp_port_secret_parameter_key: smtp_port
|
||||
smtp_username_secret_parameter_key: smtp_username
|
||||
smtp_password_secret_parameter_key: smtp_password
|
||||
sender: hello@skyvern.com
|
||||
recipients:
|
||||
- founders@skyvern.com
|
||||
subject: Skyvern - Downloaded Invoices Demo
|
||||
body: website_url
|
||||
file_attachments:
|
||||
- SKYVERN_DOWNLOAD_DIRECTORY
|
||||
block_type: navigation
|
||||
url: "{{website_url}}"
|
||||
title: Fill_Out_Contact_Form
|
||||
engine: skyvern-1.0
|
||||
navigation_goal: >-
|
||||
Find the contact form. Fill out the contact us form and submit it. Your
|
||||
goal is complete when the page says your message has been sent. In the
|
||||
case you can't find a contact us form, terminate.
|
||||
|
||||
|
||||
Fill out required fields as best you can using the following
|
||||
information:
|
||||
|
||||
{{name}}
|
||||
|
||||
{{email}}
|
||||
|
||||
{{additional_information}}
|
||||
error_code_mapping: null
|
||||
max_retries: 0
|
||||
max_steps_per_run: null
|
||||
complete_on_download: false
|
||||
download_suffix: null
|
||||
parameter_keys: []
|
||||
totp_identifier: null
|
||||
totp_verification_url: null
|
||||
cache_actions: false
|
||||
complete_criterion: ""
|
||||
terminate_criterion: ""
|
||||
include_action_history_in_verification: false
|
||||
- label: Extract_Email
|
||||
continue_on_failure: false
|
||||
block_type: extraction
|
||||
url: ""
|
||||
title: Extract_Email
|
||||
data_extraction_goal: "Extract a company email if available "
|
||||
data_schema: null
|
||||
max_retries: 0
|
||||
max_steps_per_run: null
|
||||
parameter_keys: []
|
||||
cache_actions: false
|
||||
'
|
||||
"""
|
||||
UPDATE_WORKFLOW_CODE_SAMPLE_PYTHON = """
|
||||
from skyvern import Skyvern
|
||||
|
||||
updated_workflow_definition = {
|
||||
"title": "Updated Contact Forms Workflow",
|
||||
"description": "Fill the contact form on the website",
|
||||
"proxy_location": "RESIDENTIAL",
|
||||
"webhook_callback_url": "https://example.com/webhook",
|
||||
"totp_verification_url": "https://example.com/totp",
|
||||
"totp_identifier": "4155555555",
|
||||
"workflow_definition": {
|
||||
"parameters": [
|
||||
{
|
||||
"key": "website_url",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": None,
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": None,
|
||||
},
|
||||
{
|
||||
"key": "additional_information",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": "Message: I'd love to learn more about your...\nPhone: 123-456-7890\nInquiry type: sales\nOptional Subject: Hello from [Company Here]",
|
||||
},
|
||||
{
|
||||
"key": "email",
|
||||
"description": None,
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string",
|
||||
"default_value": None,
|
||||
},
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"label": "Fill_Out_Contact_Form",
|
||||
"continue_on_failure": True,
|
||||
"block_type": "navigation",
|
||||
"url": "{{website_url}}",
|
||||
"title": "Fill_Out_Contact_Form",
|
||||
"engine": "skyvern-1.0",
|
||||
"navigation_goal": "Find the contact form. Fill out the contact us form and submit it. Your goal is complete when the page says your message has been sent. In the case you can't find a contact us form, terminate.\n\nFill out required fields as best you can using the following information:\n{{name}}\n{{email}}\n{{additional_information}}",
|
||||
"error_code_mapping": None,
|
||||
"max_retries": 0,
|
||||
"max_steps_per_run": None,
|
||||
"complete_on_download": False,
|
||||
"download_suffix": None,
|
||||
"parameter_keys": [],
|
||||
"totp_identifier": None,
|
||||
"totp_verification_url": None,
|
||||
"cache_actions": False,
|
||||
"complete_criterion": "",
|
||||
"terminate_criterion": "",
|
||||
"include_action_history_in_verification": False,
|
||||
},
|
||||
{
|
||||
"label": "Extract_Email",
|
||||
"continue_on_failure": False,
|
||||
"block_type": "extraction",
|
||||
"url": "",
|
||||
"title": "Extract_Email",
|
||||
"data_extraction_goal": "Extract a company email if available ",
|
||||
"data_schema": None,
|
||||
"max_retries": 0,
|
||||
"max_steps_per_run": None,
|
||||
"parameter_keys": [],
|
||||
"cache_actions": False,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
skyvern = Skyvern(api_key="your_api_key")
|
||||
workflow = await skyvern.workflows.update_workflow(workflow_id="wpid_123", json_definition=updated_workflow_definition)
|
||||
print(workflow)
|
||||
"""
|
||||
DELETE_WORKFLOW_CODE_SAMPLE = """from skyvern import Skyvern
|
||||
|
||||
skyvern = Skyvern(api_key="your_api_key")
|
||||
|
||||
14
skyvern/schemas/workflows.py
Normal file
14
skyvern/schemas/workflows.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from skyvern.forge.sdk.workflow.models.yaml import WorkflowCreateYAMLRequest
|
||||
|
||||
|
||||
class WorkflowRequest(BaseModel):
|
||||
json_definition: WorkflowCreateYAMLRequest | None = Field(
|
||||
default=None,
|
||||
description="Workflow definition in JSON format",
|
||||
)
|
||||
yaml_definition: str | None = Field(
|
||||
default=None,
|
||||
description="Workflow definition in YAML format",
|
||||
)
|
||||
Reference in New Issue
Block a user