// Common utility functions for agent forms const AgentFormUtils = { // Add dynamic component based on template addDynamicComponent: function(sectionId, templateFunction, dataItems) { const section = document.getElementById(sectionId); const newIndex = section.getElementsByClassName(dataItems.className).length; // Generate HTML from template function const newHtml = templateFunction(newIndex, dataItems); // Add to DOM section.insertAdjacentHTML('beforeend', newHtml); }, // Process form data into JSON structure processFormData: function(formData) { const jsonData = {}; // Process basic form fields for (const [key, value] of formData.entries()) { // Skip the array fields as they'll be processed separately if (!key.includes('[') && !key.includes('].')) { // Handle checkboxes if (value === 'on') { jsonData[key] = true; } // Handle numeric fields - specifically kb_results else if (key === 'kb_results') { // Convert to integer or default to 3 if empty jsonData[key] = value ? parseInt(value, 10) : 3; // Check if the parse was successful if (isNaN(jsonData[key])) { showToast('Knowledge Base Results must be a number', 'error'); return null; // Indicate validation error } } // Handle other numeric fields if needed else if (key === 'periodic_runs' && value) { // Try to parse as number if it looks like one const numValue = parseInt(value, 10); if (!isNaN(numValue) && String(numValue) === value) { jsonData[key] = numValue; } else { jsonData[key] = value; } } else { jsonData[key] = value; } } } return jsonData; }, // Process connectors from form processConnectors: function(button) { const connectors = []; const connectorsElements = document.getElementsByClassName('connector'); for (let i = 0; i < connectorsElements.length; i++) { const typeField = document.getElementById(`connectorType${i}`); const configField = document.getElementById(`connectorConfig${i}`); if (typeField && configField) { try { // Validate JSON but send as string const configValue = configField.value.trim() || '{}'; // Parse to validate but don't use the parsed object JSON.parse(configValue); connectors.push({ type: typeField.value, config: configValue // Send the raw string, not parsed JSON }); } catch (err) { console.error(`Error parsing connector ${i} config:`, err); showToast(`Error in connector ${i+1} configuration: Invalid JSON`, 'error'); // If button is provided, restore its state if (button) { const originalButtonText = button.getAttribute('data-original-text'); button.innerHTML = originalButtonText; button.disabled = false; } return null; // Indicate validation error } } } return connectors; }, // Process MCP servers from form processMCPServers: function() { const mcpServers = []; const mcpServerElements = document.getElementsByClassName('mcp_server'); for (let i = 0; i < mcpServerElements.length; i++) { const urlField = document.getElementById(`mcpURL${i}`); const tokenField = document.getElementById(`mcpToken${i}`); if (urlField && urlField.value.trim()) { mcpServers.push({ url: urlField.value.trim(), token: tokenField ? tokenField.value.trim() : '' }); } } return mcpServers; }, // Process actions from form processActions: function(button) { const actions = []; const actionElements = document.getElementsByClassName('action'); for (let i = 0; i < actionElements.length; i++) { const nameField = document.getElementById(`actionsName${i}`); const configField = document.getElementById(`actionsConfig${i}`); if (nameField && configField) { try { // Validate JSON but send as string const configValue = configField.value.trim() || '{}'; // Parse to validate but don't use the parsed object JSON.parse(configValue); actions.push({ name: nameField.value, config: configValue // Send the raw string, not parsed JSON }); } catch (err) { console.error(`Error parsing action ${i} config:`, err); showToast(`Error in action ${i+1} configuration: Invalid JSON`, 'error'); // If button is provided, restore its state if (button) { const originalButtonText = button.getAttribute('data-original-text'); button.innerHTML = originalButtonText; button.disabled = false; } return null; // Indicate validation error } } } return actions; }, // Process prompt blocks from form processPromptBlocks: function(button) { const promptBlocks = []; const promptBlockElements = document.getElementsByClassName('promptBlock'); for (let i = 0; i < promptBlockElements.length; i++) { const nameField = document.getElementById(`promptName${i}`); const configField = document.getElementById(`promptConfig${i}`); if (nameField && configField) { try { // Validate JSON but send as string const configValue = configField.value.trim() || '{}'; // Parse to validate but don't use the parsed object JSON.parse(configValue); promptBlocks.push({ name: nameField.value, config: configValue // Send the raw string, not parsed JSON }); } catch (err) { console.error(`Error parsing prompt block ${i} config:`, err); showToast(`Error in prompt block ${i+1} configuration: Invalid JSON`, 'error'); // If button is provided, restore its state if (button) { const originalButtonText = button.getAttribute('data-original-text'); button.innerHTML = originalButtonText; button.disabled = false; } return null; // Indicate validation error } } } return promptBlocks; }, // Helper function to format config values (for edit form) formatConfigValue: function(configElement, configValue) { // If it's a string (already stringified JSON), try to parse it first if (typeof configValue === 'string') { try { const parsed = JSON.parse(configValue); configElement.value = JSON.stringify(parsed, null, 2); } catch (e) { console.warn("Failed to parse config JSON string:", e); configElement.value = configValue; // Keep as is if parsing fails } } else if (configValue !== undefined && configValue !== null) { // Direct object, just stringify with formatting configElement.value = JSON.stringify(configValue, null, 2); } else { // Default to empty object configElement.value = '{}'; } }, // Helper function to set select value (with fallback if option doesn't exist) setSelectValue: function(selectElement, value) { // Check if the option exists const optionExists = Array.from(selectElement.options).some(option => option.value === value); if (optionExists) { selectElement.value = value; } else if (value) { // If value is provided but option doesn't exist, create a new option const newOption = document.createElement('option'); newOption.value = value; newOption.text = value + ' (custom)'; selectElement.add(newOption); selectElement.value = value; } } }; // HTML Templates for dynamic elements const AgentFormTemplates = { // Connector template connectorTemplate: function(index, data) { return `

Connector ${index + 1}

`; }, // MCP Server template mcpServerTemplate: function(index, data) { return `

MCP Server ${index + 1}

`; }, // Action template actionTemplate: function(index, data) { return `

Action ${index + 1}

`; }, // Prompt Block template promptBlockTemplate: function(index, data) { return `

Prompt Block ${index + 1}

`; } }; // Initialize form event listeners function initAgentFormCommon(options = {}) { // Setup event listeners for dynamic component buttons if (options.enableConnectors !== false) { document.getElementById('addConnectorButton').addEventListener('click', function() { AgentFormUtils.addDynamicComponent('connectorsSection', AgentFormTemplates.connectorTemplate, { className: 'connector', options: options.connectors || '' }); }); } if (options.enableMCP !== false) { document.getElementById('addMCPButton').addEventListener('click', function() { AgentFormUtils.addDynamicComponent('mcpSection', AgentFormTemplates.mcpServerTemplate, { className: 'mcp_server' }); }); } if (options.enableActions !== false) { document.getElementById('action_button').addEventListener('click', function() { AgentFormUtils.addDynamicComponent('action_box', AgentFormTemplates.actionTemplate, { className: 'action', options: options.actions || '' }); }); } if (options.enablePromptBlocks !== false) { document.getElementById('dynamic_button').addEventListener('click', function() { AgentFormUtils.addDynamicComponent('dynamic_box', AgentFormTemplates.promptBlockTemplate, { className: 'promptBlock', options: options.promptBlocks || '' }); }); } // Add additional CSS for checkbox labels const style = document.createElement('style'); style.textContent = ` .checkbox-label { display: flex; align-items: center; cursor: pointer; margin-bottom: 10px; } .checkbox-label .checkbox-custom { margin-right: 10px; } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } `; document.head.appendChild(style); }