From 9fa7c0c96e6449570948de4781c9238acf0c73cb Mon Sep 17 00:00:00 2001
From: Celal Zamanoglu <95054566+celalzamanoglu@users.noreply.github.com>
Date: Wed, 10 Dec 2025 00:07:43 +0300
Subject: [PATCH] show metadata for non-navigation blocks on workflow
parameters page (#4243)
---
.../workflowRun/WorkflowPostRunParameters.tsx | 146 ++++++++++++++++--
.../blockInfo/CodeBlockParameters.tsx | 57 +++++++
.../blockInfo/FileDownloadBlockParameters.tsx | 95 ++++++++++++
.../blockInfo/GotoUrlBlockParameters.tsx | 39 +++++
.../blockInfo/TextPromptBlockParameters.tsx | 86 +++++++++++
5 files changed, 407 insertions(+), 16 deletions(-)
create mode 100644 skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/CodeBlockParameters.tsx
create mode 100644 skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/FileDownloadBlockParameters.tsx
create mode 100644 skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/GotoUrlBlockParameters.tsx
create mode 100644 skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/TextPromptBlockParameters.tsx
diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowPostRunParameters.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowPostRunParameters.tsx
index 418bd2c6..1a272186 100644
--- a/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowPostRunParameters.tsx
+++ b/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowPostRunParameters.tsx
@@ -1,3 +1,4 @@
+import { useMemo } from "react";
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
import { CodeEditor } from "../components/CodeEditor";
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
@@ -6,12 +7,21 @@ import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuer
import { isAction, isWorkflowRunBlock } from "../types/workflowRunTypes";
import { findBlockSurroundingAction } from "./workflowTimelineUtils";
import { TaskBlockParameters } from "./TaskBlockParameters";
-import { isTaskVariantBlock, WorkflowBlockTypes } from "../types/workflowTypes";
+import {
+ isTaskVariantBlock,
+ WorkflowBlockTypes,
+ type WorkflowBlock,
+ type WorkflowBlockType,
+} from "../types/workflowTypes";
import { Input } from "@/components/ui/input";
import { ProxySelector } from "@/components/ProxySelector";
import { SendEmailBlockParameters } from "./blockInfo/SendEmailBlockInfo";
import { ProxyLocation } from "@/api/types";
import { KeyValueInput } from "@/components/KeyValueInput";
+import { CodeBlockParameters } from "./blockInfo/CodeBlockParameters";
+import { TextPromptBlockParameters } from "./blockInfo/TextPromptBlockParameters";
+import { GotoUrlBlockParameters } from "./blockInfo/GotoUrlBlockParameters";
+import { FileDownloadBlockParameters } from "./blockInfo/FileDownloadBlockParameters";
function WorkflowPostRunParameters() {
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
@@ -20,14 +30,7 @@ function WorkflowPostRunParameters() {
const { data: workflowRun, isLoading: workflowRunIsLoading } =
useWorkflowRunWithWorkflowQuery();
const parameters = workflowRun?.parameters ?? {};
-
- if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
- return
Loading workflow parameters...
;
- }
-
- if (!workflowRun || !workflowRunTimeline) {
- return null;
- }
+ const workflow = workflowRun?.workflow;
function getActiveBlock() {
if (!workflowRunTimeline) {
@@ -45,19 +48,37 @@ function WorkflowPostRunParameters() {
}
const activeBlock = getActiveBlock();
- const isTaskV2 = workflowRun.task_v2 !== null;
+ const activeBlockLabel = activeBlock?.label ?? null;
+ const definitionBlock = useMemo(() => {
+ if (!workflow || !activeBlockLabel) {
+ return null;
+ }
+ return findWorkflowBlockByLabel(
+ workflow.workflow_definition.blocks,
+ activeBlockLabel,
+ );
+ }, [workflow, activeBlockLabel]);
+ const isTaskV2 = Boolean(workflowRun?.task_v2);
const webhookCallbackUrl = isTaskV2
- ? workflowRun.task_v2?.webhook_callback_url
- : workflowRun.webhook_callback_url;
+ ? workflowRun?.task_v2?.webhook_callback_url ?? null
+ : workflowRun?.webhook_callback_url ?? null;
const proxyLocation = isTaskV2
- ? workflowRun.task_v2?.proxy_location
- : workflowRun.proxy_location;
+ ? workflowRun?.task_v2?.proxy_location ?? null
+ : workflowRun?.proxy_location ?? null;
const extraHttpHeaders = isTaskV2
- ? workflowRun.task_v2?.extra_http_headers
- : workflowRun.extra_http_headers;
+ ? workflowRun?.task_v2?.extra_http_headers ?? null
+ : workflowRun?.extra_http_headers ?? null;
+
+ if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
+ return Loading workflow parameters...
;
+ }
+
+ if (!workflowRun || !workflowRunTimeline) {
+ return null;
+ }
return (
@@ -70,6 +91,27 @@ function WorkflowPostRunParameters() {
) : null}
{activeBlock &&
+ activeBlock.block_type === WorkflowBlockTypes.FileDownload &&
+ isBlockOfType(definitionBlock, WorkflowBlockTypes.FileDownload) ? (
+
+
+
File Download Settings
+
+
+
+ ) : null}
+ {activeBlock &&
activeBlock.block_type === WorkflowBlockTypes.SendEmail ? (
@@ -105,6 +147,50 @@ function WorkflowPostRunParameters() {
) : null}
+ {activeBlock &&
+ activeBlock.block_type === WorkflowBlockTypes.Code &&
+ isBlockOfType(definitionBlock, WorkflowBlockTypes.Code) ? (
+
+ ) : null}
+ {activeBlock &&
+ activeBlock.block_type === WorkflowBlockTypes.TextPrompt &&
+ isBlockOfType(definitionBlock, WorkflowBlockTypes.TextPrompt) ? (
+
+
+
Text Prompt Block
+
+
+
+ ) : null}
+ {activeBlock && activeBlock.block_type === WorkflowBlockTypes.URL ? (
+
+
+
Go To URL Block
+
+
+
+ ) : null}
Workflow Input Parameters
@@ -192,3 +278,31 @@ function WorkflowPostRunParameters() {
}
export { WorkflowPostRunParameters };
+
+function findWorkflowBlockByLabel(
+ blocks: Array
,
+ label: string,
+): WorkflowBlock | null {
+ for (const block of blocks) {
+ if (block.label === label) {
+ return block;
+ }
+ if (
+ block.block_type === WorkflowBlockTypes.ForLoop &&
+ block.loop_blocks.length > 0
+ ) {
+ const nested = findWorkflowBlockByLabel(block.loop_blocks, label);
+ if (nested) {
+ return nested;
+ }
+ }
+ }
+ return null;
+}
+
+function isBlockOfType(
+ block: WorkflowBlock | null,
+ type: T,
+): block is Extract {
+ return block?.block_type === type;
+}
diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/CodeBlockParameters.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/CodeBlockParameters.tsx
new file mode 100644
index 00000000..1d254982
--- /dev/null
+++ b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/CodeBlockParameters.tsx
@@ -0,0 +1,57 @@
+import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
+import type { WorkflowParameter } from "@/routes/workflows/types/workflowTypes";
+
+type Props = {
+ code: string;
+ parameters?: Array;
+};
+
+function CodeBlockParameters({ code, parameters }: Props) {
+ return (
+
+
+
+
Code
+
+ The Python snippet executed for this block
+
+
+
+
+ {parameters && parameters.length > 0 ? (
+
+
+
Parameters
+
+ Inputs passed to this code block
+
+
+
+ {parameters.map((parameter) => (
+
+
{parameter.key}
+ {parameter.description ? (
+
+ {parameter.description}
+
+ ) : null}
+
+ ))}
+
+
+ ) : null}
+
+ );
+}
+
+export { CodeBlockParameters };
diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/FileDownloadBlockParameters.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/FileDownloadBlockParameters.tsx
new file mode 100644
index 00000000..b7925de3
--- /dev/null
+++ b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/FileDownloadBlockParameters.tsx
@@ -0,0 +1,95 @@
+import { Input } from "@/components/ui/input";
+import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
+
+type Props = {
+ prompt?: string | null;
+ downloadSuffix?: string | null;
+ downloadTimeout?: number | null;
+ errorCodeMapping?: Record | null;
+ maxRetries?: number | null;
+ maxStepsPerRun?: number | null;
+};
+
+function FileDownloadBlockParameters({
+ prompt,
+ downloadSuffix,
+ downloadTimeout,
+ errorCodeMapping,
+ maxRetries,
+ maxStepsPerRun,
+}: Props) {
+ const formattedErrorCodeMapping = errorCodeMapping
+ ? JSON.stringify(errorCodeMapping, null, 2)
+ : null;
+
+ return (
+
+ {prompt ? (
+
+
+
Prompt
+
+ Instructions followed to download the file
+
+
+
+
+ ) : null}
+ {downloadSuffix ? (
+
+
+
Download Suffix
+
+ Expected suffix or filename for the downloaded file
+
+
+
+
+ ) : null}
+ {typeof downloadTimeout === "number" ? (
+
+
+
Download Timeout
+ In seconds
+
+
+
+ ) : null}
+ {typeof maxRetries === "number" ? (
+
+ ) : null}
+ {typeof maxStepsPerRun === "number" ? (
+
+
+
Max Steps Per Run
+
+
+
+ ) : null}
+ {formattedErrorCodeMapping ? (
+
+
+
Error Code Mapping
+
+
+
+ ) : null}
+ {!downloadSuffix &&
+ typeof downloadTimeout !== "number" &&
+ typeof maxRetries !== "number" &&
+ typeof maxStepsPerRun !== "number" &&
+ !formattedErrorCodeMapping ? (
+
+ No additional download-specific metadata configured for this block.
+
+ ) : null}
+
+ );
+}
+
+export { FileDownloadBlockParameters };
diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/GotoUrlBlockParameters.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/GotoUrlBlockParameters.tsx
new file mode 100644
index 00000000..28ef528c
--- /dev/null
+++ b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/GotoUrlBlockParameters.tsx
@@ -0,0 +1,39 @@
+import { Input } from "@/components/ui/input";
+import { Switch } from "@/components/ui/switch";
+
+type Props = {
+ url: string;
+ continueOnFailure: boolean;
+};
+
+function GotoUrlBlockParameters({ url, continueOnFailure }: Props) {
+ return (
+
+
+
+
URL
+
+ The destination Skyvern navigates to
+
+
+
+
+
+
+
Continue on Failure
+
+ Whether to continue if navigation fails
+
+
+
+
+
+ {continueOnFailure ? "Enabled" : "Disabled"}
+
+
+
+
+ );
+}
+
+export { GotoUrlBlockParameters };
diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/TextPromptBlockParameters.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/TextPromptBlockParameters.tsx
new file mode 100644
index 00000000..6275871d
--- /dev/null
+++ b/skyvern-frontend/src/routes/workflows/workflowRun/blockInfo/TextPromptBlockParameters.tsx
@@ -0,0 +1,86 @@
+import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
+import { Input } from "@/components/ui/input";
+import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
+import type { WorkflowParameter } from "@/routes/workflows/types/workflowTypes";
+
+type Props = {
+ prompt: string;
+ llmKey?: string | null;
+ jsonSchema?: Record | string | null;
+ parameters?: Array;
+};
+
+function TextPromptBlockParameters({
+ prompt,
+ llmKey,
+ jsonSchema,
+ parameters,
+}: Props) {
+ return (
+
+
+
+
Prompt
+
+ Instructions passed to the selected LLM
+
+
+
+
+ {llmKey ? (
+
+ ) : null}
+ {jsonSchema ? (
+
+
+
JSON Schema
+
+ Expected shape of the model response
+
+
+
+
+ ) : null}
+ {parameters && parameters.length > 0 ? (
+
+
+
Parameters
+
+
+ {parameters.map((parameter) => (
+
+
{parameter.key}
+ {parameter.description ? (
+
+ {parameter.description}
+
+ ) : null}
+
+ ))}
+
+
+ ) : null}
+
+ );
+}
+
+export { TextPromptBlockParameters };