chore(ui): Reuse FormFieldDefinition on other parts of AgentForm

This commit is contained in:
Richard Palethorpe
2025-03-26 10:00:41 +00:00
parent d520d88301
commit 7fb99ecf21
8 changed files with 357 additions and 340 deletions

View File

@@ -1,82 +1,82 @@
import React from 'react'; import React from 'react';
import FormFieldDefinition from '../common/FormFieldDefinition';
/** /**
* Advanced Settings section of the agent form * Advanced Settings section of the agent form
*/ */
const AdvancedSettingsSection = ({ formData, handleInputChange }) => { const AdvancedSettingsSection = ({ formData, handleInputChange }) => {
// Define field definitions for Advanced Settings section
const fields = [
{
name: 'max_steps',
label: 'Max Steps',
type: 'number',
defaultValue: 10,
helpText: 'Maximum number of steps the agent can take',
required: true,
},
{
name: 'max_iterations',
label: 'Max Iterations',
type: 'number',
defaultValue: 5,
helpText: 'Maximum number of iterations for each step',
required: true,
},
{
name: 'autonomous',
label: 'Autonomous Mode',
type: 'checkbox',
defaultValue: false,
helpText: 'Allow the agent to operate autonomously',
},
{
name: 'verbose',
label: 'Verbose Mode',
type: 'checkbox',
defaultValue: false,
helpText: 'Enable detailed logging',
},
{
name: 'allow_code_execution',
label: 'Allow Code Execution',
type: 'checkbox',
defaultValue: false,
helpText: 'Allow the agent to execute code (use with caution)',
},
];
// Handle field value changes
const handleFieldChange = (name, value) => {
// For checkboxes, convert string 'true'/'false' to boolean
if (['autonomous', 'verbose', 'allow_code_execution'].includes(name)) {
handleInputChange({
target: {
name,
type: 'checkbox',
checked: value === 'true'
}
});
} else {
handleInputChange({
target: {
name,
value
}
});
}
};
return ( return (
<div id="advanced-section"> <div id="advanced-section">
<h3 className="section-title">Advanced Settings</h3> <h3 className="section-title">Advanced Settings</h3>
<div className="mb-4"> <FormFieldDefinition
<label htmlFor="max_steps">Max Steps</label> fields={fields}
<input values={formData}
type="number" onChange={handleFieldChange}
name="max_steps" idPrefix="advanced_"
id="max_steps"
min="1"
value={formData.max_steps || 10}
onChange={handleInputChange}
className="form-control"
/> />
<small className="form-text text-muted">Maximum number of steps the agent can take</small>
</div>
<div className="mb-4">
<label htmlFor="max_iterations">Max Iterations</label>
<input
type="number"
name="max_iterations"
id="max_iterations"
min="1"
value={formData.max_iterations || 5}
onChange={handleInputChange}
className="form-control"
/>
<small className="form-text text-muted">Maximum number of iterations for each step</small>
</div>
<div className="mb-4">
<label htmlFor="autonomous" className="checkbox-label">
<input
type="checkbox"
name="autonomous"
id="autonomous"
checked={formData.autonomous || false}
onChange={handleInputChange}
/>
Autonomous Mode
</label>
<small className="form-text text-muted">Allow the agent to operate autonomously</small>
</div>
<div className="mb-4">
<label htmlFor="verbose" className="checkbox-label">
<input
type="checkbox"
name="verbose"
id="verbose"
checked={formData.verbose || false}
onChange={handleInputChange}
/>
Verbose Mode
</label>
<small className="form-text text-muted">Enable detailed logging</small>
</div>
<div className="mb-4">
<label htmlFor="allow_code_execution" className="checkbox-label">
<input
type="checkbox"
name="allow_code_execution"
id="allow_code_execution"
checked={formData.allow_code_execution || false}
onChange={handleInputChange}
/>
Allow Code Execution
</label>
<small className="form-text text-muted">Allow the agent to execute code (use with caution)</small>
</div>
</div> </div>
); );
}; };

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import FormFieldDefinition from '../common/FormFieldDefinition';
/** /**
* Basic Information section of the agent form * Basic Information section of the agent form
@@ -9,69 +10,74 @@ const BasicInfoSection = ({ formData, handleInputChange, isEdit, isGroupForm })
return null; return null;
} }
// Define field definitions for Basic Information section
const fields = [
{
name: 'name',
label: 'Name',
type: 'text',
defaultValue: '',
required: true,
helpText: isEdit ? 'Agent name cannot be changed after creation' : '',
disabled: isEdit, // This will be handled in the component
},
{
name: 'description',
label: 'Description',
type: 'textarea',
defaultValue: '',
},
{
name: 'identity_guidance',
label: 'Identity Guidance',
type: 'textarea',
defaultValue: '',
},
{
name: 'random_identity',
label: 'Random Identity',
type: 'checkbox',
defaultValue: false,
},
{
name: 'hud',
label: 'HUD',
type: 'checkbox',
defaultValue: false,
}
];
// Handle field value changes
const handleFieldChange = (name, value) => {
// For checkboxes, convert string 'true'/'false' to boolean
if (name === 'random_identity' || name === 'hud') {
handleInputChange({
target: {
name,
type: 'checkbox',
checked: value === 'true'
}
});
} else {
handleInputChange({
target: {
name,
value
}
});
}
};
return ( return (
<div id="basic-section"> <div id="basic-section">
<h3 className="section-title">Basic Information</h3> <h3 className="section-title">Basic Information</h3>
<div className="mb-4"> <FormFieldDefinition
<label htmlFor="name">Name</label> fields={fields}
<input values={formData}
type="text" onChange={handleFieldChange}
name="name" idPrefix="basic_"
id="name"
value={formData.name || ''}
onChange={handleInputChange}
required
disabled={isEdit} // Disable name field in edit mode
/> />
{isEdit && <small className="form-text text-muted">Agent name cannot be changed after creation</small>}
</div>
<div className="mb-4">
<label htmlFor="description">Description</label>
<textarea
name="description"
id="description"
value={formData.description || ''}
onChange={handleInputChange}
/>
</div>
<div className="mb-4">
<label htmlFor="identity_guidance">Identity Guidance</label>
<textarea
name="identity_guidance"
id="identity_guidance"
value={formData.identity_guidance || ''}
onChange={handleInputChange}
/>
</div>
<div className="mb-4">
<label htmlFor="random_identity" className="checkbox-label">
<input
type="checkbox"
name="random_identity"
id="random_identity"
checked={formData.random_identity || false}
onChange={handleInputChange}
/>
Random Identity
</label>
</div>
<div className="mb-4">
<label htmlFor="hud" className="checkbox-label">
<input
type="checkbox"
name="hud"
id="hud"
checked={formData.hud || false}
onChange={handleInputChange}
/>
HUD
</label>
</div>
</div> </div>
); );
}; };

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import FormFieldDefinition from '../common/FormFieldDefinition';
/** /**
* MCP Servers section of the agent form * MCP Servers section of the agent form
@@ -9,6 +10,28 @@ const MCPServersSection = ({
handleRemoveMCPServer, handleRemoveMCPServer,
handleMCPServerChange handleMCPServerChange
}) => { }) => {
// Define field definitions for each MCP server
const getServerFields = () => [
{
name: 'url',
label: 'URL',
type: 'text',
defaultValue: '',
placeholder: 'https://example.com/mcp',
},
{
name: 'token',
label: 'API Key',
type: 'password',
defaultValue: '',
},
];
// Handle field value changes for a specific server
const handleFieldChange = (index, name, value) => {
handleMCPServerChange(index, name, value);
};
return ( return (
<div id="mcp-section"> <div id="mcp-section">
<h3 className="section-title">MCP Servers</h3> <h3 className="section-title">MCP Servers</h3>
@@ -30,29 +53,13 @@ const MCPServersSection = ({
</button> </button>
</div> </div>
<div className="mb-3"> <FormFieldDefinition
<label htmlFor={`mcp-url-${index}`}>URL</label> fields={getServerFields()}
<input values={server}
type="text" onChange={(name, value) => handleFieldChange(index, name, value)}
id={`mcp-url-${index}`} idPrefix={`mcp_server_${index}_`}
value={server.url || ''}
onChange={(e) => handleMCPServerChange(index, 'url', e.target.value)}
className="form-control"
placeholder="https://example.com/mcp"
/> />
</div> </div>
<div className="mb-3">
<label htmlFor={`mcp-api-key-${index}`}>API Key</label>
<input
type="password"
id={`mcp-api-key-${index}`}
value={server.token || ''}
onChange={(e) => handleMCPServerChange(index, 'token', e.target.value)}
className="form-control"
/>
</div>
</div>
))} ))}
<button <button

View File

@@ -1,69 +1,68 @@
import React from 'react'; import React from 'react';
import FormFieldDefinition from '../common/FormFieldDefinition';
/** /**
* Memory Settings section of the agent form * Memory Settings section of the agent form
*/ */
const MemorySettingsSection = ({ formData, handleInputChange }) => { const MemorySettingsSection = ({ formData, handleInputChange }) => {
// Define field definitions for Memory Settings section
const fields = [
{
name: 'memory_provider',
label: 'Memory Provider',
type: 'select',
defaultValue: 'local',
options: [
{ value: 'local', label: 'Local' },
{ value: 'redis', label: 'Redis' },
{ value: 'postgres', label: 'PostgreSQL' },
],
},
{
name: 'memory_collection',
label: 'Memory Collection',
type: 'text',
defaultValue: '',
placeholder: 'agent_memories',
},
{
name: 'memory_url',
label: 'Memory URL',
type: 'text',
defaultValue: '',
placeholder: 'redis://localhost:6379',
helpText: 'Connection URL for Redis or PostgreSQL',
},
{
name: 'memory_window_size',
label: 'Memory Window Size',
type: 'number',
defaultValue: 10,
helpText: 'Number of recent messages to include in context window',
},
];
// Handle field value changes
const handleFieldChange = (name, value) => {
handleInputChange({
target: {
name,
value
}
});
};
return ( return (
<div id="memory-section"> <div id="memory-section">
<h3 className="section-title">Memory Settings</h3> <h3 className="section-title">Memory Settings</h3>
<div className="mb-4"> <FormFieldDefinition
<label htmlFor="memory_provider">Memory Provider</label> fields={fields}
<select values={formData}
name="memory_provider" onChange={handleFieldChange}
id="memory_provider" idPrefix="memory_"
value={formData.memory_provider || 'local'}
onChange={handleInputChange}
className="form-control"
>
<option value="local">Local</option>
<option value="redis">Redis</option>
<option value="postgres">PostgreSQL</option>
</select>
</div>
<div className="mb-4">
<label htmlFor="memory_collection">Memory Collection</label>
<input
type="text"
name="memory_collection"
id="memory_collection"
value={formData.memory_collection || ''}
onChange={handleInputChange}
className="form-control"
placeholder="agent_memories"
/> />
</div> </div>
<div className="mb-4">
<label htmlFor="memory_url">Memory URL</label>
<input
type="text"
name="memory_url"
id="memory_url"
value={formData.memory_url || ''}
onChange={handleInputChange}
className="form-control"
placeholder="redis://localhost:6379"
/>
<small className="form-text text-muted">Connection URL for Redis or PostgreSQL</small>
</div>
<div className="mb-4">
<label htmlFor="memory_window_size">Memory Window Size</label>
<input
type="number"
name="memory_window_size"
id="memory_window_size"
min="1"
value={formData.memory_window_size || 10}
onChange={handleInputChange}
className="form-control"
/>
<small className="form-text text-muted">Number of recent messages to include in context window</small>
</div>
</div>
); );
}; };

