diff --git a/apps/shinkai-desktop/src/components/playground-tool/components/tools-selection.tsx b/apps/shinkai-desktop/src/components/playground-tool/components/tools-selection.tsx index 694fac0ff..94a03e1a9 100644 --- a/apps/shinkai-desktop/src/components/playground-tool/components/tools-selection.tsx +++ b/apps/shinkai-desktop/src/components/playground-tool/components/tools-selection.tsx @@ -22,9 +22,9 @@ import { UseFormReturn } from 'react-hook-form'; import { Link } from 'react-router-dom'; import { toast } from 'sonner'; -import { CreateToolCodeFormSchema } from '../../../pages/create-tool'; import { useAuth } from '../../../store/auth'; import { actionButtonClassnames } from '../../chat/conversation-footer'; +import { CreateToolCodeFormSchema } from '../hooks/use-tool-code'; export function ToolsSelection({ form, diff --git a/apps/shinkai-desktop/src/components/tools/python-tool.tsx b/apps/shinkai-desktop/src/components/tools/python-tool.tsx index 8da77e919..641c63007 100644 --- a/apps/shinkai-desktop/src/components/tools/python-tool.tsx +++ b/apps/shinkai-desktop/src/components/tools/python-tool.tsx @@ -173,7 +173,7 @@ export default function PythonTool({ onCheckedChange={async () => { await updateTool({ toolKey: toolKey ?? '', - toolType: 'Deno', + toolType: 'Python', toolPayload: {} as ShinkaiTool, isToolEnabled: !isEnabled, nodeAddress: auth?.node_address ?? '', diff --git a/apps/shinkai-desktop/src/pages/create-task.tsx b/apps/shinkai-desktop/src/pages/create-task.tsx new file mode 100644 index 000000000..9b09d75d9 --- /dev/null +++ b/apps/shinkai-desktop/src/pages/create-task.tsx @@ -0,0 +1,531 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { useTranslation } from '@shinkai_network/shinkai-i18n'; +import { useCreateRecurringTask } from '@shinkai_network/shinkai-node-state/v2/mutations/createRecurringTask/useCreateRecurringTask'; +import { useGetTools } from '@shinkai_network/shinkai-node-state/v2/queries/getToolsList/useGetToolsList'; +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + HoverCard, + HoverCardContent, + HoverCardTrigger, + Label, + Slider, + Switch, + Textarea, + TextField, +} from '@shinkai_network/shinkai-ui'; +import { + ScheduledTasksIcon, + ToolsIcon, +} from '@shinkai_network/shinkai-ui/assets'; +import { formatText } from '@shinkai_network/shinkai-ui/helpers'; +import { cn } from '@shinkai_network/shinkai-ui/utils'; +import cronstrue from 'cronstrue'; +import { BotIcon, ChevronDownIcon } from 'lucide-react'; +import { useEffect, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom'; +import { toast } from 'sonner'; +import { z } from 'zod'; + +import { AIModelSelector } from '../components/chat/chat-action-bar/ai-update-selection-action-bar'; +import { actionButtonClassnames } from '../components/chat/conversation-footer'; +import { useAuth } from '../store/auth'; +import { useSettings } from '../store/settings'; +import { SubpageLayout } from './layout/simple-layout'; + +const createTaskFormSchema = z.object({ + name: z.string(), + description: z.string(), + llmOrAgentId: z.string(), + cronExpression: z.string().refine( + (value) => { + try { + cronstrue.toString(value, { + throwExceptionOnParseError: true, + }); + return true; + } catch (error) { + return false; + } + }, + { + message: + 'Invalid cron expression. Please provide a valid cron expression.', + }, + ), + jobConfig: z.object({ + custom_system_prompt: z.string().optional(), + custom_prompt: z.string(), + temperature: z.number(), + max_tokens: z.number().optional(), + seed: z.number().optional(), + top_k: z.number(), + top_p: z.number(), + stream: z.boolean().optional(), + use_tools: z.boolean().optional(), + }), + jobMessage: z.object({ + content: z.string(), + toolKey: z.string().optional(), + }), +}); +type CreateTaskForm = z.infer; + +function CreateTaskPage() { + const { t } = useTranslation(); + const defaultAgentId = useSettings((state) => state.defaultAgentId); + const navigate = useNavigate(); + const auth = useAuth((state) => state.auth); + const form = useForm({ + resolver: zodResolver(createTaskFormSchema), + defaultValues: { + name: 'Hacker News Task', + description: 'Hacker News yada yada', + cronExpression: '* * * * *', + jobConfig: { + custom_system_prompt: '', + custom_prompt: '', + temperature: 0.5, + max_tokens: 100, + seed: 0, + top_k: 50, + top_p: 1, + stream: false, + use_tools: false, + }, + llmOrAgentId: defaultAgentId, + jobMessage: { + content: 'Search in duckduckgo about top hacker news stories', + toolKey: + 'local:::shinkai_tool_duckduckgo_search:::shinkai__duckduckgo_search', + }, + }, + }); + + const { data: toolsList, isSuccess: isToolListSuccess } = useGetTools({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + }); + + const { mutateAsync: createRecurringTask } = useCreateRecurringTask({ + onSuccess: () => { + toast.success('Task created successfully'); + navigate('/tasks'); + }, + onError: (error) => { + toast.error('Failed to create task', { + description: error.response?.data?.message ?? error.message, + }); + }, + }); + + const submit = async (values: CreateTaskForm) => { + await createRecurringTask({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + cronExpression: values.cronExpression, + chatConfig: values.jobConfig, + message: values.jobMessage.content, + toolRouterKey: values.jobMessage.toolKey, + llmProvider: values.llmOrAgentId, + name: values.name, + description: values.description, + }); + }; + + useEffect(() => { + if (defaultAgentId) { + form.setValue('llmOrAgentId', defaultAgentId); + } + }, [defaultAgentId]); + + const currentCronExpression = form.watch('cronExpression'); + + const readableCronExpression = useMemo(() => { + const readableCron = cronstrue.toString(currentCronExpression, { + throwExceptionOnParseError: false, + }); + if (readableCron.toLowerCase().includes('error')) { + return null; + } + return readableCron; + }, [currentCronExpression, form]); + return ( + +

+ Schedule recurring tasks at a specified time +

+
+ +
+ ( + + )} + /> + + ( + + )} + /> + ( + + )} + /> + + ( + + )} + /> + {readableCronExpression && ( +
+ + + This cron will run {readableCronExpression.toLowerCase()}{' '} + + ({form.watch('cronExpression')}) + + +
+ )} +
+
+
+
+ + AI Model Configuration + + +
+
+ + Select AI/Agent + + { + form.setValue('llmOrAgentId', value); + }} + value={form.watch('llmOrAgentId')} + /> +
+
+ + Select Tool (optional) + + + + .icon]:rotate-180', + )} + > + + + {form.watch('jobMessage.toolKey') + ? formatText( + form + .watch('jobMessage.toolKey') + ?.split(':::')?.[2] ?? '', + ) + : 'Select Tool (optional)'} + + + + + { + form.setValue('jobMessage.toolKey', value); + }} + value={form.watch('jobMessage.toolKey')} + > + + Available Tools + + {isToolListSuccess && + toolsList.length > 0 && + toolsList?.map((tool) => ( + + +
+ + {formatText(tool.name)} + +
+
+ ))} +
+
+
+
+
+
+ ( + + System Prompt + +