// 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 connectorElements = document.querySelectorAll('.connector'); for (let i = 0; i < connectorElements.length; i++) { const typeSelect = document.getElementById(`connectorType${i}`); if (!typeSelect) { showToast(`Error: Could not find connector type select for index ${i}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } const type = typeSelect.value; if (!type) { showToast(`Please select a connector type for connector ${i+1}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } // Get all config fields for this connector const connector = { type: type, config: {} }; // Find all config inputs for this connector const configInputs = document.querySelectorAll(`[name^="connectors[${i}][config]"]`); // Check if we have a JSON textarea (fallback template) const jsonTextarea = document.getElementById(`connectorConfig${i}`); if (jsonTextarea && jsonTextarea.value) { try { // If it's a JSON textarea, parse it and use the result const jsonConfig = JSON.parse(jsonTextarea.value); // Convert the parsed JSON back to a string for the backend connector.config = JSON.stringify(jsonConfig); } catch (e) { // If it's not valid JSON, use it as is connector.config = jsonTextarea.value; } } else { // Process individual form fields configInputs.forEach(input => { // Extract the key from the name attribute // Format: connectors[0][config][key] const keyMatch = input.name.match(/\[config\]\[([^\]]+)\]/); if (keyMatch && keyMatch[1]) { const key = keyMatch[1]; // For checkboxes, set true/false based on checked state if (input.type === 'checkbox') { connector.config[key] = input.checked ? 'true' : 'false'; } else { connector.config[key] = input.value; } } }); // Convert the config object to a JSON string for the backend connector.config = JSON.stringify(connector.config); } connectors.push(connector); } return connectors; }, // Process MCP servers from form processMCPServers: function() { const mcpServers = []; const mcpElements = document.querySelectorAll('.mcp_server'); for (let i = 0; i < mcpElements.length; i++) { const urlInput = document.getElementById(`mcpURL${i}`); const tokenInput = document.getElementById(`mcpToken${i}`); if (urlInput && urlInput.value) { const server = { url: urlInput.value }; // Add token if present if (tokenInput && tokenInput.value) { server.token = tokenInput.value; } mcpServers.push(server); } } return mcpServers; }, // Process actions from form processActions: function(button) { const actions = []; const actionElements = document.querySelectorAll('.action'); for (let i = 0; i < actionElements.length; i++) { const nameSelect = document.getElementById(`actionsName${i}`); const configTextarea = document.getElementById(`actionsConfig${i}`); if (!nameSelect) { showToast(`Error: Could not find action name select for index ${i}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } const name = nameSelect.value; if (!name) { showToast(`Please select an action type for action ${i+1}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } let config = {}; if (configTextarea && configTextarea.value) { try { config = JSON.parse(configTextarea.value); } catch (e) { showToast(`Invalid JSON in action ${i+1} config: ${e.message}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } } actions.push({ name: name, config: JSON.stringify(config) // Convert to JSON string for backend }); } return actions; }, // Process prompt blocks from form processPromptBlocks: function(button) { const promptBlocks = []; const promptElements = document.querySelectorAll('.prompt_block'); for (let i = 0; i < promptElements.length; i++) { const nameSelect = document.getElementById(`promptName${i}`); const configTextarea = document.getElementById(`promptConfig${i}`); if (!nameSelect) { showToast(`Error: Could not find prompt block name select for index ${i}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } const name = nameSelect.value; if (!name) { showToast(`Please select a prompt block type for block ${i+1}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } let config = {}; if (configTextarea && configTextarea.value) { try { config = JSON.parse(configTextarea.value); } catch (e) { showToast(`Invalid JSON in prompt block ${i+1} config: ${e.message}`, 'error'); button.innerHTML = button.getAttribute('data-original-text'); button.disabled = false; return null; // Validation failed } } promptBlocks.push({ name: name, config: JSON.stringify(config) // Convert to JSON string for backend }); } return promptBlocks; }, // Helper function to format config values (for edit form) formatConfigValue: function(configElement, configValue) { if (!configElement) return; // If configValue is an object, stringify it if (typeof configValue === 'object' && configValue !== null) { try { configElement.value = JSON.stringify(configValue, null, 2); } catch (e) { console.error('Error stringifying config value:', e); configElement.value = '{}'; } } // If it's a string that looks like JSON, try to parse and pretty print it else if (typeof configValue === 'string' && (configValue.startsWith('{') || configValue.startsWith('['))) { try { const parsed = JSON.parse(configValue); configElement.value = JSON.stringify(parsed, null, 2); } catch (e) { // If it's not valid JSON, just use the string as is configElement.value = configValue; } } // Otherwise, just use the value as is else { configElement.value = configValue || ''; } }, // Helper function to set select value (with fallback if option doesn't exist) setSelectValue: function(selectElement, value) { if (!selectElement) return; // 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; } }, // Render connector form based on type renderConnectorForm: function(index, type, config = {}) { const formContainer = document.getElementById(`connectorFormContainer${index}`); if (!formContainer) return; // Clear existing form formContainer.innerHTML = ''; // Debug log to see what's happening console.log(`Rendering connector form for type: ${type}`); console.log(`Config for connector:`, config); console.log(`Available templates:`, ConnectorTemplates ? Object.keys(ConnectorTemplates) : 'None'); // Ensure config is an object let configObj = config; if (typeof config === 'string') { try { configObj = JSON.parse(config); } catch (e) { console.error('Error parsing connector config string:', e); configObj = {}; } } // If we have a template for this connector type in the global ConnectorTemplates object if (ConnectorTemplates && type && ConnectorTemplates[type]) { console.log(`Found template for ${type}`); // Get the template result which contains HTML and setValues function const templateResult = ConnectorTemplates[type](configObj, index); // Set the HTML content formContainer.innerHTML = templateResult.html; // Call the setValues function to set input values safely if (typeof templateResult.setValues === 'function') { setTimeout(templateResult.setValues, 0); } } else { console.log(`No template found for ${type}, using fallback`); // Use the fallback template if (ConnectorTemplates && ConnectorTemplates.fallback) { const fallbackResult = ConnectorTemplates.fallback(configObj, index); formContainer.innerHTML = fallbackResult.html; if (typeof fallbackResult.setValues === 'function') { setTimeout(fallbackResult.setValues, 0); } } else { // Fallback to generic JSON textarea if no fallback template formContainer.innerHTML = `