From 58735a9c20b91bb3bd44a846e72f1d15e0074637 Mon Sep 17 00:00:00 2001 From: Kerem Yilmaz Date: Fri, 21 Jun 2024 13:08:00 -0700 Subject: [PATCH] Include max steps in saved task (#499) --- .../src/routes/tasks/constants.ts | 2 + .../routes/tasks/create/CreateNewTaskForm.tsx | 4 +- .../tasks/create/CreateNewTaskFormPage.tsx | 3 + .../src/routes/tasks/create/SavedTaskForm.tsx | 73 ++++++++++++++++++- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/skyvern-frontend/src/routes/tasks/constants.ts b/skyvern-frontend/src/routes/tasks/constants.ts index 5ef99a2b..25d3c012 100644 --- a/skyvern-frontend/src/routes/tasks/constants.ts +++ b/skyvern-frontend/src/routes/tasks/constants.ts @@ -1 +1,3 @@ export const PAGE_SIZE = 15; + +export const MAX_STEPS_DEFAULT = 10; diff --git a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx index d635f8ad..141077f5 100644 --- a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx +++ b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx @@ -46,6 +46,7 @@ import { } from "@/components/ui/accordion"; import { OrganizationApiResponse } from "@/api/types"; import { Skeleton } from "@/components/ui/skeleton"; +import { MAX_STEPS_DEFAULT } from "../constants"; const createNewTaskFormSchema = z .object({ @@ -94,7 +95,6 @@ function createTaskRequestObject(formValues: CreateNewTaskFormValues) { navigation_goal: transform(formValues.navigationGoal), data_extraction_goal: transform(formValues.dataExtractionGoal), proxy_location: "RESIDENTIAL", - error_code_mapping: null, navigation_payload: transform(formValues.navigationPayload), extracted_information_schema: transform( formValues.extractedInformationSchema, @@ -102,8 +102,6 @@ function createTaskRequestObject(formValues: CreateNewTaskFormValues) { }; } -const MAX_STEPS_DEFAULT = 10; - function CreateNewTaskForm({ initialValues }: Props) { const queryClient = useQueryClient(); const { toast } = useToast(); diff --git a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskFormPage.tsx b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskFormPage.tsx index f6d3d114..6070aa85 100644 --- a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskFormPage.tsx +++ b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskFormPage.tsx @@ -48,6 +48,8 @@ function CreateNewTaskFormPage() { const dataSchema = data.workflow_definition.blocks[0].data_schema; + const maxSteps = data.workflow_definition.blocks[0].max_steps_per_run; + return ( ); diff --git a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx index 3189abf6..7dd33556 100644 --- a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx +++ b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx @@ -24,7 +24,7 @@ import { apiBaseUrl } from "@/util/env"; import { zodResolver } from "@hookform/resolvers/zod"; import { InfoCircledIcon, ReloadIcon } from "@radix-ui/react-icons"; import { ToastAction } from "@radix-ui/react-toast"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import fetchToCurl from "fetch-to-curl"; import { useForm, useFormState } from "react-hook-form"; import { Link, useParams } from "react-router-dom"; @@ -46,6 +46,9 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; +import { OrganizationApiResponse } from "@/api/types"; +import { MAX_STEPS_DEFAULT } from "../constants"; +import { Skeleton } from "@/components/ui/skeleton"; const savedTaskFormSchema = z .object({ @@ -60,6 +63,7 @@ const savedTaskFormSchema = z dataExtractionGoal: z.string().or(z.null()).optional(), navigationPayload: z.string().or(z.null()).optional(), extractedInformationSchema: z.string().or(z.null()).optional(), + maxSteps: z.number().optional(), }) .superRefine( ( @@ -154,6 +158,7 @@ function createTaskTemplateRequestObject(values: SavedTaskFormValues) { navigation_goal: values.navigationGoal, data_extraction_goal: values.dataExtractionGoal, data_schema: extractedInformationSchema, + max_steps_per_run: values.maxSteps, }, ], }, @@ -167,9 +172,30 @@ function SavedTaskForm({ initialValues }: Props) { const apiCredential = useApiCredential(); const { template } = useParams(); + const { data: organizations, isPending: organizationIsPending } = useQuery< + Array + >({ + queryKey: ["organizations"], + queryFn: async () => { + const client = await getClient(credentialGetter); + return await client + .get("/organizations") + .then((response) => response.data.organizations); + }, + }); + + const organization = organizations?.[0]; + const form = useForm({ resolver: zodResolver(savedTaskFormSchema), defaultValues: initialValues, + values: { + ...initialValues, + maxSteps: + initialValues.maxSteps ?? + organization?.max_steps_per_run ?? + MAX_STEPS_DEFAULT, + }, }); const { isDirty } = useFormState({ control: form.control }); @@ -178,10 +204,19 @@ function SavedTaskForm({ initialValues }: Props) { mutationFn: async (formValues: SavedTaskFormValues) => { const taskRequest = createTaskRequestObject(formValues); const client = await getClient(credentialGetter); + const includeOverrideHeader = + formValues.maxSteps !== organization?.max_steps_per_run && + formValues.maxSteps !== MAX_STEPS_DEFAULT; return client.post< ReturnType, { data: { task_id: string } } - >("/tasks", taskRequest); + >("/tasks", taskRequest, { + ...(includeOverrideHeader && { + headers: { + "x-max-steps-override": formValues.maxSteps ?? MAX_STEPS_DEFAULT, + }, + }), + }); }, onError: (error: AxiosError) => { if (error.response?.status === 402) { @@ -516,6 +551,40 @@ function SavedTaskForm({ initialValues }: Props) { )} /> + { + return ( + + Max Steps + + Max steps for this task. This will override your + organization wide setting. + + + {organizationIsPending ? ( + + ) : ( + { + field.onChange(parseInt(event.target.value)); + }} + /> + )} + + + ); + }} + />