From ce4b4c000ef1d58845293ed01883c9fe723bf9a1 Mon Sep 17 00:00:00 2001 From: Saurav Panda Date: Tue, 18 Nov 2025 23:36:17 +0530 Subject: [PATCH 1/3] updated workflow use version --- workflows/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/pyproject.toml b/workflows/pyproject.toml index 8393d5e..723a168 100644 --- a/workflows/pyproject.toml +++ b/workflows/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "workflow-use" -version = "0.2.9" +version = "0.2.10" authors = [{ name = "Gregor Zunic" }] description = "Create, edit, run deterministic workflows" readme = "README.md" From 438b3b556f637d9d15a449627b9455d67f9cea42 Mon Sep 17 00:00:00 2001 From: Saurav Panda Date: Wed, 19 Nov 2025 19:22:37 +0530 Subject: [PATCH 2/3] ensure all workflows end with extract step --- .../examples/scripts/generate_workflow.py | 2 +- workflows/uv.lock | 2 +- .../prompts/workflow_creation_prompt.md | 14 ++++++++---- .../prompts/workflow_validation_prompt.md | 7 ++++++ workflows/workflow_use/schema/views.py | 22 ++++++++++++++++++- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/workflows/examples/scripts/generate_workflow.py b/workflows/examples/scripts/generate_workflow.py index d2ace3b..b30efb2 100644 --- a/workflows/examples/scripts/generate_workflow.py +++ b/workflows/examples/scripts/generate_workflow.py @@ -89,7 +89,7 @@ async def generate_and_store_workflow(): original_task=TASK_DESCRIPTION.strip(), ) - print(f'✅ Workflow saved to storage!\n') + print('✅ Workflow saved to storage!\n') # Display summary print('=' * 80) diff --git a/workflows/uv.lock b/workflows/uv.lock index 4828368..7939b53 100644 --- a/workflows/uv.lock +++ b/workflows/uv.lock @@ -4863,7 +4863,7 @@ wheels = [ [[package]] name = "workflow-use" -version = "0.2.9" +version = "0.2.10" source = { editable = "." } dependencies = [ { name = "aiofiles" }, diff --git a/workflows/workflow_use/healing/prompts/workflow_creation_prompt.md b/workflows/workflow_use/healing/prompts/workflow_creation_prompt.md index 2d12240..1444953 100644 --- a/workflows/workflow_use/healing/prompts/workflow_creation_prompt.md +++ b/workflows/workflow_use/healing/prompts/workflow_creation_prompt.md @@ -6,23 +6,29 @@ You are a master at building re-executable workflows from browser automation ste **BEFORE YOU START - THESE RULES ARE MANDATORY:** -1. **NEVER use `agent` steps for simple search/input/click actions!** +1. **ALL workflows MUST end with an extract step!** + - EVERY workflow must have `extract` or `extract_page_content` as the final step + - This is CRITICAL - AI processing is ALWAYS needed at the end + - Even form-filling workflows should extract confirmation/success status + - Example final step: `{{"type": "extract_page_content", "goal": "Extract the confirmation message or success status"}}` + +2. **NEVER use `agent` steps for simple search/input/click actions!** - If you see `input_text` action → Use `input` step with `target_text` - If you see `click_element` action → Use `click` step with `target_text` - If you see `send_keys` action → Use `keypress` step - Agent steps are 10-30x SLOWER and cost money per execution! -2. **ALWAYS use semantic `target_text` for element targeting!** +3. **ALWAYS use semantic `target_text` for element targeting!** - Look for visible text, labels, placeholders, aria-labels - Use `{{variable}}` syntax (one pair of curly braces) in `target_text` for dynamic values - Example: `{{"type": "click", "target_text": "{{repo_name}}"}}` -3. **Variables MUST use {variable} syntax (one pair of curly braces)** +4. **Variables MUST use {variable} syntax (one pair of curly braces)** - ✅ CORRECT: `"value": "{{email}}"` or `"target_text": "{{repo_name}}"` - ❌ WRONG: `"value": "{{{{email}}}}"` or `"value": "email"` - Python's str.format() substitutes {variable} with actual values at runtime -4. **Prefer direct navigation over search engines!** +5. **Prefer direct navigation over search engines!** - If task involves "search GitHub" → Navigate directly to https://github.com - If task involves "search Twitter" → Navigate directly to https://twitter.com - Only use search engines if the target URL is genuinely unknown diff --git a/workflows/workflow_use/healing/prompts/workflow_validation_prompt.md b/workflows/workflow_use/healing/prompts/workflow_validation_prompt.md index 814895d..910996e 100644 --- a/workflows/workflow_use/healing/prompts/workflow_validation_prompt.md +++ b/workflows/workflow_use/healing/prompts/workflow_validation_prompt.md @@ -38,6 +38,12 @@ Review the provided workflow definition and: - Extract steps without `extractionGoal` - Key press steps without `key` +6. **Workflow Must End with Extract Step** + - ALL workflows must end with an extract step (`extract` or `extract_page_content`) + - This is CRITICAL because AI processing is always needed at the end + - Even form-filling workflows should extract confirmation/success status + - Example: After submitting a form, add `{"type": "extract_page_content", "goal": "Extract the confirmation message or success status"}` + ### Warnings (May Cause Issues) 1. **Generic Target Text** @@ -117,6 +123,7 @@ Use these standardized issue types: - `incorrect_step_type`: Wrong step type for the action - `invalid_variable`: Variable reference doesn't exist in input_schema - `missing_required_field`: Required field is missing +- `missing_final_extract`: Workflow doesn't end with extract step (CRITICAL) - `generic_target_text`: Target text is too generic - `missing_error_handling`: No conditional logic for errors - `hardcoded_value`: Value that should be a variable diff --git a/workflows/workflow_use/schema/views.py b/workflows/workflow_use/schema/views.py index 053a16f..42f9ec1 100644 --- a/workflows/workflow_use/schema/views.py +++ b/workflows/workflow_use/schema/views.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Literal, Optional, Union -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, validator # --- Base Step Model --- @@ -237,6 +237,26 @@ class WorkflowDefinitionSchema(BaseModel): description='List of input schema definitions.', ) + @validator('steps') + def validate_ends_with_extract(cls, steps: List[WorkflowStep]) -> List[WorkflowStep]: + """Validate that the workflow ends with an extract step.""" + if not steps: + raise ValueError('Workflow must have at least one step') + + last_step = steps[-1] + # Check if last step is an extract step + # We need to check the 'type' attribute from the step dict/model + step_type = getattr(last_step, 'type', None) + + if step_type not in ['extract', 'extract_page_content']: + raise ValueError( + f'Workflow must end with an extract step (extract or extract_page_content). ' + f'Current last step type: {step_type}. ' + f'AI processing is always needed at the end of a workflow.' + ) + + return steps + # Add loader from json file @classmethod def load_from_json(cls, json_path: str): From 633e26e95043dff18e69a9e4cb3d7d117667ecd6 Mon Sep 17 00:00:00 2001 From: Saurav Panda Date: Wed, 19 Nov 2025 19:22:56 +0530 Subject: [PATCH 3/3] ensure all workflows end in extract step --- workflows/examples/workflows/basic/example.workflow.yaml | 4 ++++ .../examples/workflows/basic/pure_semantic.workflow.yaml | 4 ++++ .../workflows/form_filling/semantic_form_fill.workflow.yaml | 4 ++++ workflows/tests/test_go_back.workflow.yaml | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/workflows/examples/workflows/basic/example.workflow.yaml b/workflows/examples/workflows/basic/example.workflow.yaml index 7549184..b6f49f5 100644 --- a/workflows/examples/workflows/basic/example.workflow.yaml +++ b/workflows/examples/workflows/basic/example.workflow.yaml @@ -95,6 +95,10 @@ steps: xpath: id("single") elementTag: BUTTON elementText: null +- description: Extract form submission confirmation and status + type: extract_page_content + goal: Extract the confirmation message, success status, or any validation errors from the current page + output: form_status input_schema: - name: first_name type: string diff --git a/workflows/examples/workflows/basic/pure_semantic.workflow.yaml b/workflows/examples/workflows/basic/pure_semantic.workflow.yaml index 61a182c..3aa8ebd 100644 --- a/workflows/examples/workflows/basic/pure_semantic.workflow.yaml +++ b/workflows/examples/workflows/basic/pure_semantic.workflow.yaml @@ -30,6 +30,10 @@ steps: - description: Select marital status type: click target_text: Single +- description: Extract form completion status + type: extract_page_content + goal: Extract any confirmation messages, success status, or validation errors from the form page + output: form_status input_schema: - name: first_name type: string diff --git a/workflows/examples/workflows/form_filling/semantic_form_fill.workflow.yaml b/workflows/examples/workflows/form_filling/semantic_form_fill.workflow.yaml index 2514932..f42eed1 100644 --- a/workflows/examples/workflows/form_filling/semantic_form_fill.workflow.yaml +++ b/workflows/examples/workflows/form_filling/semantic_form_fill.workflow.yaml @@ -30,6 +30,10 @@ steps: - description: Continue to next section type: click target_text: 'Next: Contact Information' +- description: Extract form progress and confirmation + type: extract_page_content + goal: Extract the current section confirmation, any success messages, or validation errors shown on the page + output: form_status input_schema: - name: first_name type: string diff --git a/workflows/tests/test_go_back.workflow.yaml b/workflows/tests/test_go_back.workflow.yaml index 1e15412..27dbbf1 100644 --- a/workflows/tests/test_go_back.workflow.yaml +++ b/workflows/tests/test_go_back.workflow.yaml @@ -10,4 +10,8 @@ steps: url: https://google.com - description: Go back to example.com type: go_back +- description: Extract page title to confirm navigation + type: extract_page_content + goal: Extract the page title and URL to verify that we successfully navigated back to example.com + output: navigation_confirmation version: 1.0.0