diff --git a/skyvern-frontend/src/assets/promptBoxBg.png b/skyvern-frontend/src/assets/promptBoxBg.png new file mode 100644 index 00000000..82a4e8b0 Binary files /dev/null and b/skyvern-frontend/src/assets/promptBoxBg.png differ diff --git a/skyvern-frontend/src/components/SwitchBar.tsx b/skyvern-frontend/src/components/SwitchBar.tsx new file mode 100644 index 00000000..2a0e3fda --- /dev/null +++ b/skyvern-frontend/src/components/SwitchBar.tsx @@ -0,0 +1,42 @@ +import { cn } from "@/util/utils"; + +type Option = { + label: string; + value: string; +}; + +type Props = { + options: Option[]; + value: string; + onChange: (value: string) => void; +}; + +function SwitchBar({ options, value, onChange }: Props) { + return ( +
+ {options.map((option) => { + const selected = option.value === value; + return ( +
{ + if (!selected) { + onChange(option.value); + } + }} + > + {option.label} +
+ ); + })} +
+ ); +} + +export { SwitchBar }; diff --git a/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx b/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx new file mode 100644 index 00000000..56705ed3 --- /dev/null +++ b/skyvern-frontend/src/routes/tasks/create/PromptBox.tsx @@ -0,0 +1,200 @@ +import { getClient } from "@/api/AxiosClient"; +import { TaskGenerationApiResponse } from "@/api/types"; +import img from "@/assets/promptBoxBg.png"; +import { Textarea } from "@/components/ui/textarea"; +import { toast } from "@/components/ui/use-toast"; +import { useCredentialGetter } from "@/hooks/useCredentialGetter"; +import { PaperPlaneIcon, ReloadIcon } from "@radix-ui/react-icons"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { AxiosError } from "axios"; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { stringify as convertToYAML } from "yaml"; + +function createTaskFromTaskGenerationParameters( + values: TaskGenerationApiResponse, +) { + return { + url: values.url, + navigation_goal: values.navigation_goal, + data_extraction_goal: values.data_extraction_goal, + proxy_location: "RESIDENTIAL", + navigation_payload: values.navigation_payload, + extracted_information_schema: values.extracted_information_schema, + }; +} + +function createTemplateTaskFromTaskGenerationParameters( + values: TaskGenerationApiResponse, +) { + return { + title: values.suggested_title ?? "Untitled Task", + description: "", + is_saved_task: true, + webhook_callback_url: null, + proxy_location: "RESIDENTIAL", + workflow_definition: { + parameters: [ + { + parameter_type: "workflow", + workflow_parameter_type: "json", + key: "navigation_payload", + default_value: JSON.stringify(values.navigation_payload), + }, + ], + blocks: [ + { + block_type: "task", + label: values.suggested_title ?? "Untitled Task", + url: values.url, + navigation_goal: values.navigation_goal, + data_extraction_goal: values.data_extraction_goal, + data_schema: values.extracted_information_schema, + }, + ], + }, + }; +} + +const examplePrompts = [ + "What is the top post on hackernews?", + "Navigate to Google Finance and search for AAPL", +]; + +function PromptBox() { + const navigate = useNavigate(); + const [prompt, setPrompt] = useState(""); + const credentialGetter = useCredentialGetter(); + const queryClient = useQueryClient(); + + const getTaskFromPromptMutation = useMutation({ + mutationFn: async (prompt: string) => { + const client = await getClient(credentialGetter); + return client + .post< + { prompt: string }, + { data: TaskGenerationApiResponse } + >("/generate/task", { prompt }) + .then((response) => response.data); + }, + onError: (error: AxiosError) => { + toast({ + variant: "destructive", + title: "Error creating task from prompt", + description: error.message, + }); + }, + }); + + const saveTaskMutation = useMutation({ + mutationFn: async (params: TaskGenerationApiResponse) => { + const client = await getClient(credentialGetter); + const templateTask = + createTemplateTaskFromTaskGenerationParameters(params); + const yaml = convertToYAML(templateTask); + return client.post("/workflows", yaml, { + headers: { + "Content-Type": "text/plain", + }, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["savedTasks"], + }); + }, + onError: (error: AxiosError) => { + toast({ + variant: "destructive", + title: "Error saving task", + description: error.message, + }); + }, + }); + + const runTaskMutation = useMutation({ + mutationFn: async (params: TaskGenerationApiResponse) => { + const client = await getClient(credentialGetter); + const data = createTaskFromTaskGenerationParameters(params); + return client.post< + ReturnType, + { data: { task_id: string } } + >("/tasks", data); + }, + onSuccess: (response) => { + navigate(`/tasks/${response.data.task_id}/actions`); + }, + onError: (error: AxiosError) => { + toast({ + variant: "destructive", + title: "Error running task", + description: error.message, + }); + }, + }); + + return ( +
+
+
+ + What task would you like to accomplish? + +
+