Skip to content

Commit c5705d9

Browse files
Agent builder auto generate prompt (#1372)
1 parent 62c50ec commit c5705d9

File tree

9 files changed

+606
-46
lines changed

9 files changed

+606
-46
lines changed

packages/web/public/locales/translation/en.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ agent_builder:
4040
description_placeholder: Describe what your agent does...
4141
description_too_long: Description must be 500 characters or less
4242
edit_agent: Edit Agent
43+
editor: Editor
4344
enable_code_execution: Enabling code execution
4445
enter_agent_name: Enter the agent name
4546
enter_system_prompt: Enter a system prompt that defines the agent behavior
@@ -59,6 +60,9 @@ agent_builder:
5960
failed_to_update_agent: Failed to update agent
6061
favorites: Favorites
6162
filter_by_tag: Filter by tag
63+
generate_short: Generate
64+
generate_with_ai: Generate with AI
65+
generating_prompt: Generating system prompt...
6266
loading_agent: Loading agent...
6367
mcp_server_configuration: MCP Server Configuration
6468
mcp_server_description: >-
@@ -78,6 +82,10 @@ agent_builder:
7882
no_mcp_servers_match_filter: No MCP servers match your current filter.
7983
no_mcp_servers_selected: No MCP servers selected. Your agent will have basic functionality only.
8084
no_public_agents_available: No public agents available
85+
overwrite_confirm: Overwrite
86+
overwrite_prompt_message: A system prompt already exists. Do you want to overwrite it with AI-generated prompt?
87+
overwrite_prompt_title: Overwrite System Prompt Confirmation
88+
preview: Preview
8189
public: Public Agents
8290
public_sharing_description: >-
8391
Make this agent available on public agent directories and be discovered and

packages/web/public/locales/translation/ja.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ agent_builder:
4141
description_placeholder: エージェントの機能を説明してください...
4242
description_too_long: 説明は500文字以下である必要があります
4343
edit_agent: エージェントを編集
44+
editor: エディター
4445
enable_code_execution: コード実行を有効にする
4546
enter_agent_name: エージェント名を入力
4647
enter_system_prompt: エージェントの動作を定義するシステムプロンプトを入力
@@ -59,6 +60,9 @@ agent_builder:
5960
failed_to_update_agent: エージェントの更新に失敗しました
6061
favorites: お気に入り
6162
filter_by_tag: タグでフィルター
63+
generate_short: 生成
64+
generate_with_ai: AIにより生成する
65+
generating_prompt: システムプロンプトを生成中...
6266
loading_agent: エージェントを読み込み中...
6367
mcp_server_configuration: MCPサーバー設定
6468
mcp_server_description: エージェントに追加機能を提供するMCPサーバーを選択してください。これらのサーバーはセキュリティのため管理者によって事前設定されています。
@@ -76,6 +80,10 @@ agent_builder:
7680
no_mcp_servers_match_filter: 現在のフィルターに一致するMCPサーバーがありません。
7781
no_mcp_servers_selected: MCPサーバーが選択されていません。エージェントは基本機能のみ利用できます。
7882
no_public_agents_available: 利用可能な公開エージェントがありません
83+
overwrite_confirm: 上書きする
84+
overwrite_prompt_message: すでにシステムプロンプトが入力されています。AIで生成したプロンプトで上書きしますか?
85+
overwrite_prompt_title: システムプロンプトの上書き確認
86+
preview: プレビュー
7987
public: 公開エージェント
8088
public_sharing_description: >-
8189
このエージェントを公開し、他のユーザーが発見して利用できるようにします。エージェントはすべてのユーザーに表示されますが、オリジナルを変更することはできません。

packages/web/src/components/agentBuilder/AgentForm.tsx

Lines changed: 120 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
import React, { useState, useCallback, useEffect } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import Button from '../Button';
4+
import ButtonIcon from '../ButtonIcon';
45
import InputText from '../InputText';
56
import Textarea from '../Textarea';
67
import Select from '../Select';
78
import MCPServerManager from './MCPServerManager';
9+
import ModalDialog from '../ModalDialog';
810
import { MODELS } from '../../hooks/useModel';
911
import { AgentConfiguration } from 'generative-ai-use-cases';
12+
import usePromptGeneration from '../../hooks/usePromptGeneration';
13+
import useMCPServers from '../../hooks/useMCPServers';
14+
import { PiSparkle, PiStop } from 'react-icons/pi';
1015

1116
export interface AgentFormData {
1217
name: string;
1318
description: string;
1419
systemPrompt: string;
1520
modelId: string;
16-
mcpServers: string[]; // Changed to string array
21+
mcpServers: string[];
1722
codeExecutionEnabled: boolean;
1823
isPublic: boolean;
1924
tags: string[];
@@ -55,6 +60,38 @@ const AgentForm: React.FC<AgentFormProps> = ({
5560
});
5661

5762
const [tagsInput, setTagsInput] = useState('');
63+
const [showOverwriteDialog, setShowOverwriteDialog] = useState(false);
64+
65+
// Load available MCP servers using the shared hook
66+
const availableMCPServers = useMCPServers();
67+
68+
// Use the prompt generation hook
69+
const {
70+
generatedPrompt,
71+
suggestedMCPServers,
72+
isGenerating,
73+
generate: generatePrompt,
74+
cancel: cancelGeneration,
75+
} = usePromptGeneration({
76+
modelId: formData.modelId,
77+
agentName: formData.name,
78+
agentDescription: formData.description,
79+
availableMCPServers,
80+
});
81+
82+
// Update systemPrompt when generation produces new content
83+
useEffect(() => {
84+
if (generatedPrompt) {
85+
setFormData((prev) => ({ ...prev, systemPrompt: generatedPrompt }));
86+
}
87+
}, [generatedPrompt]);
88+
89+
// Update MCP servers when AI suggests them
90+
useEffect(() => {
91+
if (suggestedMCPServers.length > 0) {
92+
setFormData((prev) => ({ ...prev, mcpServers: suggestedMCPServers }));
93+
}
94+
}, [suggestedMCPServers]);
5895

5996
// Update formData.modelId when availableModels becomes available
6097
useEffect(() => {
@@ -86,7 +123,6 @@ const AgentForm: React.FC<AgentFormProps> = ({
86123
// Notify parent component when form data changes
87124
useEffect(() => {
88125
if (onFormDataChange) {
89-
// Parse tags from input for real-time updates
90126
const tags = tagsInput
91127
.split(',')
92128
.map((tag) => tag.trim())
@@ -103,7 +139,6 @@ const AgentForm: React.FC<AgentFormProps> = ({
103139

104140
const handleSave = useCallback(async () => {
105141
try {
106-
// Parse tags from input
107142
const tags = tagsInput
108143
.split(',')
109144
.map((tag) => tag.trim())
@@ -115,11 +150,34 @@ const AgentForm: React.FC<AgentFormProps> = ({
115150
};
116151

117152
await onSave(agentData);
118-
} catch (error) {
119-
console.error('Error saving agent:', error);
153+
} catch (err) {
154+
console.error('Error saving agent:', err);
120155
}
121156
}, [formData, tagsInput, onSave]);
122157

158+
const handleGenerateClick = useCallback(() => {
159+
if (formData.systemPrompt.trim()) {
160+
setShowOverwriteDialog(true);
161+
} else {
162+
setFormData((prev) => ({ ...prev, systemPrompt: '' }));
163+
generatePrompt();
164+
}
165+
}, [formData.systemPrompt, generatePrompt]);
166+
167+
const handleConfirmOverwrite = useCallback(() => {
168+
setShowOverwriteDialog(false);
169+
setFormData((prev) => ({ ...prev, systemPrompt: '' }));
170+
generatePrompt();
171+
}, [generatePrompt]);
172+
173+
const handleCancelGeneration = useCallback(() => {
174+
cancelGeneration();
175+
}, [cancelGeneration]);
176+
177+
// Check if generate button should be disabled
178+
const isGenerateDisabled =
179+
!formData.name.trim() || !formData.description.trim() || loading;
180+
123181
const isFormValid =
124182
formData.name && formData.systemPrompt && formData.modelId;
125183

@@ -202,10 +260,33 @@ const AgentForm: React.FC<AgentFormProps> = ({
202260

203261
{/* System Prompt */}
204262
<div className="rounded-lg border bg-white p-6">
205-
{/* eslint-disable-next-line @shopify/jsx-no-hardcoded-content */}
206-
<h2 className="mb-4 text-lg font-semibold">
207-
{t('agent_builder.system_prompt')} {'*'}
208-
</h2>
263+
<div className="mb-4 flex items-center justify-between">
264+
{/* eslint-disable-next-line @shopify/jsx-no-hardcoded-content */}
265+
<h2 className="text-lg font-semibold">
266+
{t('agent_builder.system_prompt')} {'*'}
267+
</h2>
268+
<div className="flex items-center gap-2">
269+
{isGenerating ? (
270+
<ButtonIcon onClick={handleCancelGeneration}>
271+
<PiStop className="text-red-500" />
272+
</ButtonIcon>
273+
) : (
274+
<Button
275+
onClick={handleGenerateClick}
276+
outlined
277+
disabled={isGenerateDisabled || isGenerating}
278+
className="flex items-center gap-1 text-sm">
279+
<PiSparkle />
280+
<span className="hidden sm:inline">
281+
{t('agent_builder.generate_with_ai')}
282+
</span>
283+
<span className="sm:hidden">
284+
{t('agent_builder.generate_short')}
285+
</span>
286+
</Button>
287+
)}
288+
</div>
289+
</div>
209290

210291
<Textarea
211292
value={formData.systemPrompt}
@@ -214,9 +295,35 @@ const AgentForm: React.FC<AgentFormProps> = ({
214295
}
215296
placeholder={t('agent_builder.enter_system_prompt')}
216297
rows={12}
298+
disabled={isGenerating || loading}
217299
/>
300+
{isGenerating && (
301+
<p className="mt-2 text-sm text-gray-500">
302+
{t('agent_builder.generating_prompt')}
303+
</p>
304+
)}
218305
</div>
219306

307+
{/* Overwrite Confirmation Dialog */}
308+
<ModalDialog
309+
isOpen={showOverwriteDialog}
310+
title={t('agent_builder.overwrite_prompt_title')}
311+
onClose={() => setShowOverwriteDialog(false)}>
312+
<div className="space-y-4">
313+
<p className="text-gray-700">
314+
{t('agent_builder.overwrite_prompt_message')}
315+
</p>
316+
<div className="flex justify-end gap-3">
317+
<Button outlined onClick={() => setShowOverwriteDialog(false)}>
318+
{t('common.cancel')}
319+
</Button>
320+
<Button onClick={handleConfirmOverwrite}>
321+
{t('agent_builder.overwrite_confirm')}
322+
</Button>
323+
</div>
324+
</div>
325+
</ModalDialog>
326+
220327
{/* MCP Server Configuration */}
221328
<div className="rounded-lg border bg-white p-6">
222329
<MCPServerManager
@@ -288,10 +395,12 @@ const AgentForm: React.FC<AgentFormProps> = ({
288395

289396
{/* Form Actions */}
290397
<div className="flex justify-end gap-3">
291-
<Button outlined onClick={onCancel}>
398+
<Button outlined onClick={onCancel} disabled={isGenerating}>
292399
{t('common.cancel')}
293400
</Button>
294-
<Button onClick={handleSave} disabled={loading || !isFormValid}>
401+
<Button
402+
onClick={handleSave}
403+
disabled={loading || !isFormValid || isGenerating}>
295404
{isEditMode ? t('common.update') : t('common.create')}
296405
</Button>
297406
</div>

packages/web/src/components/agentBuilder/MCPServerManager.tsx

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,13 @@ import React, { useState, useEffect, useMemo } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { PiInfo, PiMagnifyingGlass, PiX } from 'react-icons/pi';
44
import { AvailableMCPServer } from 'generative-ai-use-cases';
5+
import useMCPServers from '../../hooks/useMCPServers';
56

67
interface MCPServerManagerProps {
78
servers: string[];
89
onChange: (servers: string[]) => void;
910
}
1011

11-
// Load MCP servers from environment variable
12-
const loadMCPServersFromEnv = (): AvailableMCPServer[] => {
13-
try {
14-
const mcpConfig = import.meta.env.VITE_APP_MCP_SERVERS_CONFIG;
15-
if (!mcpConfig) {
16-
console.warn('VITE_APP_MCP_SERVERS_CONFIG not found, using fallback');
17-
return [];
18-
}
19-
20-
const parsedConfig = JSON.parse(mcpConfig);
21-
return Object.keys(parsedConfig).map((serverName) => {
22-
const metadata = parsedConfig[serverName]?.metadata || {};
23-
return {
24-
name: serverName,
25-
description: metadata.description || `MCP server: ${serverName}`,
26-
category: metadata.category || 'Other',
27-
};
28-
});
29-
} catch (error) {
30-
console.error('Error parsing MCP servers config:', error);
31-
return [];
32-
}
33-
};
34-
3512
const MCPServerManager: React.FC<MCPServerManagerProps> = ({
3613
servers,
3714
onChange,
@@ -43,8 +20,8 @@ const MCPServerManager: React.FC<MCPServerManagerProps> = ({
4320
const [searchQuery, setSearchQuery] = useState('');
4421
const [selectedCategory, setSelectedCategory] = useState<string>('all');
4522

46-
// Load available MCP servers from environment
47-
const AVAILABLE_MCP_SERVERS = useMemo(() => loadMCPServersFromEnv(), []);
23+
// Load available MCP servers from environment using the shared hook
24+
const AVAILABLE_MCP_SERVERS = useMCPServers();
4825

4926
// Initialize selected servers from props
5027
useEffect(() => {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* useMCPServers Hook
3+
*
4+
* Provides MCP server configuration loaded from environment variables.
5+
* Consolidates the logic previously duplicated in AgentForm and MCPServerManager.
6+
*/
7+
8+
import { useMemo } from 'react';
9+
import { AvailableMCPServer } from 'generative-ai-use-cases';
10+
11+
/**
12+
* Loads MCP servers configuration from the VITE_APP_MCP_SERVERS_CONFIG environment variable.
13+
*
14+
* @returns Array of available MCP servers with name, description, and category
15+
*/
16+
export const loadMCPServersFromEnv = (): AvailableMCPServer[] => {
17+
try {
18+
const mcpConfig = import.meta.env.VITE_APP_MCP_SERVERS_CONFIG;
19+
if (!mcpConfig) {
20+
console.warn('VITE_APP_MCP_SERVERS_CONFIG not found');
21+
return [];
22+
}
23+
24+
const parsedConfig = JSON.parse(mcpConfig);
25+
return Object.keys(parsedConfig).map((serverName) => {
26+
const metadata = parsedConfig[serverName]?.metadata || {};
27+
return {
28+
name: serverName,
29+
description: metadata.description || `MCP server: ${serverName}`,
30+
category: metadata.category || 'Other',
31+
};
32+
});
33+
} catch (error) {
34+
console.error('Error parsing MCP servers config:', error);
35+
return [];
36+
}
37+
};
38+
39+
/**
40+
* Custom hook that provides memoized MCP server configurations.
41+
*
42+
* @returns Memoized array of available MCP servers
43+
*/
44+
export const useMCPServers = (): AvailableMCPServer[] => {
45+
return useMemo(() => loadMCPServersFromEnv(), []);
46+
};
47+
48+
export default useMCPServers;

0 commit comments

Comments
 (0)