From 69687f121ad796ea32dd6262311e4ea6265ef0a8 Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Tue, 17 Dec 2024 10:17:00 -0800 Subject: [PATCH] Add observer in UI (#1409) --- skyvern-frontend/src/api/types.ts | 12 +++ skyvern-frontend/src/components/ui/select.tsx | 26 +++++ .../src/routes/tasks/create/PromptBox.tsx | 97 ++++++++++++++++++- skyvern-frontend/src/util/env.ts | 11 ++- 4 files changed, 141 insertions(+), 5 deletions(-) diff --git a/skyvern-frontend/src/api/types.ts b/skyvern-frontend/src/api/types.ts index d9c00a61..a897e2dd 100644 --- a/skyvern-frontend/src/api/types.ts +++ b/skyvern-frontend/src/api/types.ts @@ -233,3 +233,15 @@ export type ActionsApiResponse = { intention: string | null; response: string | null; }; + +export type ObserverCruise = { + observer_cruise_id: string; + status: Status; + workflow_run_id: string | null; + workflow_id: string | null; + workflow_permanent_id: string | null; + prompt: string | null; + url: string | null; + created_at: string; + modified_at: string; +}; diff --git a/skyvern-frontend/src/components/ui/select.tsx b/skyvern-frontend/src/components/ui/select.tsx index c6bd5582..0edc6949 100644 --- a/skyvern-frontend/src/components/ui/select.tsx +++ b/skyvern-frontend/src/components/ui/select.tsx @@ -114,6 +114,30 @@ const SelectLabel = React.forwardRef< )); SelectLabel.displayName = SelectPrimitive.Label.displayName; +const CustomSelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +CustomSelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectItemText = SelectPrimitive.ItemText; + const SelectItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -159,4 +183,6 @@ export { SelectSeparator, SelectScrollUpButton, SelectScrollDownButton, + CustomSelectItem, + SelectItemText, }; diff --git a/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx b/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx index f3f8913e..1a538cb1 100644 --- a/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx +++ b/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx @@ -1,5 +1,5 @@ import { getClient } from "@/api/AxiosClient"; -import { TaskGenerationApiResponse } from "@/api/types"; +import { ObserverCruise, TaskGenerationApiResponse } from "@/api/types"; import img from "@/assets/promptBoxBg.png"; import { CartIcon } from "@/components/icons/CartIcon"; import { GraphIcon } from "@/components/icons/GraphIcon"; @@ -7,9 +7,19 @@ import { InboxIcon } from "@/components/icons/InboxIcon"; import { MessageIcon } from "@/components/icons/MessageIcon"; import { TranslateIcon } from "@/components/icons/TranslateIcon"; import { TrophyIcon } from "@/components/icons/TrophyIcon"; +import { Button } from "@/components/ui/button"; +import { + CustomSelectItem, + Select, + SelectContent, + SelectItemText, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { toast } from "@/components/ui/use-toast"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; +import { observerFeatureEnabled } from "@/util/env"; import { FileTextIcon, GearIcon, @@ -18,10 +28,11 @@ import { PlusIcon, ReloadIcon, } from "@radix-ui/react-icons"; +import { ToastAction } from "@radix-ui/react-toast"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { AxiosError } from "axios"; import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { stringify as convertToYAML } from "yaml"; function createTemplateTaskFromTaskGenerationParameters( @@ -112,9 +123,53 @@ const exampleCases = [ function PromptBox() { const navigate = useNavigate(); const [prompt, setPrompt] = useState(""); + const [selectValue, setSelectValue] = useState( + observerFeatureEnabled ? "v2" : "v1", + ); const credentialGetter = useCredentialGetter(); const queryClient = useQueryClient(); + const startObserverCruiseMutation = useMutation({ + mutationFn: async (prompt: string) => { + const client = await getClient(credentialGetter); + return client.post<{ user_prompt: string }, { data: ObserverCruise }>( + "/cruise", + { user_prompt: prompt }, + ); + }, + onSuccess: (response) => { + toast({ + variant: "success", + title: "Workflow Run Created", + description: `Workflow run created successfully.`, + action: ( + + + + ), + }); + queryClient.invalidateQueries({ + queryKey: ["workflowRuns"], + }); + queryClient.invalidateQueries({ + queryKey: ["workflows"], + }); + }, + onError: (error: AxiosError) => { + toast({ + variant: "destructive", + title: "Error creating workflow run from prompt", + description: error.message, + }); + }, + }); + const getTaskFromPromptMutation = useMutation({ mutationFn: async (prompt: string) => { const client = await getClient(credentialGetter); @@ -180,14 +235,48 @@ function PromptBox() { placeholder="Enter your prompt..." rows={1} /> -
- {getTaskFromPromptMutation.isPending || + {observerFeatureEnabled && ( + + )} +
+ {startObserverCruiseMutation.isPending || + getTaskFromPromptMutation.isPending || saveTaskMutation.isPending ? ( ) : ( { + if (observerFeatureEnabled && selectValue === "v2") { + startObserverCruiseMutation.mutate(prompt); + return; + } const taskGenerationResponse = await getTaskFromPromptMutation.mutateAsync(prompt); await saveTaskMutation.mutateAsync(taskGenerationResponse); diff --git a/skyvern-frontend/src/util/env.ts b/skyvern-frontend/src/util/env.ts index d54adc00..19721eb6 100644 --- a/skyvern-frontend/src/util/env.ts +++ b/skyvern-frontend/src/util/env.ts @@ -19,4 +19,13 @@ if (!artifactApiBaseUrl) { console.warn("artifactApiBaseUrl environment variable was not set"); } -export { apiBaseUrl, environment, envCredential, artifactApiBaseUrl }; +const observerEnabled = import.meta.env.VITE_OBSERVER_ENABLED as string; +const observerFeatureEnabled = observerEnabled === "true"; + +export { + apiBaseUrl, + environment, + envCredential, + artifactApiBaseUrl, + observerFeatureEnabled, +};