Workflow Copilot: fix dealing with output parameters (#4527)

This commit is contained in:
Stanislav Novosad
2026-01-22 19:02:27 -07:00
committed by GitHub
parent 1f16192915
commit c4c1e84507
2 changed files with 64 additions and 14 deletions

View File

@@ -634,18 +634,19 @@ label: <unique_label>
method: <GET|POST|PUT|PATCH|DELETE> # Optional: HTTP method (default: GET) method: <GET|POST|PUT|PATCH|DELETE> # Optional: HTTP method (default: GET)
url: <https_url> # Optional: Target URL url: <https_url> # Optional: Target URL
headers: {} # Optional: HTTP headers headers: {} # Optional: HTTP headers
body: {} # Optional: JSON body (dict) body: {} # Optional: JSON body (MUST be a dict, not a string)
files: {} # Optional: Multipart files mapping files: {} # Optional: Multipart files mapping
timeout: 30 # Optional: Timeout in seconds timeout: 30 # Optional: Timeout in seconds
follow_redirects: true # Optional: Follow redirects follow_redirects: true # Optional: Follow redirects
parameter_keys: [] # Optional: Parameters used in this block parameter_keys: [] # Optional: Workflow parameters used (block outputs don't need to be listed)
Use Cases: Use Cases:
- Call third-party APIs for enrichment - Call third-party APIs for enrichment
- Post data to internal services - Post data to internal services
- Upload files via multipart requests - Upload files via multipart requests
- Send results from previous blocks to webhooks
Example: Example 1 - Using workflow parameters:
workflow_definition: workflow_definition:
version: 2 version: 2
blocks: blocks:
@@ -667,26 +668,75 @@ workflow_definition:
key: customer_email key: customer_email
workflow_parameter_type: string workflow_parameter_type: string
Example 2 - Sending block output to webhook (no parameter_keys needed):
workflow_definition:
version: 2
parameters: []
blocks:
- block_type: task_v2
label: get_data
next_block_label: send_webhook
prompt: "Get top 3 hacker news items"
url: "https://news.ycombinator.com"
- block_type: http_request
label: send_webhook
next_block_label: null
method: POST
url: "http://example.com/webhook"
body:
data: "{{ get_data.output }}"
parameter_keys: []
** PARAMETER TEMPLATING ** ** PARAMETER TEMPLATING **
All string fields in blocks support Jinja2 templating to reference parameters. All string fields in blocks support Jinja2 templating to reference parameters and block outputs.
Syntax (preferred): There are TWO types of references:
{{ param_key }}
Examples: 1. WORKFLOW PARAMETERS - Reference input parameters defined in the parameters section
Syntax: {{ param_key }}
Requires: parameter_keys list must include the param_key
2. BLOCK OUTPUTS - Reference output from a previous block by its label
Syntax: {{ block_label.output }}
Requires: NOTHING - block outputs are automatically available, no parameter_keys needed
IMPORTANT: Block outputs use the block's label directly (e.g., {{ block_1.output }}).
Examples - Workflow Parameters:
* In URL: * In URL:
url: "https://example.com/search?q={{ search_term }}" url: "https://example.com/search?q={{ search_term }}"
parameter_keys: [search_term]
* In goals: * In goals:
navigation_goal: "Search for {{ product_name }} and filter by {{ category }}" navigation_goal: "Search for {{ product_name }} and filter by {{ category }}"
parameter_keys: [product_name, category]
* In data extraction: Examples - Block Outputs (no parameter_keys needed):
data_extraction_goal: "Extract {{ field_name }} from the results"
* Complex expressions: * Send previous block output to webhook:
navigation_goal: "Enter {{ first_name }} {{ last_name }} in the name field" blocks:
- block_type: task_v2
label: get_data
prompt: "Get top 3 hacker news items"
...
- block_type: http_request
label: send_webhook
method: POST
url: "http://example.com/webhook"
body:
data: "{{ get_data.output }}"
parameter_keys: [] # Empty - block outputs don't need to be declared
* Use extraction output in next block:
blocks:
- block_type: extraction
label: extract_items
...
- block_type: task_v2
label: process_items
prompt: "Process these items: {{ extract_items.output }}"
* In schemas (as descriptions): * In schemas (as descriptions):
data_schema: data_schema:

View File

@@ -204,7 +204,7 @@ async def copilot_call_llm(
if action_type == "REPLACE_WORKFLOW": if action_type == "REPLACE_WORKFLOW":
llm_workflow_yaml = action_data.get("workflow_yaml", "") llm_workflow_yaml = action_data.get("workflow_yaml", "")
try: try:
updated_workflow = await _process_workflow_yaml( updated_workflow = _process_workflow_yaml(
workflow_id=chat_request.workflow_id, workflow_id=chat_request.workflow_id,
workflow_permanent_id=chat_request.workflow_permanent_id, workflow_permanent_id=chat_request.workflow_permanent_id,
organization_id=organization_id, organization_id=organization_id,
@@ -228,7 +228,7 @@ async def copilot_call_llm(
debug_run_info_text=debug_run_info_text, debug_run_info_text=debug_run_info_text,
error=e, error=e,
) )
updated_workflow = await _process_workflow_yaml( updated_workflow = _process_workflow_yaml(
workflow_id=chat_request.workflow_id, workflow_id=chat_request.workflow_id,
workflow_permanent_id=chat_request.workflow_permanent_id, workflow_permanent_id=chat_request.workflow_permanent_id,
organization_id=organization_id, organization_id=organization_id,
@@ -298,7 +298,7 @@ async def _auto_correct_workflow_yaml(
return action_data.get("workflow_yaml", workflow_yaml) return action_data.get("workflow_yaml", workflow_yaml)
async def _process_workflow_yaml( def _process_workflow_yaml(
workflow_id: str, workflow_id: str,
workflow_permanent_id: str, workflow_permanent_id: str,
organization_id: str, organization_id: str,