Skip to content

Commit

Permalink
fix: edit tool playground (#554)
Browse files Browse the repository at this point in the history
* fix: edit tool playground

* fixes

* feat: link agent settings in dropdown
  • Loading branch information
paulclindo authored Dec 6, 2024
1 parent 960a110 commit 2e9e7b4
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 127 deletions.
2 changes: 1 addition & 1 deletion apps/shinkai-desktop/src/components/agent/agents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ const AgentCard = ({
name: t('common.edit'),
icon: <Edit className="mr-3 h-4 w-4" />,
onClick: () => {
navigate(`/edit/${agentId}`);
navigate(`/agents/edit/${agentId}`);
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
} from '@shinkai_network/shinkai-ui';
import { AIAgentIcon } from '@shinkai_network/shinkai-ui/assets';
import { cn } from '@shinkai_network/shinkai-ui/utils';
import { BotIcon, ChevronDownIcon } from 'lucide-react';
import { BoltIcon, BotIcon, ChevronDownIcon } from 'lucide-react';
import { Link } from 'react-router-dom';
import { toast } from 'sonner';

import { useGetCurrentInbox } from '../../../hooks/use-current-inbox';
Expand Down Expand Up @@ -80,14 +81,39 @@ export function AIModelSelector({
{isAgentsSuccess &&
agents.map((agent) => (
<DropdownMenuRadioItem
className="flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-2 text-white transition-colors hover:bg-gray-200 aria-checked:bg-gray-200"
className="flex cursor-pointer items-center justify-between gap-1.5 rounded-md px-2 py-2 text-white transition-colors hover:bg-gray-200 aria-checked:bg-gray-200"
key={agent.name}
value={agent.name}
>
<AIAgentIcon className="h-3.5 w-3.5 shrink-0" />
<div className="flex flex-col gap-1">
<span className="text-xs">{agent.name}</span>
<div className="inline-flex gap-1.5">
<AIAgentIcon className="h-3.5 w-3.5 shrink-0" />
<div className="flex flex-col gap-1">
<span className="text-xs">{agent.name}</span>
</div>
</div>
<Tooltip>
<TooltipTrigger
asChild
className="flex shrink-0 items-center gap-1"
>
<Link
className="text-gray-80 size-3.5 rounded-lg hover:text-white"
to={`/agents/edit/${agent.name}`}
>
<BoltIcon className="size-full" />
</Link>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent
align="center"
alignOffset={-10}
className="z-[2000000001] max-w-md"
side="top"
>
<p>Configure Agent</p>
</TooltipContent>
</TooltipPortal>
</Tooltip>
</DropdownMenuRadioItem>
))}
{isAgentsSuccess && agents.length > 0 && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ToolMetadata } from '@shinkai_network/shinkai-message-ts/api/tools/types';
import { useMemo, useRef, useState } from 'react';
import { z } from 'zod';
import { useEffect, useRef, useState } from 'react';

import { useChatConversationWithOptimisticUpdates } from '../../../pages/chat/chat-conversation';
import { useAuth } from '../../../store/auth';
Expand All @@ -14,17 +13,20 @@ export const useToolMetadata = ({
code?: string;
initialState?: {
metadata: ToolMetadata | null;
isMetadataGenerationPending?: boolean;
isMetadataGenerationSuccess?: boolean;
isMetadataGenerationIdle?: boolean;
isMetadataGenerationError?: boolean;
metadataGenerationError?: string | null;
state?: 'idle' | 'pending' | 'success' | 'error';
error?: string | null;
};
}) => {
const [metadataData, setMetadataData] = useState<ToolMetadata | null>(
initialState?.metadata ?? null,
);
const auth = useAuth((state) => state.auth);
const [error, setError] = useState<string | null>(
initialState?.error ?? null,
);
const [metadataState, setMetadataState] = useState<
'idle' | 'pending' | 'success' | 'error'
>(initialState?.state ?? 'idle');

const forceGenerateMetadata = useRef(false);

Expand All @@ -34,83 +36,64 @@ export const useToolMetadata = ({
forceRefetchInterval: true,
});

const {
isMetadataGenerationPending,
isMetadataGenerationSuccess,
isMetadataGenerationIdle,
metadataGenerationData,
metadataGenerationError,
isMetadataGenerationError,
} = useMemo(() => {
useEffect(() => {
setMetadataData(initialState?.metadata ?? null);
setError(initialState?.error ?? null);
setMetadataState(initialState?.state ?? 'idle');
}, [initialState?.error, initialState?.metadata, initialState?.state]);

useEffect(() => {
const metadata = metadataChatConversation?.pages?.at(-1)?.at(-1);

if (initialState && !metadata) {
return {
isMetadataGenerationPending:
initialState.isMetadataGenerationPending ?? false,
isMetadataGenerationSuccess:
initialState.isMetadataGenerationSuccess ?? false,
isMetadataGenerationIdle: initialState.isMetadataGenerationIdle ?? true,
metadataGenerationData: metadataData,
isMetadataGenerationError:
initialState.isMetadataGenerationError ?? false,
metadataGenerationError: initialState.metadataGenerationError ?? null,
};
if (!metadata) {
return;
}

if (metadata?.role === 'assistant' && metadata?.status.type === 'running') {
setMetadataState('pending');
return;
}
const isMetadataGenerationIdle = metadata == null;
const isMetadataGenerationPending =
metadata?.role === 'assistant' && metadata?.status.type === 'running';
const isMetadataGenerationSuccess =
metadata?.role === 'assistant' && metadata?.status.type === 'complete';
let metadataGenerationData = null;
let metadataGenerationError: null | string = null;
if (isMetadataGenerationSuccess) {

if (
metadata?.role === 'assistant' &&
metadata?.status.type === 'complete'
) {
const jsonCodeMatch = metadata.content.match(/```json\n([\s\S]*?)\n```/);
if (jsonCodeMatch) {
try {
const parsedJson = JSON.parse(jsonCodeMatch[1].trim());
const parsedmetadataGenerationData = ToolMetadataSchema.parse(
const parsedMetadata = ToolMetadataSchema.parse(
parsedJson,
) as ToolMetadata;
metadataGenerationData = {
...parsedmetadataGenerationData,
setMetadataData({
...parsedMetadata,
author: auth?.shinkai_identity ?? '',
};
setMetadataData(metadataGenerationData);
});
setMetadataState('success');
setError(null);
} catch (error) {
if (error instanceof Error) {
metadataGenerationError = 'Invalid Metadata: ' + error.message;
}
if (error instanceof z.ZodError) {
metadataGenerationError =
'Invalid Metadata: ' +
error.issues.map((issue) => issue.message).join(', ');
}
setMetadataState('error');
setError(
error instanceof Error
? 'Invalid Metadata: ' + error.message
: 'Invalid Metadata: Unknown Error',
);
}
} else {
metadataGenerationError = 'No JSON code found';
setMetadataState('error');
setError('No JSON code found');
}
return;
}

return {
metadataMessageContent: metadata?.content,
isMetadataGenerationPending,
isMetadataGenerationSuccess,
isMetadataGenerationIdle,
metadataGenerationData,
isMetadataGenerationError: metadataGenerationError != null,
metadataGenerationError,
};
}, [initialState, metadataChatConversation?.pages]);
}, [auth?.shinkai_identity, metadataChatConversation?.pages]);

return {
isMetadataGenerationPending,
isMetadataGenerationSuccess,
isMetadataGenerationIdle,
metadataGenerationData,
metadataGenerationError,
isMetadataGenerationError,
isMetadataGenerationIdle: metadataState === 'idle',
isMetadataGenerationPending: metadataState === 'pending',
isMetadataGenerationSuccess: metadataState === 'success',
isMetadataGenerationError: metadataState === 'error',
metadataGenerationError: error,
metadataGenerationData: metadataData,
forceGenerateMetadata,
setMetadataData,
};
};
101 changes: 54 additions & 47 deletions apps/shinkai-desktop/src/pages/edit-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
Save,
} from 'lucide-react';
import { PrismEditor } from 'prism-react-editor';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { Link, To, useNavigate, useParams } from 'react-router-dom';
import { toast } from 'sonner';
Expand Down Expand Up @@ -91,7 +91,11 @@ function EditToolPage() {

const [isDirty, setIsDirty] = useState(false);
const [toolResult, setToolResult] = useState<object | null>(null);
const [chatInboxId, setChatInboxId] = useState<string | undefined>(undefined);

const chatInboxId = playgroundTool
? buildInboxIdFromJobId(playgroundTool.job_id)
: '';

const [chatInboxIdMetadata, setChatInboxIdMetadata] = useState<
string | undefined
>(undefined);
Expand Down Expand Up @@ -121,6 +125,27 @@ function EditToolPage() {
playgroundTool?.metadata?.parameters ?? {},
);

const initialState = useMemo(
() => ({
metadata: playgroundTool?.metadata ?? null,
state: isPlaygroundToolPending
? 'pending'
: isPlaygroundToolError || !isValidSchema
? 'error'
: isPlaygroundToolSuccess
? 'success'
: ('idle' as 'idle' | 'pending' | 'success' | 'error'),
error: isValidSchema ? null : 'Tool Metadata doesnt follow the schema',
}),
[
playgroundTool?.metadata,
isPlaygroundToolPending,
isPlaygroundToolSuccess,
isPlaygroundToolError,
isValidSchema,
],
);

const {
isMetadataGenerationPending,
isMetadataGenerationSuccess,
Expand All @@ -129,20 +154,10 @@ function EditToolPage() {
metadataGenerationError,
isMetadataGenerationError,
forceGenerateMetadata,
// setMetadataData,
} = useToolMetadata({
chatInboxIdMetadata,
code: toolCode,
initialState: {
metadata: playgroundTool?.metadata ?? null,
isMetadataGenerationPending: isPlaygroundToolPending,
isMetadataGenerationSuccess: isPlaygroundToolSuccess,
isMetadataGenerationIdle: false,
isMetadataGenerationError: isPlaygroundToolError || !isValidSchema,
metadataGenerationError: isValidSchema
? null
: 'Tool Metadata doesnt follow the schema',
},
initialState,
});

const form = useForm<CreateToolCodeFormSchema>({
Expand Down Expand Up @@ -215,13 +230,6 @@ function EditToolPage() {
form.setValue('llmProviderId', defaultAgentId);
}, [form, defaultAgentId]);

useEffect(() => {
if (playgroundTool) {
setToolCode(playgroundTool.code);
setChatInboxId(buildInboxIdFromJobId(playgroundTool.job_id));
}
}, [playgroundTool]);

useEffect(() => {
if (!toolCode || !forceGenerateMetadata.current) return;
const run = async () => {
Expand Down Expand Up @@ -250,18 +258,18 @@ function EditToolPage() {
]);

const onSubmit = async (data: CreateToolCodeFormSchema) => {
if (!auth) return;
if (!auth || !chatInboxId) return;
await createToolCode(
{
nodeAddress: auth.node_address,
token: auth.api_v2_key,
message: data.message,
llmProviderId: data.llmProviderId,
tools: data.tools,
jobId: playgroundTool?.job_id,
},
{
onSuccess: (data) => {
setChatInboxId(data.inbox);
onSuccess: () => {
setToolCode('');
forceGenerateMetadata.current = true;
baseToolCodeRef.current = '';
Expand Down Expand Up @@ -647,7 +655,7 @@ function EditToolPage() {

<ResizablePanel className="flex flex-col">
<div className="flex size-full min-h-[220px] flex-col rounded-lg bg-gray-300 pb-4 pl-4 pr-3">
<div className="text-gray-80 flex flex-col gap-1 py-3 text-xs">
<div className="text-gray-80 flex h-full flex-col gap-1 py-3 text-xs">
<div className="flex items-center justify-between">
<div className="text-gray-80 flex flex-col gap-1 py-3 text-xs">
<h2 className="flex font-mono font-semibold text-gray-50">
Expand Down Expand Up @@ -682,36 +690,35 @@ function EditToolPage() {
Generating...
</div>
)}
{!isMetadataGenerationPending &&
!isToolCodeGenerationPending &&
{!isToolCodeGenerationPending &&
isMetadataGenerationError && (
<ToolErrorFallback
error={new Error(metadataGenerationError ?? '')}
resetErrorBoundary={regenerateToolMetadata}
/>
)}
{isMetadataGenerationSuccess &&
!isToolCodeGenerationPending &&
!isMetadataGenerationError &&
isValidSchema && (
{!isToolCodeGenerationPending &&
isMetadataGenerationSuccess && (
<div className="text-gray-80 text-xs">
<JsonForm
className="py-4"
formData={formData}
id="parameters-form"
noHtml5Validate={true}
onChange={(e) => setFormData(e.formData)}
onSubmit={handleRunCode}
schema={
metadataGenerationData?.parameters as RJSFSchema
}
uiSchema={{
'ui:submitButtonOptions': {
norender: true,
},
}}
validator={validator}
/>
{!isMetadataGenerationPending && (
<JsonForm
className="py-4"
formData={formData}
id="parameters-form"
noHtml5Validate={true}
onChange={(e) => setFormData(e.formData)}
onSubmit={handleRunCode}
schema={
metadataGenerationData?.parameters as RJSFSchema
}
uiSchema={{
'ui:submitButtonOptions': {
norender: true,
},
}}
validator={validator}
/>
)}
<AnimatePresence>
{(isExecutingCode ||
isCodeExecutionError ||
Expand Down
Loading

0 comments on commit 2e9e7b4

Please sign in to comment.