show metadata for non-navigation blocks on workflow parameters page (#4243)
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { CodeEditor } from "../components/CodeEditor";
|
import { CodeEditor } from "../components/CodeEditor";
|
||||||
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
|
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
|
||||||
@@ -6,12 +7,21 @@ import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuer
|
|||||||
import { isAction, isWorkflowRunBlock } from "../types/workflowRunTypes";
|
import { isAction, isWorkflowRunBlock } from "../types/workflowRunTypes";
|
||||||
import { findBlockSurroundingAction } from "./workflowTimelineUtils";
|
import { findBlockSurroundingAction } from "./workflowTimelineUtils";
|
||||||
import { TaskBlockParameters } from "./TaskBlockParameters";
|
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 { Input } from "@/components/ui/input";
|
||||||
import { ProxySelector } from "@/components/ProxySelector";
|
import { ProxySelector } from "@/components/ProxySelector";
|
||||||
import { SendEmailBlockParameters } from "./blockInfo/SendEmailBlockInfo";
|
import { SendEmailBlockParameters } from "./blockInfo/SendEmailBlockInfo";
|
||||||
import { ProxyLocation } from "@/api/types";
|
import { ProxyLocation } from "@/api/types";
|
||||||
import { KeyValueInput } from "@/components/KeyValueInput";
|
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() {
|
function WorkflowPostRunParameters() {
|
||||||
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
||||||
@@ -20,14 +30,7 @@ function WorkflowPostRunParameters() {
|
|||||||
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
||||||
useWorkflowRunWithWorkflowQuery();
|
useWorkflowRunWithWorkflowQuery();
|
||||||
const parameters = workflowRun?.parameters ?? {};
|
const parameters = workflowRun?.parameters ?? {};
|
||||||
|
const workflow = workflowRun?.workflow;
|
||||||
if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
|
|
||||||
return <div>Loading workflow parameters...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!workflowRun || !workflowRunTimeline) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getActiveBlock() {
|
function getActiveBlock() {
|
||||||
if (!workflowRunTimeline) {
|
if (!workflowRunTimeline) {
|
||||||
@@ -45,19 +48,37 @@ function WorkflowPostRunParameters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const activeBlock = getActiveBlock();
|
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
|
const webhookCallbackUrl = isTaskV2
|
||||||
? workflowRun.task_v2?.webhook_callback_url
|
? workflowRun?.task_v2?.webhook_callback_url ?? null
|
||||||
: workflowRun.webhook_callback_url;
|
: workflowRun?.webhook_callback_url ?? null;
|
||||||
|
|
||||||
const proxyLocation = isTaskV2
|
const proxyLocation = isTaskV2
|
||||||
? workflowRun.task_v2?.proxy_location
|
? workflowRun?.task_v2?.proxy_location ?? null
|
||||||
: workflowRun.proxy_location;
|
: workflowRun?.proxy_location ?? null;
|
||||||
|
|
||||||
const extraHttpHeaders = isTaskV2
|
const extraHttpHeaders = isTaskV2
|
||||||
? workflowRun.task_v2?.extra_http_headers
|
? workflowRun?.task_v2?.extra_http_headers ?? null
|
||||||
: workflowRun.extra_http_headers;
|
: workflowRun?.extra_http_headers ?? null;
|
||||||
|
|
||||||
|
if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
|
||||||
|
return <div>Loading workflow parameters...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!workflowRun || !workflowRunTimeline) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
@@ -70,6 +91,27 @@ function WorkflowPostRunParameters() {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{activeBlock &&
|
{activeBlock &&
|
||||||
|
activeBlock.block_type === WorkflowBlockTypes.FileDownload &&
|
||||||
|
isBlockOfType(definitionBlock, WorkflowBlockTypes.FileDownload) ? (
|
||||||
|
<div className="rounded bg-slate-elevation2 p-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="text-lg font-bold">File Download Settings</h1>
|
||||||
|
<FileDownloadBlockParameters
|
||||||
|
prompt={
|
||||||
|
activeBlock.navigation_goal ??
|
||||||
|
definitionBlock.navigation_goal ??
|
||||||
|
null
|
||||||
|
}
|
||||||
|
downloadSuffix={definitionBlock.download_suffix ?? null}
|
||||||
|
downloadTimeout={definitionBlock.download_timeout ?? null}
|
||||||
|
errorCodeMapping={definitionBlock.error_code_mapping ?? null}
|
||||||
|
maxRetries={definitionBlock.max_retries ?? null}
|
||||||
|
maxStepsPerRun={definitionBlock.max_steps_per_run ?? null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{activeBlock &&
|
||||||
activeBlock.block_type === WorkflowBlockTypes.SendEmail ? (
|
activeBlock.block_type === WorkflowBlockTypes.SendEmail ? (
|
||||||
<div className="rounded bg-slate-elevation2 p-6">
|
<div className="rounded bg-slate-elevation2 p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -105,6 +147,50 @@ function WorkflowPostRunParameters() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{activeBlock &&
|
||||||
|
activeBlock.block_type === WorkflowBlockTypes.Code &&
|
||||||
|
isBlockOfType(definitionBlock, WorkflowBlockTypes.Code) ? (
|
||||||
|
<div className="rounded bg-slate-elevation2 p-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="text-lg font-bold">Code Block</h1>
|
||||||
|
<CodeBlockParameters
|
||||||
|
code={definitionBlock.code}
|
||||||
|
parameters={definitionBlock.parameters}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{activeBlock &&
|
||||||
|
activeBlock.block_type === WorkflowBlockTypes.TextPrompt &&
|
||||||
|
isBlockOfType(definitionBlock, WorkflowBlockTypes.TextPrompt) ? (
|
||||||
|
<div className="rounded bg-slate-elevation2 p-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="text-lg font-bold">Text Prompt Block</h1>
|
||||||
|
<TextPromptBlockParameters
|
||||||
|
prompt={activeBlock.prompt ?? definitionBlock.prompt ?? ""}
|
||||||
|
llmKey={definitionBlock.llm_key}
|
||||||
|
jsonSchema={definitionBlock.json_schema}
|
||||||
|
parameters={definitionBlock.parameters}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{activeBlock && activeBlock.block_type === WorkflowBlockTypes.URL ? (
|
||||||
|
<div className="rounded bg-slate-elevation2 p-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="text-lg font-bold">Go To URL Block</h1>
|
||||||
|
<GotoUrlBlockParameters
|
||||||
|
url={
|
||||||
|
activeBlock.url ??
|
||||||
|
(isBlockOfType(definitionBlock, WorkflowBlockTypes.URL)
|
||||||
|
? definitionBlock.url
|
||||||
|
: "")
|
||||||
|
}
|
||||||
|
continueOnFailure={activeBlock.continue_on_failure}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="rounded bg-slate-elevation2 p-6">
|
<div className="rounded bg-slate-elevation2 p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h1 className="text-lg font-bold">Workflow Input Parameters</h1>
|
<h1 className="text-lg font-bold">Workflow Input Parameters</h1>
|
||||||
@@ -192,3 +278,31 @@ function WorkflowPostRunParameters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export { WorkflowPostRunParameters };
|
export { WorkflowPostRunParameters };
|
||||||
|
|
||||||
|
function findWorkflowBlockByLabel(
|
||||||
|
blocks: Array<WorkflowBlock>,
|
||||||
|
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<T extends WorkflowBlockType>(
|
||||||
|
block: WorkflowBlock | null,
|
||||||
|
type: T,
|
||||||
|
): block is Extract<WorkflowBlock, { block_type: T }> {
|
||||||
|
return block?.block_type === type;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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<WorkflowParameter>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function CodeBlockParameters({ code, parameters }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Code</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
The Python snippet executed for this block
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<CodeEditor
|
||||||
|
className="w-full"
|
||||||
|
language="python"
|
||||||
|
value={code}
|
||||||
|
readOnly
|
||||||
|
minHeight="160px"
|
||||||
|
maxHeight="400px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{parameters && parameters.length > 0 ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Parameters</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
Inputs passed to this code block
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full flex-col gap-3">
|
||||||
|
{parameters.map((parameter) => (
|
||||||
|
<div
|
||||||
|
key={parameter.key}
|
||||||
|
className="rounded border border-slate-700/40 bg-slate-elevation3 p-3"
|
||||||
|
>
|
||||||
|
<p className="font-medium">{parameter.key}</p>
|
||||||
|
{parameter.description ? (
|
||||||
|
<p className="text-sm text-slate-400">
|
||||||
|
{parameter.description}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { CodeBlockParameters };
|
||||||
@@ -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<string, string> | 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 (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{prompt ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Prompt</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
Instructions followed to download the file
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea value={prompt} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{downloadSuffix ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Download Suffix</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
Expected suffix or filename for the downloaded file
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<Input value={downloadSuffix} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{typeof downloadTimeout === "number" ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Download Timeout</h1>
|
||||||
|
<h2 className="text-base text-slate-400">In seconds</h2>
|
||||||
|
</div>
|
||||||
|
<Input value={downloadTimeout.toString()} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{typeof maxRetries === "number" ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Max Retries</h1>
|
||||||
|
</div>
|
||||||
|
<Input value={maxRetries.toString()} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{typeof maxStepsPerRun === "number" ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Max Steps Per Run</h1>
|
||||||
|
</div>
|
||||||
|
<Input value={maxStepsPerRun.toString()} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{formattedErrorCodeMapping ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Error Code Mapping</h1>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea value={formattedErrorCodeMapping} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{!downloadSuffix &&
|
||||||
|
typeof downloadTimeout !== "number" &&
|
||||||
|
typeof maxRetries !== "number" &&
|
||||||
|
typeof maxStepsPerRun !== "number" &&
|
||||||
|
!formattedErrorCodeMapping ? (
|
||||||
|
<div className="text-sm text-slate-400">
|
||||||
|
No additional download-specific metadata configured for this block.
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { FileDownloadBlockParameters };
|
||||||
@@ -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 (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">URL</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
The destination Skyvern navigates to
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<Input value={url} readOnly />
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Continue on Failure</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
Whether to continue if navigation fails
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full items-center gap-3">
|
||||||
|
<Switch checked={continueOnFailure} disabled />
|
||||||
|
<span className="text-sm text-slate-400">
|
||||||
|
{continueOnFailure ? "Enabled" : "Disabled"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { GotoUrlBlockParameters };
|
||||||
@@ -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, unknown> | string | null;
|
||||||
|
parameters?: Array<WorkflowParameter>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function TextPromptBlockParameters({
|
||||||
|
prompt,
|
||||||
|
llmKey,
|
||||||
|
jsonSchema,
|
||||||
|
parameters,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Prompt</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
Instructions passed to the selected LLM
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea value={prompt} readOnly />
|
||||||
|
</div>
|
||||||
|
{llmKey ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">LLM Key</h1>
|
||||||
|
</div>
|
||||||
|
<Input value={llmKey} readOnly />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{jsonSchema ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">JSON Schema</h1>
|
||||||
|
<h2 className="text-base text-slate-400">
|
||||||
|
Expected shape of the model response
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<CodeEditor
|
||||||
|
className="w-full"
|
||||||
|
language="json"
|
||||||
|
value={
|
||||||
|
typeof jsonSchema === "string"
|
||||||
|
? jsonSchema
|
||||||
|
: JSON.stringify(jsonSchema, null, 2)
|
||||||
|
}
|
||||||
|
readOnly
|
||||||
|
minHeight="160px"
|
||||||
|
maxHeight="400px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{parameters && parameters.length > 0 ? (
|
||||||
|
<div className="flex gap-16">
|
||||||
|
<div className="w-80">
|
||||||
|
<h1 className="text-lg">Parameters</h1>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full flex-col gap-3">
|
||||||
|
{parameters.map((parameter) => (
|
||||||
|
<div
|
||||||
|
key={parameter.key}
|
||||||
|
className="rounded border border-slate-700/40 bg-slate-elevation3 p-3"
|
||||||
|
>
|
||||||
|
<p className="font-medium">{parameter.key}</p>
|
||||||
|
{parameter.description ? (
|
||||||
|
<p className="text-sm text-slate-400">
|
||||||
|
{parameter.description}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TextPromptBlockParameters };
|
||||||
Reference in New Issue
Block a user