From ca08245b375ea2321efb907ed898fec4da2d38ca Mon Sep 17 00:00:00 2001 From: paulclindo Date: Wed, 18 Dec 2024 19:48:06 -0500 Subject: [PATCH 1/3] feat: manage sources in tool playground --- .../components/tool-playground.tsx | 207 ++++++++++++++++-- .../playground-tool/tool-code-editor.tsx | 2 +- .../src/api/general/types.ts | 6 + .../shinkai-message-ts/src/api/tools/index.ts | 96 ++++++++ .../shinkai-message-ts/src/api/tools/types.ts | 15 ++ libs/shinkai-node-state/src/v2/constants.ts | 1 + .../src/v2/mutations/removeAssetTool/index.ts | 21 ++ .../src/v2/mutations/removeAssetTool/types.ts | 14 ++ .../removeAssetTool/useRemoveAssetTool.ts | 30 +++ .../v2/mutations/uploadAssetsTool/index.ts | 21 ++ .../v2/mutations/uploadAssetsTool/types.ts | 14 ++ .../uploadAssetsTool/useUploadAssetsTool.ts | 30 +++ .../src/v2/queries/getAllToolAssets/index.ts | 18 ++ .../src/v2/queries/getAllToolAssets/types.ts | 12 + .../getAllToolAssets/useGetAllToolAssets.ts | 29 +++ libs/shinkai-ui/src/assets/icons/general.tsx | 42 +++- 16 files changed, 538 insertions(+), 20 deletions(-) create mode 100644 libs/shinkai-node-state/src/v2/mutations/removeAssetTool/index.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/removeAssetTool/types.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/removeAssetTool/useRemoveAssetTool.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/index.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/types.ts create mode 100644 libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/useUploadAssetsTool.ts create mode 100644 libs/shinkai-node-state/src/v2/queries/getAllToolAssets/index.ts create mode 100644 libs/shinkai-node-state/src/v2/queries/getAllToolAssets/types.ts create mode 100644 libs/shinkai-node-state/src/v2/queries/getAllToolAssets/useGetAllToolAssets.ts diff --git a/apps/shinkai-desktop/src/components/playground-tool/components/tool-playground.tsx b/apps/shinkai-desktop/src/components/playground-tool/components/tool-playground.tsx index fb298d6ca..cdbf66f8b 100644 --- a/apps/shinkai-desktop/src/components/playground-tool/components/tool-playground.tsx +++ b/apps/shinkai-desktop/src/components/playground-tool/components/tool-playground.tsx @@ -1,16 +1,26 @@ +import { DialogClose } from '@radix-ui/react-dialog'; import { ReloadIcon } from '@radix-ui/react-icons'; import { FormProps } from '@rjsf/core'; import validator from '@rjsf/validator-ajv8'; import { useTranslation } from '@shinkai_network/shinkai-i18n'; +import { CustomToolHeaders } from '@shinkai_network/shinkai-message-ts/api/general/types'; import { CodeLanguage, ToolMetadata, } from '@shinkai_network/shinkai-message-ts/api/tools/types'; +import { useRemoveAssetTool } from '@shinkai_network/shinkai-node-state/v2/mutations/removeAssetTool/useRemoveAssetTool'; +import { useUploadAssetsTool } from '@shinkai_network/shinkai-node-state/v2/mutations/uploadAssetsTool/useUploadAssetsTool'; +import { useGetAllToolAssets } from '@shinkai_network/shinkai-node-state/v2/queries/getAllToolAssets/useGetAllToolAssets'; import { useGetShinkaiFileProtocol } from '@shinkai_network/shinkai-node-state/v2/queries/getShinkaiFileProtocol/useGetShinkaiFileProtocol'; import { Badge, Button, ChatInputArea, + Dialog, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger, Form, FormControl, FormField, @@ -21,6 +31,7 @@ import { ResizableHandle, ResizablePanel, ResizablePanelGroup, + Separator, Tabs, TabsContent, TabsList, @@ -34,7 +45,9 @@ import { fileIconMap, FileTypeIcon, SendIcon, + ToolAssetsIcon, } from '@shinkai_network/shinkai-ui/assets'; +import { getFileExt } from '@shinkai_network/shinkai-ui/helpers'; import { cn } from '@shinkai_network/shinkai-ui/utils'; import { save } from '@tauri-apps/plugin-dialog'; import * as fs from '@tauri-apps/plugin-fs'; @@ -49,11 +62,15 @@ import { Redo2Icon, Save, Undo2Icon, + Upload, + XIcon, } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; +import { useDropzone } from 'react-dropzone'; import { Link, To } from 'react-router-dom'; import { toast } from 'sonner'; +import { allowedFileExtensions } from '../../../lib/constants'; import { useAuth } from '../../../store/auth'; import { AIModelSelector } from '../../chat/chat-action-bar/ai-update-selection-action-bar'; import { ToolErrorFallback } from '../error-boundary'; @@ -359,7 +376,7 @@ function PlaygroundToolEditor({ Metadata -
+
{toolHistory.length > 1 && (
{toolCode === toolHistory?.at(-1)?.code ? ( @@ -446,24 +463,33 @@ function PlaygroundToolEditor({
+
)} - +
+ + +
); } + +function ManageToolSourceModal({ + xShinkaiAppId, + xShinkaiToolId, +}: CustomToolHeaders) { + const auth = useAuth((state) => state.auth); + const { t } = useTranslation(); + + const { data: assets, isSuccess: isGetAllToolAssetsSuccess } = + useGetAllToolAssets({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + xShinkaiAppId, + xShinkaiToolId, + }); + + const { mutateAsync: uploadAssets } = useUploadAssetsTool({ + onError: (error) => { + toast.error('Failed uploading source:', { + description: error.response?.data?.message ?? error.message, + }); + }, + }); + + const { mutateAsync: removeAsset } = useRemoveAssetTool({ + onError: (error) => { + toast.error('Failed removing source:', { + description: error.response?.data?.message ?? error.message, + }); + }, + }); + + const { getRootProps: getRootFileProps, getInputProps: getInputFileProps } = + useDropzone({ + multiple: true, + maxFiles: 5, + onDrop: async (acceptedFiles) => { + await uploadAssets({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + files: acceptedFiles, + xShinkaiAppId, + xShinkaiToolId, + }); + }, + }); + + return ( + + + + + + + + +
+ Manage Sources + + Add knowledge directly to your tool. It is used to provide context + to the large language model. + +
+ +
+
+
+ +
+

{t('common.clickToUpload')}

+ +

+ Supports {allowedFileExtensions.join(', ')} +

+
+ + +
+ +
5, + )} + > + {isGetAllToolAssetsSuccess && assets.length === 0 && ( + + No source files uploaded yet. + + )} + {isGetAllToolAssetsSuccess && + assets.map((asset) => ( +
+
+
+ {getFileExt(asset) && fileIconMap[getFileExt(asset)] ? ( + + ) : ( + + )} +
+ {decodeURIComponent(asset)} +
+ +
+ ))} +
+
+
+ ); +} diff --git a/apps/shinkai-desktop/src/components/playground-tool/tool-code-editor.tsx b/apps/shinkai-desktop/src/components/playground-tool/tool-code-editor.tsx index f44da5501..d47c95950 100644 --- a/apps/shinkai-desktop/src/components/playground-tool/tool-code-editor.tsx +++ b/apps/shinkai-desktop/src/components/playground-tool/tool-code-editor.tsx @@ -116,7 +116,7 @@ const ToolCodeEditor = forwardRef< } >(({ value, onUpdate, language, name, readOnly, style }, ref) => ( { + const response = await httpClient.get( + urlJoin(nodeAddress, '/v2/list_tool_asset'), + { + headers: { + Authorization: `Bearer ${bearerToken}`, + 'x-shinkai-app-id': xShinkaiAppId, + 'x-shinkai-tool-id': xShinkaiToolId, + }, + responseType: 'json', + }, + ); + return response.data as GetAllToolAssetsResponse; +}; + +export const uploadAssetTool = async ( + nodeAddress: string, + bearerToken: string, + payload: AddToolRequestRequest, + xShinkaiAppId: string, + xShinkaiToolId: string, +) => { + const fileData = await payload.file.arrayBuffer(); + + const formData = new FormData(); + formData.append('file_name', payload.filename); + formData.append('file', new Blob([fileData])); + + const response = await httpClient.post( + urlJoin(nodeAddress, '/v2/tool_asset'), + formData, + { + headers: { + Authorization: `Bearer ${bearerToken}`, + 'x-shinkai-app-id': xShinkaiAppId, + 'x-shinkai-tool-id': xShinkaiToolId, + }, + responseType: 'json', + }, + ); + return response.data as AddToolRequestResponse; +}; + +export const uploadAssetsToTool = async ( + nodeAddress: string, + bearerToken: string, + xShinkaiAppId: string, + xShinkaiToolId: string, + files: File[], +) => { + for (const file of files) { + await uploadAssetTool( + nodeAddress, + bearerToken, + { + filename: encodeURIComponent(file.name), + file, + }, + xShinkaiAppId, + xShinkaiToolId, + ); + } + return { success: true }; +}; + +export const removeToolAsset = async ( + nodeAddress: string, + bearerToken: string, + payload: RemoveToolRequestRequest, + xShinkaiAppId: string, + xShinkaiToolId: string, +) => { + const response = await httpClient.delete( + urlJoin(nodeAddress, '/v2/tool_asset'), + { + headers: { + Authorization: `Bearer ${bearerToken}`, + 'x-shinkai-app-id': xShinkaiAppId, + 'x-shinkai-tool-id': xShinkaiToolId, + }, + params: { file_name: payload.filename }, + responseType: 'json', + }, + ); + return response.data; +}; diff --git a/libs/shinkai-message-ts/src/api/tools/types.ts b/libs/shinkai-message-ts/src/api/tools/types.ts index 979c3e715..be332505c 100644 --- a/libs/shinkai-message-ts/src/api/tools/types.ts +++ b/libs/shinkai-message-ts/src/api/tools/types.ts @@ -319,3 +319,18 @@ export type ExportToolRequest = { export type ExportToolResponse = Blob; export type GetShinkaiFileProtocolRequest = { file: string }; export type GetShinkaiFileProtocolResponse = Blob; + +export type GetAllToolAssetsResponse = string[]; +export type AddToolRequestRequest = { + filename: string; + file: File; +}; +export type AddToolRequestResponse = { + file: number; + file_name: string; + message: string; + status: string; +}; +export type RemoveToolRequestRequest = { + filename: string; +}; diff --git a/libs/shinkai-node-state/src/v2/constants.ts b/libs/shinkai-node-state/src/v2/constants.ts index b9e7de3b9..9c96d24dd 100644 --- a/libs/shinkai-node-state/src/v2/constants.ts +++ b/libs/shinkai-node-state/src/v2/constants.ts @@ -39,6 +39,7 @@ export enum FunctionKeyV2 { GET_RECURRING_TASK = 'GET_RECURRING_TASK', GET_RECURRING_TASK_LOGS = 'GET_RECURRING_TASK_LOGS', GET_SHINKAI_FILE_PROTOCOL = 'GET_SHINKAI_FILE_PROTOCOL', + GET_ALL_TOOL_ASSETS = 'GET_ALL_TOOL_ASSETS', } export const DEFAULT_CHAT_CONFIG = { diff --git a/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/index.ts b/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/index.ts new file mode 100644 index 000000000..83b02bac1 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/index.ts @@ -0,0 +1,21 @@ +import { removeToolAsset as removeToolAssetApi } from '@shinkai_network/shinkai-message-ts/api/tools/index'; + +import { RemoveAssetToToolInput } from './types'; + +export const removeToolAsset = async ({ + nodeAddress, + token, + filename, + xShinkaiAppId, + xShinkaiToolId, +}: RemoveAssetToToolInput) => { + const response = await removeToolAssetApi( + nodeAddress, + token, + { filename }, + xShinkaiAppId, + xShinkaiToolId, + ); + + return response; +}; diff --git a/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/types.ts b/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/types.ts new file mode 100644 index 000000000..281cdf4d7 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/types.ts @@ -0,0 +1,14 @@ +import { + CustomToolHeaders, + Token, +} from '@shinkai_network/shinkai-message-ts/api/general/types'; + +export type RemoveAssetToToolInput = Token & + CustomToolHeaders & { + nodeAddress: string; + filename: string; + }; + +export type RemoveAssetToToolOutput = { + success: boolean; +}; diff --git a/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/useRemoveAssetTool.ts b/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/useRemoveAssetTool.ts new file mode 100644 index 000000000..739545366 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/removeAssetTool/useRemoveAssetTool.ts @@ -0,0 +1,30 @@ +import type { UseMutationOptions } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { FunctionKeyV2 } from '../../constants'; +import { APIError } from '../../types'; +import { removeToolAsset } from '.'; +import { RemoveAssetToToolInput, RemoveAssetToToolOutput } from './types'; + +type Options = UseMutationOptions< + RemoveAssetToToolOutput, + APIError, + RemoveAssetToToolInput +>; + +export const useRemoveAssetTool = (options?: Options) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: removeToolAsset, + ...options, + onSuccess: (response, variables, context) => { + queryClient.invalidateQueries({ + queryKey: [FunctionKeyV2.GET_ALL_TOOL_ASSETS], + }); + + if (options?.onSuccess) { + options.onSuccess(response, variables, context); + } + }, + }); +}; diff --git a/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/index.ts b/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/index.ts new file mode 100644 index 000000000..815830976 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/index.ts @@ -0,0 +1,21 @@ +import { uploadAssetsToTool as uploadAssetsToToolApi } from '@shinkai_network/shinkai-message-ts/api/tools/index'; + +import { UploadAssetsToToolInput } from './types'; + +export const uploadAssetsToTool = async ({ + nodeAddress, + token, + files, + xShinkaiAppId, + xShinkaiToolId, +}: UploadAssetsToToolInput) => { + const response = await uploadAssetsToToolApi( + nodeAddress, + token, + xShinkaiAppId, + xShinkaiToolId, + files, + ); + + return response; +}; diff --git a/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/types.ts b/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/types.ts new file mode 100644 index 000000000..aad6111cd --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/types.ts @@ -0,0 +1,14 @@ +import { + CustomToolHeaders, + Token, +} from '@shinkai_network/shinkai-message-ts/api/general/types'; + +export type UploadAssetsToToolInput = Token & + CustomToolHeaders & { + nodeAddress: string; + files: File[]; + }; + +export type UploadAssetsToToolOutput = { + success: boolean; +}; diff --git a/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/useUploadAssetsTool.ts b/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/useUploadAssetsTool.ts new file mode 100644 index 000000000..8d3ced0c5 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/mutations/uploadAssetsTool/useUploadAssetsTool.ts @@ -0,0 +1,30 @@ +import type { UseMutationOptions } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { FunctionKeyV2 } from '../../constants'; +import { APIError } from '../../types'; +import { uploadAssetsToTool } from '.'; +import { UploadAssetsToToolInput, UploadAssetsToToolOutput } from './types'; + +type Options = UseMutationOptions< + UploadAssetsToToolOutput, + APIError, + UploadAssetsToToolInput +>; + +export const useUploadAssetsTool = (options?: Options) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: uploadAssetsToTool, + ...options, + onSuccess: (response, variables, context) => { + queryClient.invalidateQueries({ + queryKey: [FunctionKeyV2.GET_ALL_TOOL_ASSETS], + }); + + if (options?.onSuccess) { + options.onSuccess(response, variables, context); + } + }, + }); +}; diff --git a/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/index.ts b/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/index.ts new file mode 100644 index 000000000..b15154803 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/index.ts @@ -0,0 +1,18 @@ +import { getAllToolAssets as getAllToolAssetsApi } from '@shinkai_network/shinkai-message-ts/api/tools/index'; + +import type { GetAllToolAssetsInput } from './types'; + +export const getAllToolAssets = async ({ + nodeAddress, + token, + xShinkaiAppId, + xShinkaiToolId, +}: GetAllToolAssetsInput) => { + const result = await getAllToolAssetsApi( + nodeAddress, + token, + xShinkaiAppId, + xShinkaiToolId, + ); + return result; +}; diff --git a/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/types.ts b/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/types.ts new file mode 100644 index 000000000..b00117fa6 --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/types.ts @@ -0,0 +1,12 @@ +import { + CustomToolHeaders, + Token, +} from '@shinkai_network/shinkai-message-ts/api/general/types'; +import { GetAllToolAssetsResponse } from '@shinkai_network/shinkai-message-ts/api/tools/types'; + +export type GetAllToolAssetsInput = Token & + CustomToolHeaders & { + nodeAddress: string; + }; + +export type GetAllToolAssetsOutput = GetAllToolAssetsResponse; diff --git a/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/useGetAllToolAssets.ts b/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/useGetAllToolAssets.ts new file mode 100644 index 000000000..ba4cbdade --- /dev/null +++ b/libs/shinkai-node-state/src/v2/queries/getAllToolAssets/useGetAllToolAssets.ts @@ -0,0 +1,29 @@ +import { QueryObserverOptions, useQuery } from '@tanstack/react-query'; + +import { FunctionKeyV2 } from '../../constants'; +import { getAllToolAssets } from './index'; +import { GetAllToolAssetsInput, GetAllToolAssetsOutput } from './types'; + +export type UseGetAllToolAssets = [ + FunctionKeyV2.GET_ALL_TOOL_ASSETS, + GetAllToolAssetsInput, +]; +type Options = QueryObserverOptions< + GetAllToolAssetsOutput, + Error, + GetAllToolAssetsOutput, + GetAllToolAssetsOutput, + UseGetAllToolAssets +>; + +export const useGetAllToolAssets = ( + input: GetAllToolAssetsInput, + options?: Omit, +) => { + const response = useQuery({ + queryKey: [FunctionKeyV2.GET_ALL_TOOL_ASSETS, input], + queryFn: () => getAllToolAssets(input), + ...options, + }); + return response; +}; diff --git a/libs/shinkai-ui/src/assets/icons/general.tsx b/libs/shinkai-ui/src/assets/icons/general.tsx index 7a7bb8ef3..56f493b64 100644 --- a/libs/shinkai-ui/src/assets/icons/general.tsx +++ b/libs/shinkai-ui/src/assets/icons/general.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import { cn } from '../../utils'; import { fileIconMap } from './file'; @@ -1258,3 +1256,43 @@ export const AIAgentIcon = ({ className }: { className?: string }) => ( /> ); +export const ToolAssetsIcon = ({ className }: { className?: string }) => ( + + + + + + + +); From cc5c6b80587fa5dbf706db7c4923468bb2a634aa Mon Sep 17 00:00:00 2001 From: paulclindo Date: Wed, 18 Dec 2024 20:42:47 -0500 Subject: [PATCH 2/3] more fixes --- .../components/remove-tool-button.tsx | 1 - .../src/components/tools/deno-tool.tsx | 11 +++++++---- .../src/components/tools/network-tool.tsx | 2 +- .../src/components/tools/python-tool.tsx | 11 +++++++---- .../src/components/tools/rust-tool.tsx | 2 +- libs/shinkai-ui/src/helpers/format-text.ts | 12 ++++++++++++ 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/apps/shinkai-desktop/src/components/playground-tool/components/remove-tool-button.tsx b/apps/shinkai-desktop/src/components/playground-tool/components/remove-tool-button.tsx index 9daa278c9..7d57a2bc4 100644 --- a/apps/shinkai-desktop/src/components/playground-tool/components/remove-tool-button.tsx +++ b/apps/shinkai-desktop/src/components/playground-tool/components/remove-tool-button.tsx @@ -24,7 +24,6 @@ export default function RemoveToolButton({ toolKey }: { toolKey: string }) { return (