View File

@@ -1,83 +1,75 @@
import React from 'react'; import React from 'react';
import FormFieldDefinition from '../common/FormFieldDefinition';
/** /**
* Model Settings section of the agent form * Model Settings section of the agent form
*/ */
const ModelSettingsSection = ({ formData, handleInputChange }) => { const ModelSettingsSection = ({ formData, handleInputChange }) => {
// Define field definitions for Model Settings section
const fields = [
{
name: 'model',
label: 'Model',
type: 'text',
defaultValue: '',
},
{
name: 'multimodal_model',
label: 'Multimodal Model',
type: 'text',
defaultValue: '',
},
{
name: 'api_url',
label: 'API URL',
type: 'text',
defaultValue: '',
},
{
name: 'api_key',
label: 'API Key',
type: 'password',
defaultValue: '',
},
{
name: 'temperature',
label: 'Temperature',
type: 'number',
defaultValue: 0.7,
min: 0,
max: 2,
step: 0.1,
},
{
name: 'max_tokens',
label: 'Max Tokens',
type: 'number',
defaultValue: 2000,
min: 1,
},
];
// Handle field value changes
const handleFieldChange = (name, value) => {
handleInputChange({
target: {
name,
value
}
});
};
return ( return (
<div id="model-section"> <div id="model-section">
<h3 className="section-title">Model Settings</h3> <h3 className="section-title">Model Settings</h3>
<div className="mb-4"> <FormFieldDefinition
<label htmlFor="model">Model</label> fields={fields}
<input values={formData}
type="text" onChange={handleFieldChange}
name="model" idPrefix="model_"
id="model"
value={formData.model || ''}
onChange={handleInputChange}
/> />
</div> </div>
<div className="mb-4">
<label htmlFor="multimodal_model">Multimodal Model</label>
<input
type="text"
name="multimodal_model"
id="multimodal_model"
value={formData.multimodal_model || ''}
onChange={handleInputChange}
/>
</div>
<div className="mb-4">
<label htmlFor="api_url">API URL</label>
<input
type="text"
name="api_url"
id="api_url"
value={formData.api_url || ''}
onChange={handleInputChange}
/>
</div>
<div className="mb-4">
<label htmlFor="api_key">API Key</label>
<input
type="password"
name="api_key"
id="api_key"
value={formData.api_key || ''}
onChange={handleInputChange}
/>
</div>
<div className="mb-4">
<label htmlFor="temperature">Temperature</label>
<input
type="number"
name="temperature"
id="temperature"
min="0"
max="2"
step="0.1"
value={formData.temperature || 0.7}
onChange={handleInputChange}
/>
</div>
<div className="mb-4">
<label htmlFor="max_tokens">Max Tokens</label>
<input
type="number"
name="max_tokens"
id="max_tokens"
min="1"
value={formData.max_tokens || 2000}
onChange={handleInputChange}
/>
</div>
</div>
); );
}; };

