SKY-5061: add model to workflow (#2529)
This commit is contained in:
@@ -233,6 +233,7 @@ function FlowRenderer({
|
||||
proxy_location: data.settings.proxyLocation,
|
||||
webhook_callback_url: data.settings.webhookCallbackUrl,
|
||||
persist_browser_session: data.settings.persistBrowserSession,
|
||||
model: data.settings.model,
|
||||
totp_verification_url: workflow.totp_verification_url,
|
||||
workflow_definition: {
|
||||
parameters: data.parameters,
|
||||
|
||||
@@ -58,6 +58,7 @@ function WorkflowEditor() {
|
||||
persistBrowserSession: workflow.persist_browser_session,
|
||||
proxyLocation: workflow.proxy_location,
|
||||
webhookCallbackUrl: workflow.webhook_callback_url,
|
||||
model: workflow.model,
|
||||
};
|
||||
|
||||
const elements = getElements(
|
||||
|
||||
@@ -35,6 +35,7 @@ import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { RunEngineSelector } from "@/components/EngineSelector";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
const urlTooltip =
|
||||
"The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off.";
|
||||
@@ -59,6 +60,7 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
||||
cacheActions: data.cacheActions,
|
||||
downloadSuffix: data.downloadSuffix,
|
||||
totpVerificationUrl: data.totpVerificationUrl,
|
||||
model: data.model,
|
||||
totpIdentifier: data.totpIdentifier,
|
||||
engine: data.engine,
|
||||
});
|
||||
@@ -175,6 +177,13 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
||||
<AccordionContent className="pl-6 pr-1 pt-1">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<ParametersMultiSelect
|
||||
availableOutputParameters={outputParameterKeys}
|
||||
parameters={data.parameterKeys}
|
||||
|
||||
@@ -33,6 +33,7 @@ export const actionNodeDefaultData: ActionNodeData = {
|
||||
continueOnFailure: false,
|
||||
cacheActions: false,
|
||||
engine: RunEngine.SkyvernV1,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isActionNode(node: Node): node is ActionNode {
|
||||
|
||||
@@ -25,4 +25,5 @@ export const codeBlockNodeDefaultData: CodeBlockNodeData = {
|
||||
code: codeLead,
|
||||
continueOnFailure: false,
|
||||
parameterKeys: null,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
@@ -13,4 +13,5 @@ export const downloadNodeDefaultData: DownloadNodeData = {
|
||||
label: "",
|
||||
url: SKYVERN_DOWNLOAD_DIRECTORY,
|
||||
continueOnFailure: false,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
@@ -34,6 +34,7 @@ import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { RunEngineSelector } from "@/components/EngineSelector";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -50,6 +51,7 @@ function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
||||
continueOnFailure: data.continueOnFailure,
|
||||
cacheActions: data.cacheActions,
|
||||
engine: data.engine,
|
||||
model: data.model,
|
||||
});
|
||||
const deleteNodeCallback = useDeleteNodeCallback();
|
||||
const nodes = useNodes<AppNode>();
|
||||
@@ -153,6 +155,13 @@ function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
||||
<AccordionContent className="pl-6 pr-1 pt-1">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<ParametersMultiSelect
|
||||
availableOutputParameters={outputParameterKeys}
|
||||
parameters={data.parameterKeys}
|
||||
|
||||
@@ -27,6 +27,7 @@ export const extractionNodeDefaultData: ExtractionNodeData = {
|
||||
continueOnFailure: false,
|
||||
cacheActions: false,
|
||||
engine: RunEngine.SkyvernV1,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isExtractionNode(node: Node): node is ExtractionNode {
|
||||
|
||||
@@ -34,6 +34,7 @@ import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { RunEngineSelector } from "@/components/EngineSelector";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
const urlTooltip =
|
||||
"The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off.";
|
||||
@@ -60,6 +61,7 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
||||
totpVerificationUrl: data.totpVerificationUrl,
|
||||
totpIdentifier: data.totpIdentifier,
|
||||
engine: data.engine,
|
||||
model: data.model,
|
||||
});
|
||||
const deleteNodeCallback = useDeleteNodeCallback();
|
||||
|
||||
@@ -167,6 +169,13 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
||||
<AccordionContent className="pl-6 pr-1 pt-1">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<ParametersMultiSelect
|
||||
availableOutputParameters={outputParameterKeys}
|
||||
parameters={data.parameterKeys}
|
||||
|
||||
@@ -33,6 +33,7 @@ export const fileDownloadNodeDefaultData: FileDownloadNodeData = {
|
||||
continueOnFailure: false,
|
||||
cacheActions: false,
|
||||
engine: RunEngine.SkyvernV1,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isFileDownloadNode(node: Node): node is FileDownloadNode {
|
||||
|
||||
@@ -12,4 +12,5 @@ export const fileParserNodeDefaultData: FileParserNodeData = {
|
||||
label: "",
|
||||
fileUrl: "",
|
||||
continueOnFailure: false,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
@@ -23,4 +23,5 @@ export const fileUploadNodeDefaultData: FileUploadNodeData = {
|
||||
awsSecretAccessKey: "",
|
||||
regionName: "",
|
||||
continueOnFailure: false,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
@@ -36,6 +36,8 @@ import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { LoginBlockCredentialSelector } from "./LoginBlockCredentialSelector";
|
||||
import { RunEngineSelector } from "@/components/EngineSelector";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
const { editable } = data;
|
||||
@@ -55,6 +57,7 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
||||
completeCriterion: data.completeCriterion,
|
||||
terminateCriterion: data.terminateCriterion,
|
||||
engine: data.engine,
|
||||
model: data.model,
|
||||
});
|
||||
const deleteNodeCallback = useDeleteNodeCallback();
|
||||
|
||||
@@ -177,6 +180,13 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
||||
<AccordionContent className="pl-6 pr-1 pt-1">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<ParametersMultiSelect
|
||||
availableOutputParameters={outputParameterKeys}
|
||||
parameters={data.parameterKeys}
|
||||
|
||||
@@ -36,6 +36,7 @@ export const loginNodeDefaultData: LoginNodeData = {
|
||||
completeCriterion: "",
|
||||
terminateCriterion: "",
|
||||
engine: RunEngine.SkyvernV1,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isLoginNode(node: Node): node is LoginNode {
|
||||
|
||||
@@ -16,6 +16,7 @@ export const loopNodeDefaultData: LoopNodeData = {
|
||||
loopVariableReference: "",
|
||||
completeIfEmpty: false,
|
||||
continueOnFailure: false,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isLoopNode(node: Node): node is LoopNode {
|
||||
|
||||
@@ -36,6 +36,7 @@ import { AppNode } from "..";
|
||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { RunEngineSelector } from "@/components/EngineSelector";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -58,6 +59,7 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
||||
completeCriterion: data.completeCriterion,
|
||||
terminateCriterion: data.terminateCriterion,
|
||||
engine: data.engine,
|
||||
model: data.model,
|
||||
includeActionHistoryInVerification: data.includeActionHistoryInVerification,
|
||||
});
|
||||
const deleteNodeCallback = useDeleteNodeCallback();
|
||||
@@ -198,6 +200,13 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
||||
/>
|
||||
</div>
|
||||
<Separator />
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex gap-2">
|
||||
<Label className="text-xs font-normal text-slate-300">
|
||||
|
||||
@@ -29,6 +29,7 @@ export const navigationNodeDefaultData: NavigationNodeData = {
|
||||
completeCriterion: "",
|
||||
terminateCriterion: "",
|
||||
errorCodeMapping: "null",
|
||||
model: { model: "" },
|
||||
engine: RunEngine.SkyvernV1,
|
||||
maxRetries: null,
|
||||
maxStepsOverride: null,
|
||||
|
||||
@@ -15,6 +15,7 @@ export const pdfParserNodeDefaultData: PDFParserNodeData = {
|
||||
fileUrl: "",
|
||||
continueOnFailure: false,
|
||||
jsonSchema: "null",
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isPdfParserNode(node: AppNode): node is PDFParserNode {
|
||||
|
||||
@@ -36,4 +36,5 @@ export const sendEmailNodeDefaultData: SendEmailNodeData = {
|
||||
smtpUsernameSecretParameterKey: SMTP_USERNAME_PARAMETER_KEY,
|
||||
smtpPasswordSecretParameterKey: SMTP_PASSWORD_PARAMETER_KEY,
|
||||
continueOnFailure: false,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getClient } from "@/api/AxiosClient";
|
||||
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
||||
import type { StartNode } from "./types";
|
||||
import {
|
||||
@@ -8,15 +9,38 @@ import {
|
||||
} from "@/components/ui/accordion";
|
||||
import { useState } from "react";
|
||||
import { ProxyLocation } from "@/api/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { HelpTooltip } from "@/components/HelpTooltip";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { ProxySelector } from "@/components/ProxySelector";
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
} from "@/components/ui/select";
|
||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { ModelsResponse } from "@/api/types";
|
||||
|
||||
function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||
const credentialGetter = useCredentialGetter();
|
||||
const { updateNodeData } = useReactFlow();
|
||||
|
||||
const { data: availableModels } = useQuery<ModelsResponse>({
|
||||
queryKey: ["models"],
|
||||
queryFn: async () => {
|
||||
const client = await getClient(credentialGetter);
|
||||
|
||||
return client.get("/models").then((res) => res.data);
|
||||
},
|
||||
});
|
||||
|
||||
const models = availableModels?.models ?? [];
|
||||
|
||||
const [inputs, setInputs] = useState({
|
||||
webhookCallbackUrl: data.withWorkflowSettings
|
||||
? data.webhookCallbackUrl
|
||||
@@ -27,6 +51,7 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||
persistBrowserSession: data.withWorkflowSettings
|
||||
? data.persistBrowserSession
|
||||
: false,
|
||||
model: data.withWorkflowSettings ? data.model : { model: models[0] || "" },
|
||||
});
|
||||
|
||||
function handleChange(key: string, value: unknown) {
|
||||
@@ -57,6 +82,29 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="pl-6 pr-1 pt-1">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex gap-2">
|
||||
<Label>Model</Label>
|
||||
<HelpTooltip content="The LLM model to use for this workflow" />
|
||||
</div>
|
||||
<Select
|
||||
value={inputs.model?.model ?? "Skyvern Optimized"}
|
||||
onValueChange={(value) => {
|
||||
handleChange("model", { model: value });
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select model" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{models.map((m) => (
|
||||
<SelectItem key={m} value={m}>
|
||||
{m}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex gap-2">
|
||||
<Label>Webhook Callback URL</Label>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { ProxyLocation } from "@/api/types";
|
||||
import type { Node } from "@xyflow/react";
|
||||
import { AppNode } from "..";
|
||||
import { WorkflowModel } from "@/routes/workflows/types/workflowTypes";
|
||||
|
||||
export type WorkflowStartNodeData = {
|
||||
withWorkflowSettings: true;
|
||||
webhookCallbackUrl: string;
|
||||
proxyLocation: ProxyLocation;
|
||||
persistBrowserSession: boolean;
|
||||
model: WorkflowModel;
|
||||
editable: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import type { TaskNode } from "./types";
|
||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { RunEngineSelector } from "@/components/EngineSelector";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -69,6 +70,7 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||
totpIdentifier: data.totpIdentifier,
|
||||
includeActionHistoryInVerification: data.includeActionHistoryInVerification,
|
||||
engine: data.engine,
|
||||
model: data.model,
|
||||
});
|
||||
|
||||
function handleChange(key: string, value: unknown) {
|
||||
@@ -231,6 +233,13 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||
/>
|
||||
</div>
|
||||
<Separator />
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex gap-2">
|
||||
<Label className="text-xs font-normal text-slate-300">
|
||||
|
||||
@@ -45,6 +45,7 @@ export const taskNodeDefaultData: TaskNodeData = {
|
||||
cacheActions: false,
|
||||
includeActionHistoryInVerification: false,
|
||||
engine: RunEngine.SkyvernV1,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isTaskNode(node: Node): node is TaskNode {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { NodeActionMenu } from "../NodeActionMenu";
|
||||
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||
import { MAX_STEPS_DEFAULT, type Taskv2Node } from "./types";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function Taskv2Node({ id, data, type }: NodeProps<Taskv2Node>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -38,6 +39,7 @@ function Taskv2Node({ id, data, type }: NodeProps<Taskv2Node>) {
|
||||
totpVerificationUrl: data.totpVerificationUrl,
|
||||
totpIdentifier: data.totpIdentifier,
|
||||
maxSteps: data.maxSteps,
|
||||
model: data.model,
|
||||
});
|
||||
|
||||
function handleChange(key: string, value: unknown) {
|
||||
@@ -131,6 +133,13 @@ function Taskv2Node({ id, data, type }: NodeProps<Taskv2Node>) {
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="pl-6 pr-1 pt-4">
|
||||
<div className="space-y-4">
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<div className="flex gap-2">
|
||||
<Label className="text-xs text-slate-300">Max Steps</Label>
|
||||
|
||||
@@ -22,6 +22,7 @@ export const taskv2NodeDefaultData: Taskv2NodeData = {
|
||||
totpIdentifier: null,
|
||||
totpVerificationUrl: null,
|
||||
maxSteps: MAX_STEPS_DEFAULT,
|
||||
model: null,
|
||||
};
|
||||
|
||||
export function isTaskV2Node(node: Node): node is Taskv2Node {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTexta
|
||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||
import { dataSchemaExampleValue } from "../types";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -33,6 +34,7 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||
const [inputs, setInputs] = useState({
|
||||
prompt: data.prompt,
|
||||
jsonSchema: data.jsonSchema,
|
||||
model: data.model,
|
||||
});
|
||||
|
||||
const nodes = useNodes<AppNode>();
|
||||
@@ -127,6 +129,13 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||
/>
|
||||
</div>
|
||||
<Separator />
|
||||
<ModelSelector
|
||||
className="nopan w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<WorkflowDataSchemaInputGroup
|
||||
exampleValue={dataSchemaExampleValue}
|
||||
value={inputs.jsonSchema}
|
||||
|
||||
@@ -17,6 +17,7 @@ export const textPromptNodeDefaultData: TextPromptNodeData = {
|
||||
jsonSchema: "null",
|
||||
continueOnFailure: false,
|
||||
parameterKeys: [],
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
export function isTextPromptNode(node: AppNode): node is TextPromptNode {
|
||||
|
||||
@@ -12,6 +12,7 @@ export const urlNodeDefaultData: URLNodeData = {
|
||||
continueOnFailure: false,
|
||||
url: "",
|
||||
editable: true,
|
||||
model: null,
|
||||
};
|
||||
|
||||
export function isUrlNode(node: Node): node is URLNode {
|
||||
|
||||
@@ -14,4 +14,5 @@ export const uploadNodeDefaultData: UploadNodeData = {
|
||||
label: "",
|
||||
path: SKYVERN_DOWNLOAD_DIRECTORY,
|
||||
continueOnFailure: false,
|
||||
model: null,
|
||||
} as const;
|
||||
|
||||
@@ -33,6 +33,7 @@ import { AppNode } from "..";
|
||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||
import { ModelSelector } from "@/components/ModelSelector";
|
||||
|
||||
function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -45,6 +46,7 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
||||
completeCriterion: data.completeCriterion,
|
||||
terminateCriterion: data.terminateCriterion,
|
||||
errorCodeMapping: data.errorCodeMapping,
|
||||
model: data.model,
|
||||
});
|
||||
const deleteNodeCallback = useDeleteNodeCallback();
|
||||
const nodes = useNodes<AppNode>();
|
||||
@@ -139,6 +141,13 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
||||
<AccordionContent>
|
||||
<div className="ml-6 mt-4 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<ModelSelector
|
||||
className="nopan mr-[1px] w-52 text-xs"
|
||||
value={inputs.model}
|
||||
onChange={(value) => {
|
||||
handleChange("model", value);
|
||||
}}
|
||||
/>
|
||||
<ParametersMultiSelect
|
||||
availableOutputParameters={outputParameterKeys}
|
||||
parameters={data.parameterKeys}
|
||||
|
||||
@@ -18,6 +18,7 @@ export const validationNodeDefaultData: ValidationNodeData = {
|
||||
continueOnFailure: false,
|
||||
editable: true,
|
||||
parameterKeys: [],
|
||||
model: null,
|
||||
};
|
||||
|
||||
export function isValidationNode(node: Node): node is ValidationNode {
|
||||
|
||||
@@ -12,6 +12,7 @@ export const waitNodeDefaultData: WaitNodeData = {
|
||||
continueOnFailure: false,
|
||||
editable: true,
|
||||
waitInSeconds: "1",
|
||||
model: null,
|
||||
};
|
||||
|
||||
export function isWaitNode(node: Node): node is WaitNode {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { WorkflowBlockType } from "../../types/workflowTypes";
|
||||
import type { WorkflowModel } from "../../types/workflowTypes";
|
||||
|
||||
export type NodeBaseData = {
|
||||
label: string;
|
||||
continueOnFailure: boolean;
|
||||
editable: boolean;
|
||||
model: WorkflowModel | null;
|
||||
};
|
||||
|
||||
export const errorMappingExampleValue = {
|
||||
|
||||
@@ -100,6 +100,7 @@ import { taskv2NodeDefaultData } from "./nodes/Taskv2Node/types";
|
||||
import { urlNodeDefaultData } from "./nodes/URLNode/types";
|
||||
import { fileUploadNodeDefaultData } from "./nodes/FileUploadNode/types";
|
||||
export const NEW_NODE_LABEL_PREFIX = "block_";
|
||||
const DEFAULT_MODEL = { model: "Skyvern Optimized" };
|
||||
|
||||
function layoutUtil(
|
||||
nodes: Array<AppNode>,
|
||||
@@ -208,6 +209,7 @@ function convertToNode(
|
||||
label: block.label,
|
||||
continueOnFailure: block.continue_on_failure,
|
||||
editable,
|
||||
model: block.model,
|
||||
};
|
||||
switch (block.block_type) {
|
||||
case "task": {
|
||||
@@ -660,6 +662,7 @@ function getElements(
|
||||
persistBrowserSession: settings.persistBrowserSession,
|
||||
proxyLocation: settings.proxyLocation ?? ProxyLocation.Residential,
|
||||
webhookCallbackUrl: settings.webhookCallbackUrl ?? "",
|
||||
model: settings.model ?? DEFAULT_MODEL,
|
||||
editable,
|
||||
}),
|
||||
);
|
||||
@@ -958,6 +961,7 @@ function getWorkflowBlock(node: WorkflowBlockNode): BlockYAML {
|
||||
const base = {
|
||||
label: node.data.label,
|
||||
continue_on_failure: node.data.continueOnFailure,
|
||||
model: node.data.model,
|
||||
};
|
||||
switch (node.type) {
|
||||
case "task": {
|
||||
@@ -1318,6 +1322,7 @@ function getWorkflowSettings(nodes: Array<AppNode>): WorkflowSettings {
|
||||
persistBrowserSession: false,
|
||||
proxyLocation: ProxyLocation.Residential,
|
||||
webhookCallbackUrl: null,
|
||||
model: DEFAULT_MODEL,
|
||||
};
|
||||
const startNodes = nodes.filter(isStartNode);
|
||||
const startNodeWithWorkflowSettings = startNodes.find(
|
||||
@@ -1332,6 +1337,7 @@ function getWorkflowSettings(nodes: Array<AppNode>): WorkflowSettings {
|
||||
persistBrowserSession: data.persistBrowserSession,
|
||||
proxyLocation: data.proxyLocation,
|
||||
webhookCallbackUrl: data.webhookCallbackUrl,
|
||||
model: data.model,
|
||||
};
|
||||
}
|
||||
return defaultSettings;
|
||||
@@ -1778,6 +1784,7 @@ function convertBlocksToBlockYAML(
|
||||
url: block.url,
|
||||
title: block.title,
|
||||
engine: block.engine,
|
||||
model: block.model,
|
||||
navigation_goal: block.navigation_goal,
|
||||
error_code_mapping: block.error_code_mapping,
|
||||
max_retries: block.max_retries,
|
||||
@@ -1973,8 +1980,9 @@ function convert(workflow: WorkflowApiResponse): WorkflowCreateYAMLRequest {
|
||||
description: workflow.description,
|
||||
proxy_location: workflow.proxy_location,
|
||||
webhook_callback_url: workflow.webhook_callback_url,
|
||||
totp_verification_url: workflow.totp_verification_url,
|
||||
persist_browser_session: workflow.persist_browser_session,
|
||||
model: workflow.model,
|
||||
totp_verification_url: workflow.totp_verification_url,
|
||||
workflow_definition: {
|
||||
parameters: convertParametersToParameterYAML(userParameters),
|
||||
blocks: convertBlocksToBlockYAML(workflow.workflow_definition.blocks),
|
||||
|
||||
Reference in New Issue
Block a user