// Common utility functions for agent forms const AgentFormUtils = { // Add dynamic component based on template addDynamicComponent: function(sectionId, templateFunction, options = {}) { const section = document.getElementById(sectionId); if (!section) return; const index = section.getElementsByClassName(options.className || 'dynamic-component').length; const templateData = { index, ...options }; // Create a new element from the template const tempDiv = document.createElement('div'); tempDiv.innerHTML = templateFunction(index, templateData); const newElement = tempDiv.firstElementChild; // Add the new element to the section section.appendChild(newElement); // If it's a connector, add event listener for type change if (options.className === 'connector') { const newIndex = index; const connectorType = document.getElementById(`connectorType${newIndex}`); if (connectorType) { // Add event listener for future changes connectorType.addEventListener('change', function() { loadConnectorForm(newIndex, this.value, null); }); // If a connector type is already selected (default value), load its form immediately if (connectorType.value) { loadConnectorForm(newIndex, connectorType.value, null); } } } return newElement; }, // 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 typeSelect = document.getElementById(`connectorType${i}`); if (typeSelect) { const connectorType = typeSelect.value; const configContainer = document.getElementById(`connectorConfigContainer${i}`); // Only process if we have a metaform if (configContainer && configContainer.querySelector('.metaform')) { try { // Get all form fields const fields = configContainer.querySelectorAll('.connector-field'); let configObj = {}; // Process each field based on its type fields.forEach(field => { const fieldName = field.dataset.fieldName; const fieldType = field.dataset.fieldType; // Convert value based on field type let value = field.value; if (fieldType === 'number' && value !== '') { value = parseFloat(value); } configObj[fieldName] = value; }); // Add the connector to the list connectors.push({ type: connectorType, config: JSON.stringify(configObj) }); } catch (err) { console.error(`Error processing connector ${i} form:`, err); showToast(`Error in connector ${i+1} configuration`, '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 } } else { // If no form is loaded, create an empty config connectors.push({ type: connectorType, config: '{}' }); } } } 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) { // If parsing fails, use the raw string configElement.value = configValue; } } // If it's already an object, stringify it else if (typeof configValue === 'object' && configValue !== null) { configElement.value = JSON.stringify(configValue, null, 2); } // Default to empty object else { configElement.value = '{}'; } }, // Helper function to set select value (with fallback if option doesn't exist) setSelectValue: function(selectElement, value) { // Check if the option exists let optionExists = false; for (let i = 0; i < selectElement.options.length; i++) { if (selectElement.options[i].value === value) { optionExists = true; break; } } // Set the value if the option exists if (optionExists) { selectElement.value = value; } else if (selectElement.options.length > 0) { // Otherwise select the first option selectElement.selectedIndex = 0; } } }; // Function to load connector form based on type function loadConnectorForm(index, connectorType, configData) { if (!connectorType) return; const configContainer = document.getElementById(`connectorConfigContainer${index}`); if (!configContainer) return; // Show loading indicator configContainer.innerHTML = '
Select a connector type to load its configuration form.