feat(filters): Add configurable filters for incoming jobs
Signed-off-by: Richard Palethorpe <io@richiejp.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import ModelSettingsSection from './agent-form-sections/ModelSettingsSection';
|
||||
import PromptsGoalsSection from './agent-form-sections/PromptsGoalsSection';
|
||||
import AdvancedSettingsSection from './agent-form-sections/AdvancedSettingsSection';
|
||||
import ExportSection from './agent-form-sections/ExportSection';
|
||||
import FiltersSection from './agent-form-sections/FiltersSection';
|
||||
|
||||
const AgentForm = ({
|
||||
isEdit = false,
|
||||
@@ -189,6 +190,13 @@ const AgentForm = ({
|
||||
<i className="fas fa-plug"></i>
|
||||
Connectors
|
||||
</li>
|
||||
<li
|
||||
className={`wizard-nav-item ${activeSection === 'filters-section' ? 'active' : ''}`}
|
||||
onClick={() => handleSectionChange('filters-section')}
|
||||
>
|
||||
<i className="fas fa-shield"></i>
|
||||
Filters & Triggers
|
||||
</li>
|
||||
<li
|
||||
className={`wizard-nav-item ${activeSection === 'actions-section' ? 'active' : ''}`}
|
||||
onClick={() => handleSectionChange('actions-section')}
|
||||
@@ -255,6 +263,10 @@ const AgentForm = ({
|
||||
<ConnectorsSection formData={formData} handleAddConnector={handleAddConnector} handleRemoveConnector={handleRemoveConnector} handleConnectorChange={handleConnectorChange} metadata={metadata} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: activeSection === 'filters-section' ? 'block' : 'none' }}>
|
||||
<FiltersSection formData={formData} setFormData={setFormData} metadata={metadata} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: activeSection === 'actions-section' ? 'block' : 'none' }}>
|
||||
<ActionsSection formData={formData} setFormData={setFormData} metadata={metadata} />
|
||||
</div>
|
||||
@@ -306,6 +318,10 @@ const AgentForm = ({
|
||||
<ConnectorsSection formData={formData} handleAddConnector={handleAddConnector} handleRemoveConnector={handleRemoveConnector} handleConnectorChange={handleConnectorChange} metadata={metadata} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: activeSection === 'filters-section' ? 'block' : 'none' }}>
|
||||
<FiltersSection formData={formData} setFormData={setFormData} metadata={metadata} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: activeSection === 'actions-section' ? 'block' : 'none' }}>
|
||||
<ActionsSection formData={formData} setFormData={setFormData} metadata={metadata} />
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import FormFieldDefinition from './common/FormFieldDefinition';
|
||||
* @param {String} props.itemType - Type of items being configured ('action', 'connector', etc.)
|
||||
* @param {String} props.typeField - The field name that determines the item's type (e.g., 'name' for actions, 'type' for connectors)
|
||||
* @param {String} props.addButtonText - Text for the add button
|
||||
* @param {String} props.saveAllFieldsAsString - Whether to save all fields as string or the appropriate JSON type
|
||||
*/
|
||||
const ConfigForm = ({
|
||||
items = [],
|
||||
@@ -22,7 +23,8 @@ const ConfigForm = ({
|
||||
onAdd,
|
||||
itemType = 'item',
|
||||
typeField = 'type',
|
||||
addButtonText = 'Add Item'
|
||||
addButtonText = 'Add Item',
|
||||
saveAllFieldsAsString = true,
|
||||
}) => {
|
||||
// Generate options from fieldGroups
|
||||
const typeOptions = [
|
||||
@@ -62,8 +64,10 @@ const ConfigForm = ({
|
||||
const item = items[index];
|
||||
const config = parseConfig(item);
|
||||
|
||||
if (type === 'checkbox')
|
||||
config[key] = checked ? 'true' : 'false';
|
||||
if (type === 'number' && !saveAllFieldsAsString)
|
||||
config[key] = Number(value);
|
||||
else if (type === 'checkbox')
|
||||
config[key] = saveAllFieldsAsString ? (checked ? 'true' : 'false') : checked;
|
||||
else
|
||||
config[key] = value;
|
||||
|
||||
|
||||
27
webui/react-ui/src/components/FilterForm.jsx
Normal file
27
webui/react-ui/src/components/FilterForm.jsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import ConfigForm from './ConfigForm';
|
||||
|
||||
/**
|
||||
* FilterForm component for configuring an filter
|
||||
* Renders filter configuration forms based on field group metadata
|
||||
*/
|
||||
const FilterForm = ({ filters = [], onChange, onRemove, onAdd, fieldGroups = [] }) => {
|
||||
const handleFilterChange = (index, updatedFilter) => {
|
||||
onChange(index, updatedFilter);
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigForm
|
||||
items={filters}
|
||||
fieldGroups={fieldGroups}
|
||||
onChange={handleFilterChange}
|
||||
onRemove={onRemove}
|
||||
onAdd={onAdd}
|
||||
itemType="filter"
|
||||
addButtonText="Add Filter"
|
||||
saveAllFieldsAsString={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilterForm;
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import FilterForm from '../FilterForm';
|
||||
|
||||
/**
|
||||
* FiltersSection component for the agent form
|
||||
*/
|
||||
const FiltersSection = ({ formData, setFormData, metadata }) => {
|
||||
// Handle filter change
|
||||
const handleFilterChange = (index, updatedFilter) => {
|
||||
const updatedFilters = [...(formData.filters || [])];
|
||||
updatedFilters[index] = updatedFilter;
|
||||
setFormData({
|
||||
...formData,
|
||||
filters: updatedFilters
|
||||
});
|
||||
};
|
||||
|
||||
// Handle filter removal
|
||||
const handleFilterRemove = (index) => {
|
||||
const updatedFilters = [...(formData.filters || [])].filter((_, i) => i !== index);
|
||||
setFormData({
|
||||
...formData,
|
||||
filters: updatedFilters
|
||||
});
|
||||
};
|
||||
|
||||
// Handle adding an filter
|
||||
const handleAddFilter = () => {
|
||||
setFormData({
|
||||
...formData,
|
||||
filters: [
|
||||
...(formData.filters || []),
|
||||
{ name: '', config: '{}' }
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="filters-section">
|
||||
<h3>Filters</h3>
|
||||
<p className="text-muted">
|
||||
Jobs received by the agent must pass all filters and at least one trigger (if any are specified)
|
||||
</p>
|
||||
|
||||
<FilterForm
|
||||
filters={formData.filters || []}
|
||||
onChange={handleFilterChange}
|
||||
onRemove={handleFilterRemove}
|
||||
onAdd={handleAddFilter}
|
||||
fieldGroups={metadata?.filters || []}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FiltersSection;
|
||||
@@ -86,9 +86,22 @@ function ObservableSummary({ observable }) {
|
||||
completionError = `Error: ${completion.error}`;
|
||||
}
|
||||
|
||||
let completionFilter = '';
|
||||
if (completion?.filter_result) {
|
||||
if (completion.filter_result?.has_triggers && !completion.filter_result?.triggered_by) {
|
||||
completionFilter = 'Failed to match any triggers';
|
||||
} else if (completion.filter_result?.triggered_by) {
|
||||
completionFilter = `Triggered by ${completion.filter_result.triggered_by}`;
|
||||
}
|
||||
|
||||
if (completion?.filter_result?.failed_by)
|
||||
completionFilter = `${completionFilter ? completionFilter + ', ' : ''}Failed by ${completion.filter_result.failed_by}`;
|
||||
}
|
||||
|
||||
// Only show if any summary is present
|
||||
if (!creationChatMsg && !creationFunctionDef && !creationFunctionParams &&
|
||||
!completionChatMsg && !completionConversation && !completionActionResult && !completionAgentState && !completionError) {
|
||||
!completionChatMsg && !completionConversation && !completionActionResult &&
|
||||
!completionAgentState && !completionError && !completionFilter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -172,6 +185,12 @@ function ObservableSummary({ observable }) {
|
||||
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}>{completionError}</span>
|
||||
</div>
|
||||
)}
|
||||
{completionFilter && (
|
||||
<div title={completionFilter} style={{ display: 'flex', alignItems: 'center', color: '#ffd7', fontSize: 14 }}>
|
||||
<i className="fas fa-shield-alt" style={{ marginRight: 6, flex: '0 0 auto' }}></i>
|
||||
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}>{completionFilter}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
1
webui/react-ui/src/utils/api.js
vendored
1
webui/react-ui/src/utils/api.js
vendored
@@ -121,6 +121,7 @@ export const agentApi = {
|
||||
groupedMetadata.actions = metadata.Actions;
|
||||
}
|
||||
groupedMetadata.dynamicPrompts = metadata.DynamicPrompts;
|
||||
groupedMetadata.filters = metadata.Filters;
|
||||
|
||||
return groupedMetadata;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user