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
- {/*
*/}
+
@@ -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 = () => {