Skip to content

Commit

Permalink
Instructions enhancer ui (#1902)
Browse files Browse the repository at this point in the history
- Playground instructions ui
- Expose getModel for use in enhancer (#1900)
- chore: version packages (alpha) (#1901)
- Instructions enhancer

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 13, 2025
1 parent b9c7047 commit 5b5de5e
Show file tree
Hide file tree
Showing 10 changed files with 867 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-badgers-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mastra': patch
---

Instructions playground
46 changes: 46 additions & 0 deletions packages/cli/src/playground/src/components/ui/code-display.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ScrollArea } from '@/components/ui/scroll-area';

interface CodeDisplayProps {
content: string;
height?: string;
isCopied?: boolean;
isDraft?: boolean;
onCopy?: () => void;
className?: string;
}

export function CodeDisplay({
content,
height = '150px',
isCopied = false,
isDraft = false,
onCopy,
className = '',
}: CodeDisplayProps) {
return (
<div className={`rounded-md border bg-mastra-bg-2 ${className}`} style={{ height }}>
<ScrollArea className="h-full">
<div className="p-2 cursor-pointer hover:bg-mastra-bg-3/50 transition-colors group relative" onClick={onCopy}>
<pre className="text-[10px] whitespace-pre-wrap font-mono">{content}</pre>
{isDraft && (
<div className="mt-1.5">
<span className="text-[10px] px-1.5 py-0.5 rounded-full bg-yellow-500/20 text-yellow-500">
Draft - Save changes to apply
</span>
</div>
)}
{isCopied && (
<span className="absolute top-2 right-2 text-[10px] px-1.5 py-0.5 rounded-full bg-green-500/20 text-green-500">
Copied!
</span>
)}
{onCopy && (
<span className="absolute top-2 right-2 text-[10px] px-1.5 py-0.5 rounded-full bg-mastra-bg-3 text-mastra-el-4 opacity-0 group-hover:opacity-100 transition-opacity">
Click to copy
</span>
)}
</div>
</ScrollArea>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';

import { AgentDetails } from './agent-details';
import { AgentEndpoints } from './agent-endpoints';
import { AgentPromptEnhancer } from './agent-instructions-enhancer';
import { AgentLogs } from './agent-logs';

export function AgentInformation({ agentId }: { agentId: string }) {
Expand All @@ -13,6 +14,11 @@ export function AgentInformation({ agentId }: { agentId: string }) {
Details
</p>
</TabsTrigger>
<TabsTrigger value="instructions" className="group">
<p className="text-xs p-3 text-mastra-el-3 group-data-[state=active]:text-mastra-el-5 group-data-[state=active]:border-b-2 group-data-[state=active]:pb-2.5 border-white">
Instructions
</p>
</TabsTrigger>
<TabsTrigger value="endpoints" className="group">
<p className="text-xs p-3 text-mastra-el-3 group-data-[state=active]:text-mastra-el-5 group-data-[state=active]:border-b-2 group-data-[state=active]:pb-2.5 border-white">
Endpoints
Expand All @@ -31,6 +37,9 @@ export function AgentInformation({ agentId }: { agentId: string }) {
<TabsContent value="logs">
<AgentLogs agentId={agentId} />
</TabsContent>
<TabsContent value="instructions">
<AgentPromptEnhancer agentId={agentId} />
</TabsContent>
</Tabs>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { AlertDialog } from '@/components/ui/alert-dialog';

import { useAgent } from '@/hooks/use-agents';

import { CurrentInstructions } from './components/current-instructions';
import { VersionHistory } from './components/version-history';
import { usePromptEnhancer } from './hooks/use-prompt-enhancer';
import { usePromptVersions } from './hooks/use-prompt-versions';

interface AgentPromptEnhancerProps {
agentId: string;
}

export function AgentPromptEnhancer({ agentId }: AgentPromptEnhancerProps) {
const { agent } = useAgent(agentId);

const {
versions,
isUpdating,
versionToDelete,
setVersions,
setVersionToDelete,
deleteVersion,
updateVersion,
setVersionActive,
} = usePromptVersions(agentId, agent?.instructions);

const {
enhancedPrompt,
isEnhancing,
userComment,
showCommentInput,
enhancePrompt,
setUserComment,
setShowCommentInput,
clearEnhancement,
applyChanges,
} = usePromptEnhancer({
agentId,
instructions: agent?.instructions,
versions,
onVersionCreate: newVersion => {
setVersions(prev => [...prev, newVersion]);
},
onVersionUpdate: updateVersion,
});

return (
<div className="grid p-4 h-full">
<div className="space-y-2">
<CurrentInstructions
instructions={agent?.instructions}
enhancedPrompt={enhancedPrompt}
isEnhancing={isEnhancing}
showCommentInput={showCommentInput}
userComment={userComment}
onEnhance={enhancePrompt}
onCancel={clearEnhancement}
onSave={applyChanges}
onCommentToggle={() => setShowCommentInput(!showCommentInput)}
onCommentChange={setUserComment}
/>

<VersionHistory
versions={versions}
isUpdating={isUpdating}
copiedVersions={{}}
onCopy={async () => {}}
onSetActive={setVersionActive}
onDelete={setVersionToDelete}
/>
</div>

<AlertDialog open={versionToDelete !== null} onOpenChange={() => setVersionToDelete(null)}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Delete Version {versionToDelete !== null ? versionToDelete + 1 : ''}</AlertDialog.Title>
<AlertDialog.Description>
Are you sure you want to delete this version? This action cannot be undone.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<AlertDialog.Action
className="bg-red-500 hover:bg-red-600"
onClick={() => {
if (versionToDelete !== null) {
deleteVersion(versionToDelete);
}
}}
>
Delete
</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Terminal, Wand2, MessageSquare } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { CodeDisplay } from '@/components/ui/code-display';
import { Textarea } from '@/components/ui/textarea';

import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';

interface CurrentInstructionsProps {
instructions?: string;
enhancedPrompt: string;
isEnhancing: boolean;
showCommentInput: boolean;
userComment: string;
onEnhance: () => void;
onCancel: () => void;
onSave: () => void;
onCommentToggle: () => void;
onCommentChange: (comment: string) => void;
}

export function CurrentInstructions({
instructions,
enhancedPrompt,
isEnhancing,
showCommentInput,
userComment,
onEnhance,
onCancel,
onSave,
onCommentToggle,
onCommentChange,
}: CurrentInstructionsProps) {
const currentContent = enhancedPrompt || instructions?.trim();

const { isCopied, handleCopy } = useCopyToClipboard({ text: currentContent || '' });

return (
<div>
<div className="flex items-center justify-between mb-1.5">
<div>
<h3 className="text-sm font-medium text-mastra-el-5"> Current Instructions </h3>
</div>

<Button
variant="default"
size="sm"
onClick={onEnhance}
disabled={isEnhancing || !instructions}
className="bg-[#6366F1] hover:bg-[#6366F1]/90 text-white font-medium"
>
{isEnhancing ? (
<>
<Terminal className="mr-2 h-3 w-3 animate-spin" />
Enhancing...
</>
) : (
<>
<Wand2 className="mr-2 h-3 w-3" />
Enhance
</>
)}
</Button>
</div>

<div className="space-y-2">
<CodeDisplay
content={currentContent || ''}
isCopied={isCopied}
isDraft={!!enhancedPrompt}
onCopy={() => currentContent && handleCopy()}
/>
</div>

<div className="flex items-center justify-between mt-1.5">
{enhancedPrompt && (
<div className="flex space-x-1.5">
<Button variant="outline" onClick={onCancel}>
Cancel
</Button>
<Button onClick={onSave} disabled={!enhancedPrompt}>
Save
</Button>
</div>
)}
<Button variant="ghost" size="sm" onClick={onCommentToggle} className="text-mastra-el-4 hover:text-mastra-el-5">
<MessageSquare className="h-3 w-3 mr-1" />
{showCommentInput ? 'Hide Comment' : 'Add Comment'}
</Button>
</div>

{showCommentInput && (
<div className="mt-1.5 bg-mastra-bg-1/50 p-2 rounded-lg border border-mastra-bg-3 shadow-sm">
<Textarea
value={userComment}
onChange={e => onCommentChange(e.target.value)}
placeholder="Add your comments or requirements for enhancing the prompt..."
className="w-full h-16 px-3 py-2 text-xs rounded-md bg-mastra-bg-2 border border-mastra-bg-3 text-mastra-el-5 placeholder:text-mastra-el-3 focus:outline-none focus:ring-2 focus:ring-[#6366F1]/30 focus:border-[#6366F1] transition-all"
/>
</div>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Play, Trash2 } from 'lucide-react';

import { Button } from '@/components/ui/button';

import type { VersionActionsProps } from '../types';

export function VersionActions({
version,
index,
isUpdating,
onSetActive,
onDelete,
}: Omit<VersionActionsProps, 'onCopy' | 'copiedVersions'>) {
return (
<div className="flex items-center space-x-1">
{version.status !== 'active' && version.status !== 'draft' && (
<Button
variant="ghost"
size="sm"
className="h-6 px-2 hover:bg-mastra-bg-3 relative group"
onClick={e => {
e.stopPropagation();
onSetActive(version, index);
}}
disabled={isUpdating}
>
<Play className="h-3 w-3" />
</Button>
)}
{index !== 0 && version.status !== 'active' && (
<Button
variant="ghost"
size="sm"
className="h-6 px-2 hover:bg-mastra-bg-3 relative group"
onClick={e => {
e.stopPropagation();
onDelete(index);
}}
>
<Trash2 className="h-3 w-3 text-red-400 hover:text-red-500" />
</Button>
)}
</div>
);
}
Loading

0 comments on commit 5b5de5e

Please sign in to comment.