View File

@@ -1,67 +1,78 @@
import React from 'react'; import React from 'react';
import FormFieldDefinition from '../common/FormFieldDefinition';
/** /**
* Prompts & Goals section of the agent form * Prompts & Goals section of the agent form
*/ */
const PromptsGoalsSection = ({ formData, handleInputChange, isGroupForm }) => { const PromptsGoalsSection = ({ formData, handleInputChange, isGroupForm }) => {
// In group form context, we hide the system prompt as it comes from each agent profile // Define field definitions for Prompts & Goals section
const getFields = () => {
// Base fields that are always shown
const baseFields = [
{
name: 'goals',
label: 'Goals',
type: 'textarea',
defaultValue: '',
helpText: 'Define the agent\'s goals (one per line)',
rows: 5,
},
{
name: 'constraints',
label: 'Constraints',
type: 'textarea',
defaultValue: '',
helpText: 'Define the agent\'s constraints (one per line)',
rows: 5,
},
{
name: 'tools',
label: 'Tools',
type: 'textarea',
defaultValue: '',
helpText: 'Define the agent\'s tools (one per line)',
rows: 5,
},
];
// Only include system_prompt field if not in group form context
if (!isGroupForm) {
return [
{
name: 'system_prompt',
label: 'System Prompt',
type: 'textarea',
defaultValue: '',
helpText: 'Instructions that define the agent\'s behavior',
rows: 5,
},
...baseFields
];
}
return baseFields;
};
// Handle field value changes
const handleFieldChange = (name, value) => {
handleInputChange({
target: {
name,
value
}
});
};
return ( return (
<div id="prompts-section"> <div id="prompts-section">
<h3 className="section-title">Prompts & Goals</h3> <h3 className="section-title">Prompts & Goals</h3>
{!isGroupForm && ( <FormFieldDefinition
<div className="mb-4"> fields={getFields()}
<label htmlFor="system_prompt">System Prompt</label> values={formData}
<textarea onChange={handleFieldChange}
name="system_prompt" idPrefix="prompts_"
id="system_prompt"
value={formData.system_prompt || ''}
onChange={handleInputChange}
className="form-control"
rows="5"
/> />
<small className="form-text text-muted">Instructions that define the agent's behavior</small>
</div>
)}
<div className="mb-4">
<label htmlFor="goals">Goals</label>
<textarea
name="goals"
id="goals"
value={formData.goals || ''}
onChange={handleInputChange}
className="form-control"
rows="5"
/>
<small className="form-text text-muted">Define the agent's goals (one per line)</small>
</div>
<div className="mb-4">
<label htmlFor="constraints">Constraints</label>
<textarea
name="constraints"
id="constraints"
value={formData.constraints || ''}
onChange={handleInputChange}
className="form-control"
rows="5"
/>
<small className="form-text text-muted">Define the agent's constraints (one per line)</small>
</div>
<div className="mb-4">
<label htmlFor="tools">Tools</label>
<textarea
name="tools"
id="tools"
value={formData.tools || ''}
onChange={handleInputChange}
className="form-control"
rows="5"
/>
<small className="form-text text-muted">Define the agent's tools (one per line)</small>
</div>
</div> </div>
); );
}; };

View File

@@ -81,6 +81,7 @@ const FormField = ({
className="form-control" className="form-control"
placeholder={placeholder} placeholder={placeholder}
required={required} required={required}
rows={5}
/> />
{helpText && <small className="form-text text-muted">{helpText}</small>} {helpText && <small className="form-text text-muted">{helpText}</small>}
</> </>

View File

@@ -22,8 +22,8 @@ const FormFieldDefinition = ({
return ( return (
<div className="form-fields"> <div className="form-fields">
{fields.map((field) => ( {fields.map((field) => (
<div key={field.name} style={{ marginBottom: '16px' }}>
<FormField <FormField
key={field.name}
id={`${idPrefix}${field.name}`} id={`${idPrefix}${field.name}`}
label={field.label} label={field.label}
type={field.type} type={field.type}
@@ -34,6 +34,7 @@ const FormFieldDefinition = ({
options={field.options || []} options={field.options || []}
required={field.required || false} required={field.required || false}
/> />
</div>
))} ))}
</div> </div>
); );