SKY-5061: add model to workflow (#2529)

This commit is contained in:
Shuchang Zheng
2025-05-29 15:06:31 -07:00
committed by GitHub
parent 0024af2fe8
commit 69a07c01ab
39 changed files with 260 additions and 1 deletions

View File

@@ -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,

View File

@@ -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(

View File

@@ -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}

View File

@@ -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 {

View File

@@ -25,4 +25,5 @@ export const codeBlockNodeDefaultData: CodeBlockNodeData = {
code: codeLead,
continueOnFailure: false,
parameterKeys: null,
model: null,
} as const;

View File

@@ -13,4 +13,5 @@ export const downloadNodeDefaultData: DownloadNodeData = {
label: "",
url: SKYVERN_DOWNLOAD_DIRECTORY,
continueOnFailure: false,
model: null,
} as const;

View File

@@ -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}

View File

@@ -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 {

View File

@@ -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}

View File

@@ -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 {

View File

@@ -12,4 +12,5 @@ export const fileParserNodeDefaultData: FileParserNodeData = {
label: "",
fileUrl: "",
continueOnFailure: false,
model: null,
} as const;

View File

@@ -23,4 +23,5 @@ export const fileUploadNodeDefaultData: FileUploadNodeData = {
awsSecretAccessKey: "",
regionName: "",
continueOnFailure: false,
model: null,
} as const;

View File

@@ -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}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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">

View File

@@ -29,6 +29,7 @@ export const navigationNodeDefaultData: NavigationNodeData = {
completeCriterion: "",
terminateCriterion: "",
errorCodeMapping: "null",
model: { model: "" },
engine: RunEngine.SkyvernV1,
maxRetries: null,
maxStepsOverride: null,

View File

@@ -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 {

View File

@@ -36,4 +36,5 @@ export const sendEmailNodeDefaultData: SendEmailNodeData = {
smtpUsernameSecretParameterKey: SMTP_USERNAME_PARAMETER_KEY,
smtpPasswordSecretParameterKey: SMTP_PASSWORD_PARAMETER_KEY,
continueOnFailure: false,
model: null,
} as const;

View File

@@ -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>

View File

@@ -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;
};

View File

@@ -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">

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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}

View File

@@ -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 {

View File

@@ -12,6 +12,7 @@ export const urlNodeDefaultData: URLNodeData = {
continueOnFailure: false,
url: "",
editable: true,
model: null,
};
export function isUrlNode(node: Node): node is URLNode {

View File

@@ -14,4 +14,5 @@ export const uploadNodeDefaultData: UploadNodeData = {
label: "",
path: SKYVERN_DOWNLOAD_DIRECTORY,
continueOnFailure: false,
model: null,
} as const;

View File

@@ -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}

View File

@@ -18,6 +18,7 @@ export const validationNodeDefaultData: ValidationNodeData = {
continueOnFailure: false,
editable: true,
parameterKeys: [],
model: null,
};
export function isValidationNode(node: Node): node is ValidationNode {

View File

@@ -12,6 +12,7 @@ export const waitNodeDefaultData: WaitNodeData = {
continueOnFailure: false,
editable: true,
waitInSeconds: "1",
model: null,
};
export function isWaitNode(node: Node): node is WaitNode {

View File

@@ -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 = {

View File

@@ -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),

View File

@@ -1,3 +1,4 @@
import { JsonObjectExtendable } from "@/types";
import { ProxyLocation, RunEngine } from "@/api/types";
export type WorkflowParameterBase = {
@@ -231,6 +232,7 @@ export type WorkflowBlockBase = {
block_type: WorkflowBlockType;
output_parameter: OutputParameter;
continue_on_failure: boolean;
model: WorkflowModel | null;
};
export type TaskBlock = WorkflowBlockBase & {
@@ -449,6 +451,7 @@ export type WorkflowApiResponse = {
proxy_location: ProxyLocation | null;
webhook_callback_url: string | null;
persist_browser_session: boolean;
model: WorkflowModel | null;
totp_verification_url: string | null;
totp_identifier: string | null;
created_at: string;
@@ -460,8 +463,11 @@ export type WorkflowSettings = {
proxyLocation: ProxyLocation | null;
webhookCallbackUrl: string | null;
persistBrowserSession: boolean;
model: WorkflowModel | null;
};
export type WorkflowModel = JsonObjectExtendable<{ model: string }>;
export function isOutputParameter(
parameter: Parameter,
): parameter is OutputParameter {

View File

@@ -1,5 +1,6 @@
import { RunEngine } from "@/api/types";
import { WorkflowBlockType } from "./workflowTypes";
import { WorkflowModel } from "./workflowTypes";
export type WorkflowCreateYAMLRequest = {
title: string;
@@ -7,6 +8,7 @@ export type WorkflowCreateYAMLRequest = {
proxy_location?: string | null;
webhook_callback_url?: string | null;
persist_browser_session?: boolean;
model?: WorkflowModel | null;
totp_verification_url?: string | null;
workflow_definition: WorkflowDefinitionYAML;
is_saved_task?: boolean;
@@ -189,6 +191,7 @@ export type NavigationBlockYAML = BlockYAMLBase & {
complete_criterion: string | null;
terminate_criterion: string | null;
engine: RunEngine | null;
model: WorkflowModel | null;
include_action_history_in_verification: boolean;
};