Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: manage sources in tool playground #572

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export default function RemoveToolButton({ toolKey }: { toolKey: string }) {

return (
<Button
className="border-red-800 bg-red-700/50 text-red-50 hover:bg-red-900"
disabled={isRemoveToolPending}
isLoading={isRemoveToolPending}
onClick={async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -21,6 +31,7 @@ import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
Separator,
Tabs,
TabsContent,
TabsList,
Expand All @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -359,7 +376,7 @@ function PlaygroundToolEditor({
Metadata
</TabsTrigger>
</TabsList>
<div className="flex items-center gap-6">
<div className="flex items-stretch gap-6">
{toolHistory.length > 1 && (
<div className="flex items-center gap-4">
{toolCode === toolHistory?.at(-1)?.code ? (
Expand Down Expand Up @@ -446,24 +463,33 @@ function PlaygroundToolEditor({
</TooltipPortal>
</Tooltip>
</div>
<Separator
className="my-1 bg-gray-300"
orientation="vertical"
/>
</div>
)}
<Button
className="text-gray-80 h-[30px] shrink-0 rounded-md text-xs"
disabled={
!toolCode ||
!metadataGenerationData ||
!chatInboxId ||
isSavingTool
}
isLoading={isSavingTool}
onClick={handleSaveTool}
size="sm"
variant="outline"
>
<Save className="mr-2 h-4 w-4" />
Save Tool
</Button>
<div className="flex items-center gap-2.5">
<ManageToolSourceModal
xShinkaiAppId={xShinkaiAppId}
xShinkaiToolId={xShinkaiToolId}
/>
<Button
className="h-[30px] shrink-0 rounded-md text-xs"
disabled={
!toolCode ||
!metadataGenerationData ||
!chatInboxId ||
isSavingTool
}
isLoading={isSavingTool}
onClick={handleSaveTool}
size="sm"
>
<Save className="mr-2 h-4 w-4" />
Save Tool
</Button>
</div>
</div>
</div>
<TabsContent
Expand Down Expand Up @@ -909,3 +935,147 @@ function ToolResultFileCard({ filePath }: { filePath: string }) {
</Button>
);
}

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 (
<Dialog>
<DialogTrigger asChild>
<Button
className="text-gray-80 h-[30px] shrink-0 rounded-md text-xs"
size="sm"
variant="outline"
>
<ToolAssetsIcon className="text-gray-80 mr-2 h-4 w-4" />
Manage Sources ({isGetAllToolAssetsSuccess ? assets.length : '-'})
</Button>
</DialogTrigger>
<DialogContent className="flex h-[50vh] max-w-[500px] flex-col gap-4">
<DialogClose className="absolute right-4 top-4">
<XIcon className="text-gray-80 h-5 w-5" />
</DialogClose>
<div className="space-y-2">
<DialogTitle className="pb-0">Manage Sources</DialogTitle>
<DialogDescription className="text-xs">
Add knowledge directly to your tool. It is used to provide context
to the large language model.
</DialogDescription>
</div>

<div
{...getRootFileProps({
className:
'dropzone py-4 bg-gray-400 group relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg border border-dashed border-gray-200 transition-colors hover:border-gray-100',
})}
>
<div className="flex flex-col items-center justify-center space-y-1 px-2">
<div className="bg-gray-350 rounded-full p-2 shadow-sm">
<Upload className="h-4 w-4" />
</div>
<p className="text-sm text-white">{t('common.clickToUpload')}</p>

<p className="text-gray-80 line-clamp-1 text-xs">
Supports {allowedFileExtensions.join(', ')}
</p>
</div>

<input {...getInputFileProps({})} />
</div>
<Separator className="my-1 bg-gray-200" orientation="horizontal" />
<div
className={cn(
'flex flex-1 flex-col gap-2 overflow-y-auto pr-2',
(assets ?? []).length > 5,
)}
>
{isGetAllToolAssetsSuccess && assets.length === 0 && (
<span className="text-gray-80 text-center text-xs">
No source files uploaded yet.
</span>
)}
{isGetAllToolAssetsSuccess &&
assets.map((asset) => (
<div
className="flex items-center justify-between gap-2 rounded-lg border border-gray-200 px-1.5 py-1.5 py-2"
key={asset}
>
<div className="flex items-center gap-2 text-gray-50">
<div className="w-4.5 flex aspect-square shrink-0 items-center justify-center">
{getFileExt(asset) && fileIconMap[getFileExt(asset)] ? (
<FileTypeIcon
className="text-gray-80 h-[18px] w-[18px] shrink-0"
type={getFileExt(asset)}
/>
) : (
<Paperclip className="text-gray-80 h-3.5 w-3.5 shrink-0" />
)}
</div>
<span className="text-sm">{decodeURIComponent(asset)}</span>
</div>
<Button
className="text-gray-80 !size-5 border-0 p-0.5 hover:text-white"
onClick={async () => {
await removeAsset({
nodeAddress: auth?.node_address ?? '',
token: auth?.api_v2_key ?? '',
xShinkaiAppId,
xShinkaiToolId,
filename: asset,
});
}}
size="auto"
variant="outline"
>
<XIcon className="size-full" />
</Button>
</div>
))}
</div>
</DialogContent>
</Dialog>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const ToolCodeEditor = forwardRef<
}
>(({ value, onUpdate, language, name, readOnly, style }, ref) => (
<Editor
insertSpaces={false}
insertSpaces={true}
language={language}
onUpdate={onUpdate}
readOnly={readOnly}
Expand Down
11 changes: 7 additions & 4 deletions apps/shinkai-desktop/src/components/tools/deno-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
Switch,
TextField,
} from '@shinkai_network/shinkai-ui';
import { formatText } from '@shinkai_network/shinkai-ui/helpers';
import {
formatCamelCaseText,
formatText,
} 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';
Expand Down Expand Up @@ -194,7 +197,7 @@ export default function DenoTool({
},
tool.keywords.length > 0 && {
label: 'Keyword',
value: tool.keywords,
value: tool.keywords.join(', '),
},
]
.filter((item) => !!item)
Expand Down Expand Up @@ -223,7 +226,7 @@ export default function DenoTool({
render={({ field }) => (
<TextField
field={field}
label={formatText(conf.BasicConfig.key_name)}
label={formatCamelCaseText(conf.BasicConfig.key_name)}
type="password"
/>
)}
Expand All @@ -242,7 +245,7 @@ export default function DenoTool({
</Form>
</div>
)}
<div className="flex flex-col gap-4 py-4">
<div className="flex flex-col gap-4 py-6">
{isPlaygroundTool && (
<Link
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion apps/shinkai-desktop/src/components/tools/network-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ export default function NetworkTool({
{/* Go Playground*/}
{/* </Link>*/}
{/*)}*/}
<div className="flex flex-col gap-4 py-4">
<div className="flex flex-col gap-4 py-6">
<RemoveToolButton toolKey={toolKey as string} />
</div>
</div>
Expand Down
11 changes: 7 additions & 4 deletions apps/shinkai-desktop/src/components/tools/python-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
Switch,
TextField,
} from '@shinkai_network/shinkai-ui';
import { formatText } from '@shinkai_network/shinkai-ui/helpers';
import {
formatCamelCaseText,
formatText,
} 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';
Expand Down Expand Up @@ -194,7 +197,7 @@ export default function PythonTool({
},
tool.keywords.length > 0 && {
label: 'Keyword',
value: tool.keywords,
value: tool.keywords.join(', '),
},
]
.filter((item) => !!item)
Expand Down Expand Up @@ -223,7 +226,7 @@ export default function PythonTool({
render={({ field }) => (
<TextField
field={field}
label={formatText(conf.BasicConfig.key_name)}
label={formatCamelCaseText(conf.BasicConfig.key_name)}
type="password"
/>
)}
Expand All @@ -242,7 +245,7 @@ export default function PythonTool({
</Form>
</div>
)}
<div className="flex flex-col gap-4 py-4">
<div className="flex flex-col gap-4 py-6">
{isPlaygroundTool && (
<Link
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion apps/shinkai-desktop/src/components/tools/rust-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export default function RustTool({
{/* Go Playground*/}
{/* </Link>*/}
{/*)}*/}
<div className="flex flex-col gap-4 py-4">
<div className="flex flex-col gap-4 py-6">
<RemoveToolButton toolKey={toolKey as string} />
</div>
</div>
Expand Down
6 changes: 6 additions & 0 deletions libs/shinkai-message-ts/src/api/general/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export type CheckHealthResponse = {
};

export type Token = { token: string };

export type CustomToolHeaders = {
xShinkaiAppId: string;
xShinkaiToolId: string;
};

export type WalletBalance = {
amount: string;
asset: {
Expand Down
Loading