From 9626f7e000d519d23772c19284ca730486b6cd47 Mon Sep 17 00:00:00 2001 From: paulclindo Date: Thu, 19 Dec 2024 18:51:49 -0500 Subject: [PATCH 1/4] feat: add active/inactive cron tasks --- apps/shinkai-desktop/src/pages/edit-task.tsx | 1 + apps/shinkai-desktop/src/pages/tasks.tsx | 49 +++++++++++++++++-- .../src/api/recurring-tasks/types.ts | 11 ++--- .../v2/mutations/updateRecurringTask/index.ts | 2 + .../v2/mutations/updateRecurringTask/types.ts | 1 + 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/apps/shinkai-desktop/src/pages/edit-task.tsx b/apps/shinkai-desktop/src/pages/edit-task.tsx index 5c4dc6d9d..9eabd67df 100644 --- a/apps/shinkai-desktop/src/pages/edit-task.tsx +++ b/apps/shinkai-desktop/src/pages/edit-task.tsx @@ -25,6 +25,7 @@ export default function EditTaskPage() { created_at: task.created_at, last_modified: task.last_modified, description: task.description, + paused: task.paused, name: task.name, } : undefined diff --git a/apps/shinkai-desktop/src/pages/tasks.tsx b/apps/shinkai-desktop/src/pages/tasks.tsx index 1def1e417..ce509dc15 100644 --- a/apps/shinkai-desktop/src/pages/tasks.tsx +++ b/apps/shinkai-desktop/src/pages/tasks.tsx @@ -1,7 +1,9 @@ import { DialogClose } from '@radix-ui/react-dialog'; import { DotsVerticalIcon } from '@radix-ui/react-icons'; import { useTranslation } from '@shinkai_network/shinkai-i18n'; +import { JobConfig } from '@shinkai_network/shinkai-message-ts/api/jobs/types'; import { useRemoveRecurringTask } from '@shinkai_network/shinkai-node-state/v2/mutations/removeRecurringTask/useRemoveRecurringTask'; +import { useUpdateRecurringTask } from '@shinkai_network/shinkai-node-state/v2/mutations/updateRecurringTask/useUpdateRecurringTask'; import { useGetRecurringTasks } from '@shinkai_network/shinkai-node-state/v2/queries/getRecurringTasks/useGetRecurringTasks'; import { Button, @@ -40,6 +42,14 @@ export const Tasks = () => { token: auth?.api_v2_key ?? '', }); + const { mutateAsync: updateRecurringTask } = useUpdateRecurringTask({ + onError: (error) => { + toast.error('Failed to updated task', { + description: error.response?.data?.message ?? error.message, + }); + }, + }); + return ( { description={task.description} key={task.task_id} name={task.name} + onCheckedChange={async (active) => { + if ('CreateJobWithConfigAndMessage' in task.action) { + const config: JobConfig = + task.action.CreateJobWithConfigAndMessage.config; + const message = + task.action.CreateJobWithConfigAndMessage.message.content; + const llmProvider = + task.action.CreateJobWithConfigAndMessage.llm_provider; + const jobId = + task.action.CreateJobWithConfigAndMessage.message.job_id; + await updateRecurringTask({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + taskId: task.task_id.toString(), + active, + chatConfig: config, + cronExpression: task.cron, + description: task.description, + jobId, + llmProvider, + name: task.name, + message, + }); + return; + } + }} + paused={task.paused} prompt={ 'CreateJobWithConfigAndMessage' in task.action ? task.action.CreateJobWithConfigAndMessage.message.content @@ -113,15 +150,20 @@ const TaskCard = ({ description, cronExpression, prompt, + paused, + onCheckedChange, }: { taskId: number; name: string; description?: string; cronExpression: string; prompt: string; + paused: boolean; + onCheckedChange: (active: boolean) => void; }) => { const navigate = useNavigate(); const { t } = useTranslation(); + const [isDeleteTaskDrawerOpen, setIsDeleteTaskDrawerOpen] = React.useState(false); @@ -154,12 +196,9 @@ const TaskCard = ({

- +
; export type CreateRecurringTaskResponse = RecurringTask; @@ -48,6 +46,7 @@ export type RecurringTask = { action: RecurringTaskAction; description?: string; name: string; + paused: boolean; }; export type GetRecurringTasksResponse = RecurringTask[]; diff --git a/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/index.ts b/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/index.ts index 045ef37ae..cdc8e3a3c 100644 --- a/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/index.ts +++ b/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/index.ts @@ -14,12 +14,14 @@ export const updateRecurringTask = async ({ toolRouterKey, taskId, jobId, + active, }: UpdateRecurringTaskInput) => { const response = await setRecurringTaskApi(nodeAddress, token, { cron_task_id: taskId, name: name, description: description, cron: cronExpression, + paused: !active, action: { CreateJobWithConfigAndMessage: { config: chatConfig, diff --git a/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/types.ts b/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/types.ts index dd1cdd7ac..bdca839da 100644 --- a/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/types.ts +++ b/libs/shinkai-node-state/src/v2/mutations/updateRecurringTask/types.ts @@ -7,6 +7,7 @@ export type UpdateRecurringTaskOutput = CreateRecurringTaskResponse; export type UpdateRecurringTaskInput = Token & { nodeAddress: string; name: string; + active: boolean; description?: string; taskId: string; jobId: string; From f556068e660db812a7ca2b36f8981403fabece5b Mon Sep 17 00:00:00 2001 From: paulclindo Date: Thu, 19 Dec 2024 20:13:32 -0500 Subject: [PATCH 2/4] feat: cron jobs activity --- apps/shinkai-desktop/src/pages/task-logs.tsx | 103 ++++++++++-------- apps/shinkai-desktop/src/pages/tasks.tsx | 89 ++++++++++++++- .../src/api/recurring-tasks/index.ts | 15 +++ .../src/api/recurring-tasks/types.ts | 4 + .../index.ts | 11 ++ .../types.ts | 8 ++ .../useGetRecurringTaskNextExecutionTime.ts | 15 +++ libs/shinkai-ui/src/assets/icons/general.tsx | 54 +++++++++ 8 files changed, 250 insertions(+), 49 deletions(-) create mode 100644 libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/index.ts create mode 100644 libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/types.ts create mode 100644 libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/useGetRecurringTaskNextExecutionTime.ts diff --git a/apps/shinkai-desktop/src/pages/task-logs.tsx b/apps/shinkai-desktop/src/pages/task-logs.tsx index f6dc87006..7243e2ca8 100644 --- a/apps/shinkai-desktop/src/pages/task-logs.tsx +++ b/apps/shinkai-desktop/src/pages/task-logs.tsx @@ -32,6 +32,7 @@ import { CheckCircle2, Clock, Edit, + RefreshCwIcon, Sparkles, TrashIcon, XCircle, @@ -59,7 +60,9 @@ export const TaskLogs = () => { const { data: logs, isPending, + isRefetching, isSuccess, + refetch, } = useGetRecurringTaskLogs({ nodeAddress: auth?.node_address ?? '', token: auth?.api_v2_key ?? '', @@ -77,6 +80,7 @@ export const TaskLogs = () => { { ))} -
+

Logs

- {/**/} - {/* {isRunning ? (*/} - {/* */} - {/* ) : (*/} - {/* */} - {/* )}*/} - {/* {isRunning ? 'Pause Task' : 'Resume Task'}*/} - {/**/} +
@@ -139,15 +143,15 @@ export const TaskLogs = () => {
)} {isSuccess && logs.length > 0 && ( -
-
+
+
Execution Time Status Message
{logs.map((log) => (
@@ -164,6 +168,9 @@ export const TaskLogs = () => { {log.success ? 'Success' : 'Failed'}
+
+ {log.error_message || '-'} +
))}
@@ -181,7 +188,7 @@ const TaskCard = ({ cronExpression, prompt, llmProvider, - isRunning = true, + isRunning, }: { taskId: number; name: string; @@ -189,7 +196,7 @@ const TaskCard = ({ cronExpression: string; prompt: string; llmProvider: string; - isRunning?: boolean; + isRunning: boolean; }) => { const navigate = useNavigate(); const { t } = useTranslation(); @@ -201,17 +208,21 @@ const TaskCard = ({ }); return ( - - + +
{name} - {isRunning ? 'Active' : 'Paused'} + {isRunning ? 'Active' : 'Inactive'} {description} @@ -282,34 +293,32 @@ const TaskCard = ({ />
- -
-
-
- - Prompt -
-
{prompt}
+ +
+
+ + Prompt:
-
-
-
- - Schedule -
-
- {readableCron} - ({cronExpression}) -
-
-
-
- - Agent/AI Model -
-
{llmProvider}
-
+
{prompt}
+
+
+
+ + Schedule: +
+
+ {readableCron} + + {cronExpression} + +
+
+
+
+ + Agent/AI Model:
+
{llmProvider}
diff --git a/apps/shinkai-desktop/src/pages/tasks.tsx b/apps/shinkai-desktop/src/pages/tasks.tsx index ce509dc15..c6bb13684 100644 --- a/apps/shinkai-desktop/src/pages/tasks.tsx +++ b/apps/shinkai-desktop/src/pages/tasks.tsx @@ -4,6 +4,7 @@ import { useTranslation } from '@shinkai_network/shinkai-i18n'; import { JobConfig } from '@shinkai_network/shinkai-message-ts/api/jobs/types'; import { useRemoveRecurringTask } from '@shinkai_network/shinkai-node-state/v2/mutations/removeRecurringTask/useRemoveRecurringTask'; import { useUpdateRecurringTask } from '@shinkai_network/shinkai-node-state/v2/mutations/updateRecurringTask/useUpdateRecurringTask'; +import { useGetRecurringTaskNextExecutionTime } from '@shinkai_network/shinkai-node-state/v2/queries/getRecurringTaskNextExecutionTime/useGetRecurringTaskNextExecutionTime'; import { useGetRecurringTasks } from '@shinkai_network/shinkai-node-state/v2/queries/getRecurringTasks/useGetRecurringTasks'; import { Button, @@ -18,11 +19,19 @@ import { DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, + Popover, + PopoverContent, + PopoverTrigger, Switch, } from '@shinkai_network/shinkai-ui'; +import { + ScheduledTasksComingSoonIcon, + ScheduledTasksIcon, +} from '@shinkai_network/shinkai-ui/assets'; import { cn } from '@shinkai_network/shinkai-ui/utils'; import cronstrue from 'cronstrue'; -import { Edit, PlusIcon, TrashIcon } from 'lucide-react'; +import { formatDistance } from 'date-fns'; +import { Edit, PlusIcon, RefreshCwIcon, TrashIcon } from 'lucide-react'; import React from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; @@ -42,6 +51,16 @@ export const Tasks = () => { token: auth?.api_v2_key ?? '', }); + const { + data: cronTasksNextExecutionTime, + isSuccess: isCronTasksNextExecutionTimeSuccess, + refetch, + isRefetching, + } = useGetRecurringTaskNextExecutionTime({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + }); + const { mutateAsync: updateRecurringTask } = useUpdateRecurringTask({ onError: (error) => { toast.error('Failed to updated task', { @@ -53,7 +72,73 @@ export const Tasks = () => { return ( +
+ {isCronTasksNextExecutionTimeSuccess && + cronTasksNextExecutionTime.length > 0 && ( + + + + + e.preventDefault()} + > +
+

Scheduled Cron Tasks

+ +
+ {cronTasksNextExecutionTime?.map(([task, date]) => ( +
+ +
+ + {task.name} + + {cronstrue.toString(task.cron, { + throwExceptionOnParseError: false, + })} + + + + {' '} + Next execution in{' '} + + {formatDistance(new Date(date), new Date(), { + addSuffix: true, + })} + + +
+
+ ))} +
+
+ )} + { + const response = await httpClient.get( + urlJoin(nodeAddress, '/v2/get_cron_schedule'), + { + headers: { Authorization: `Bearer ${bearerToken}` }, + responseType: 'json', + }, + ); + return response.data as GetRecurringTasksNextExecutionTimeResponse; +}; + export const setRecurringTask = async ( nodeAddress: string, bearerToken: string, diff --git a/libs/shinkai-message-ts/src/api/recurring-tasks/types.ts b/libs/shinkai-message-ts/src/api/recurring-tasks/types.ts index f2f3cb52c..665172419 100644 --- a/libs/shinkai-message-ts/src/api/recurring-tasks/types.ts +++ b/libs/shinkai-message-ts/src/api/recurring-tasks/types.ts @@ -55,6 +55,10 @@ export type GetRecurringTaskRequest = { cron_task_id: string; }; export type GetRecurringTaskResponse = RecurringTask; +export type GetRecurringTasksNextExecutionTimeResponse = [ + RecurringTask, + string, +][]; export type SetRecurringTaskRequest = { cron_task_id: string; } & Omit; diff --git a/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/index.ts b/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/index.ts new file mode 100644 index 000000000..7d649e12b --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/index.ts @@ -0,0 +1,11 @@ +import { getRecurringTasksExecutionTime as getRecurringTaskExecutionTimeApi } from '@shinkai_network/shinkai-message-ts/api/recurring-tasks/index'; + +import type { GetRecurringTasksNextExecutionTimeInput } from './types'; + +export const getRecurringTasksExecutionTime = async ({ + nodeAddress, + token, +}: GetRecurringTasksNextExecutionTimeInput) => { + const result = await getRecurringTaskExecutionTimeApi(nodeAddress, token); + return result; +}; diff --git a/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/types.ts b/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/types.ts new file mode 100644 index 000000000..509bc49a9 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/types.ts @@ -0,0 +1,8 @@ +import { Token } from '@shinkai_network/shinkai-message-ts/api/general/types'; +import { GetRecurringTaskResponse } from '@shinkai_network/shinkai-message-ts/api/recurring-tasks/types'; + +export type GetRecurringTasksNextExecutionTimeInput = Token & { + nodeAddress: string; +}; + +export type GetRecurringTasksNextExecutionTimeOutput = GetRecurringTaskResponse; diff --git a/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/useGetRecurringTaskNextExecutionTime.ts b/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/useGetRecurringTaskNextExecutionTime.ts new file mode 100644 index 000000000..819fb84e7 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getRecurringTaskNextExecutionTime/useGetRecurringTaskNextExecutionTime.ts @@ -0,0 +1,15 @@ +import { useQuery } from '@tanstack/react-query'; + +import { FunctionKeyV2 } from '../../constants'; +import { getRecurringTasksExecutionTime } from './index'; +import { GetRecurringTasksNextExecutionTimeInput } from './types'; + +export const useGetRecurringTaskNextExecutionTime = ( + input: GetRecurringTasksNextExecutionTimeInput, +) => { + const response = useQuery({ + queryKey: [FunctionKeyV2.GET_RECURRING_TASK, input], + queryFn: () => getRecurringTasksExecutionTime(input), + }); + return response; +}; diff --git a/libs/shinkai-ui/src/assets/icons/general.tsx b/libs/shinkai-ui/src/assets/icons/general.tsx index 56f493b64..2302d40ee 100644 --- a/libs/shinkai-ui/src/assets/icons/general.tsx +++ b/libs/shinkai-ui/src/assets/icons/general.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import { cn } from '../../utils'; import { fileIconMap } from './file'; @@ -1296,3 +1298,55 @@ export const ToolAssetsIcon = ({ className }: { className?: string }) => ( /> ); + +export const ScheduledTasksComingSoonIcon = ({ + className, +}: { + className?: string; +}) => ( + + + + + + + +); From badb11ea2c956ba29faeec17d23fcf6bea4346c2 Mon Sep 17 00:00:00 2001 From: paulclindo Date: Thu, 19 Dec 2024 20:22:52 -0500 Subject: [PATCH 3/4] feat: refetch activity --- apps/shinkai-desktop/src/pages/tasks.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/shinkai-desktop/src/pages/tasks.tsx b/apps/shinkai-desktop/src/pages/tasks.tsx index c6bb13684..8c66a6aba 100644 --- a/apps/shinkai-desktop/src/pages/tasks.tsx +++ b/apps/shinkai-desktop/src/pages/tasks.tsx @@ -79,6 +79,7 @@ export const Tasks = () => {