I am working on a project to audit websites. The entire workflow works well but the decision making by the AI agent is not appropriate. The parametrs are as below: the Ai needs to analyse and select option from dropdown list:
- On the career page the user should be able to efficiently initate their Job Search. (check if Job Search Can Be Initiated on the Home Page and IS Above the Fold
meaning you do not have to scroll to have access to a Keyword and/or Category Search options) - The Navigation is Specific to the Career Site Journey as well as having well organized links proper grouping and titles (For a Score of 5 ensure that once you initiate the Apply Process that you can get back to the JD or Career Site Content WITHOUT having to click the back button; meaning there is menu navigation present in the Apply to move back to the previous screens)
- The task flow for applying to a job is intuitive. ( Check Job search and apply process are intuitive and require less than 3 clicks to apply start)
ex. of dropdown for 1 :Job Search Can Be Initiated on the Career Site Home Page, but one Needs to Scroll Down to Find These Options i.e. Keyword Search, Category Search, etc.
{
"name": "V19_FINAL",
"nodes": [
{
"parameters": {},
"id": "210ab051-2dd8-4a94-af3f-c71a00e4a6e9",
"name": "Chat Trigger",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1,
"position": [
-624,
96
],
"webhookId": "visual-audit-working-trigger"
},
{
"parameters": {
"url": "https://sheets.googleapis.com/v4/spreadsheets/15aKx4vqudvXD7UR3HoLNh-XGogDy02YVKqeMm0FLhJ0/values/F%26C%26T!D2:H4?valueRenderOption=FORMATTED_VALUE",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleOAuth2Api",
"options": {}
},
"id": "43c6ce27-3558-42e3-8f86-a35c8f704bcc",
"name": "Fetch Validation Options",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-400,
96
],
"alwaysOutputData": true,
"credentials": {
"googleOAuth2Api": {
"id": "viJmB6h4zreO9eck",
"name": "Google account New"
}
}
},
{
"parameters": {
"jsCode": "const careerSiteUrl = $('Chat Trigger').item.json.chatInput;\nconst validationData = $('Fetch Validation Options').item.json;\nconst outputItems = [];\n\nconsole.log('š Starting audit task preparation...');\nconsole.log(`š Career Site URL: ${careerSiteUrl}`);\n\n// Process validation options from spreadsheet\nlet validationOptions = {\n 2: [\n \"Job Search CANNOT be Initiated from the Career Site Home Page i.e. You have to click Search Jobs rather than actually entering or selecting Search Criteria.\",\n \"Job Search Can Be Initiated on the Career Site Home Page, but one Needs to Scroll Down to Find These Options i.e. Keyword Search, Category Search, etc.\",\n \"Job Search Can Be Initiated on the Home Page and IS Above the Fold meaning you do not have to scroll to have access to a Keyword and/or Category Search options\"\n ],\n 3: [\n \"The task flow is very confusing and not intuitive at all\",\n \"The task flow is somewhat confusing and could be more intuitive\", \n \"The task flow for applying to a job is intuitive and user-friendly\"\n ],\n 4: [\n \"The website does not meet accessibility guidelines\",\n \"The website partially meets accessibility guidelines\",\n \"The website fully aligns with accessibility guidelines (WCAG 2.1 AA)\"\n ]\n};\n\n// Override with spreadsheet data if available\nif (validationData && validationData.values) {\n validationData.values.forEach((row, index) => {\n const rowNumber = index + 2;\n const options = row.filter(cell => cell && cell.trim() !== '' && cell !== '[empty]');\n if (options.length > 0) {\n validationOptions[rowNumber] = options;\n console.log(`š Row ${rowNumber}: Found ${options.length} validation options`);\n }\n });\n}\n\n// Create audit tasks for all three rows\nconst auditTasks = [\n {\n rowNumber: 2,\n task: \"Job Search Homepage Analysis\",\n description: \"Analyze if job search can be initiated from the homepage and determine if it's above the fold\",\n options: validationOptions[2],\n focus: \"job_search_homepage\",\n updateCell: \"G2\"\n },\n {\n rowNumber: 3, \n task: \"Apply Process Task Flow\",\n description: \"Test the task flow for applying to a job by navigating through the process\",\n options: validationOptions[3],\n focus: \"apply_task_flow\",\n updateCell: \"G3\"\n },\n {\n rowNumber: 4,\n task: \"Accessibility Guidelines\", \n description: \"Evaluate the website's compliance with accessibility guidelines\",\n options: validationOptions[4],\n focus: \"accessibility_compliance\",\n updateCell: \"G4\"\n }\n];\n\n// Create items for processing - ONE ITEM PER ROW\nauditTasks.forEach((task, index) => {\n const item = {\n json: {\n career_site_url: careerSiteUrl,\n rowNumber: task.rowNumber,\n auditTask: task.task,\n taskDescription: task.description,\n dropdownOptions: task.options,\n focus_area: task.focus,\n spreadsheet_id: '15aKx4vqudvXD7UR3HoLNh-XGogDy02YVKqeMm0FLhJ0',\n update_cell: task.updateCell,\n task_index: index,\n total_tasks: auditTasks.length,\n batch_id: Date.now() + '_' + Math.random().toString(36).substr(2, 9)\n }\n };\n \n outputItems.push(item);\n console.log(`ā
Created audit task ${index + 1}/3: Row ${task.rowNumber} - ${task.focus} -> ${task.updateCell}`);\n});\n\nconsole.log(`šÆ Audit preparation complete! Created ${outputItems.length} tasks`);\nreturn outputItems;"
},
"id": "62beb782-e5e6-420d-b459-9f94f25cc5e4",
"name": "Prepare Audit Tasks",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-176,
96
]
},
{
"parameters": {
"url": "https://app.scrapingbee.com/api/v1/",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "api_key",
"value": PUT },
{
"name": "url",
"value": "={{ $json.career_site_url }}"
},
{
"name": "screenshot",
"value": "true"
},
{
"name": "screenshot_full_page",
"value": "true"
},
{
"name": "window_width",
"value": "1280"
},
{
"name": "window_height",
"value": "1024"
},
{
"name": "wait",
"value": "3000"
}
]
},
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 45000
}
},
"id": "d1b87a43-1c41-4043-a349-775973d18583",
"name": "Visual Scrape (Working)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
48,
0
],
"executeOnce": false,
"continueOnFail": true
},
{
"parameters": {
"url": "={{ $json.career_site_url }}",
"options": {
"response": {
"response": {
"neverError": true
}
},
"timeout": 30000
}
},
"id": "b9234eeb-6d4e-4990-95a7-13b0a9be3d7a",
"name": "Scrape HTML Content",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
48,
192
],
"executeOnce": false,
"alwaysOutputData": true,
"continueOnFail": true
},
{
"parameters": {
"mode": "combine",
"combinationMode": "mergeByPosition",
"options": {
"includeUnpaired": true
}
},
"id": "a4443dab-c65e-4dfe-a25c-450f4c1fe0a0",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"typeVersion": 2.1,
"position": [
272,
96
],
"alwaysOutputData": true
},
{
"parameters": {
"jsCode": "// Process merged data from visual and HTML scraping\nconst items = $input.all();\nconst outputItems = [];\n\nconsole.log('š Processing merged data...');\nconsole.log(`š Received ${items.length} merged items`);\n\n// In n8n merge by position, check if we have json1 and json2 structure\nitems.forEach((item, index) => {\n console.log(`\\nšÆ Processing Item ${index + 1}:`);\n \n // Check the structure of the merged item\n console.log(' Item structure:', Object.keys(item));\n console.log(' Has json:', !!item.json);\n console.log(' Has json1:', !!item.json1);\n console.log(' Has json2:', !!item.json2);\n console.log(' Has binary:', !!(item.binary && Object.keys(item.binary).length > 0));\n \n // Extract data based on merge structure\n let visualData = {};\n let htmlData = {};\n let binary = item.binary || {};\n \n // n8n merge by position might create json1 and json2\n if (item.json1 && item.json2) {\n // json1 is from first input (Visual Scrape)\n // json2 is from second input (HTML Scrape)\n visualData = item.json1;\n htmlData = item.json2;\n console.log(' Using json1/json2 structure');\n } else if (item.json) {\n // Fallback to single json object\n visualData = item.json;\n htmlData = item.json;\n console.log(' Using single json structure');\n }\n \n // Log what we found\n console.log(` Row Number: ${visualData.rowNumber || htmlData.rowNumber || 'unknown'}`);\n console.log(` Focus Area: ${visualData.focus_area || htmlData.focus_area || 'unknown'}`);\n console.log(` Binary keys: ${Object.keys(binary).join(', ') || 'none'}`);\n console.log(` HTML data length: ${htmlData.data ? htmlData.data.length : 0}`);\n \n // Extract row data\n const rowNumber = visualData.rowNumber || htmlData.rowNumber || index + 2;\n const focusArea = visualData.focus_area || htmlData.focus_area || '';\n \n // Initialize variables\n let screenshotAvailable = false;\n let screenshotSize = 0;\n let htmlContent = '';\n let htmlLength = 0;\n \n // Check for screenshot in binary data\n if (Object.keys(binary).length > 0) {\n const binaryKey = Object.keys(binary)[0];\n screenshotAvailable = true;\n \n // Get size from metadata if available\n if (visualData.File_Size) {\n screenshotSize = parseInt(visualData.File_Size) || 500000;\n } else {\n screenshotSize = 500000; // Estimate\n }\n \n console.log(` ā
Screenshot found in binary: ${binaryKey}`);\n }\n \n // Extract HTML content from the HTML scrape data\n if (htmlData.data && typeof htmlData.data === 'string') {\n htmlContent = htmlData.data;\n htmlLength = htmlContent.length;\n console.log(` ā
HTML content found: ${htmlLength} characters`);\n }\n \n // Prepare HTML for AI (limit to 6000 chars)\n let finalHtmlContent = '';\n if (htmlLength > 0) {\n finalHtmlContent = htmlContent.substring(0, 6000);\n } else {\n finalHtmlContent = '<html><body><!-- No HTML content retrieved --></body></html>';\n }\n \n // Create combined output\n const outputItem = {\n json: {\n // Original task data (prefer visual data, fallback to HTML data)\n career_site_url: visualData.career_site_url || htmlData.career_site_url,\n rowNumber: rowNumber,\n auditTask: visualData.auditTask || htmlData.auditTask,\n taskDescription: visualData.taskDescription || htmlData.taskDescription,\n dropdownOptions: visualData.dropdownOptions || htmlData.dropdownOptions,\n focus_area: focusArea,\n spreadsheet_id: visualData.spreadsheet_id || htmlData.spreadsheet_id,\n update_cell: visualData.update_cell || htmlData.update_cell,\n task_index: visualData.task_index || htmlData.task_index,\n total_tasks: visualData.total_tasks || htmlData.total_tasks,\n batch_id: visualData.batch_id || htmlData.batch_id,\n \n // Screenshot data\n screenshot_available: screenshotAvailable,\n screenshot_size: screenshotSize,\n \n // HTML data\n html_content: finalHtmlContent,\n html_length: finalHtmlContent.length,\n html_raw_length: htmlLength,\n \n // Combined flags\n combined_analysis: true,\n has_visual_data: screenshotAvailable,\n has_html_data: htmlLength > 0\n },\n // Pass through binary data\n binary: binary\n };\n \n outputItems.push(outputItem);\n \n console.log(`ā
Processed Row ${rowNumber}:`);\n console.log(` Screenshot: ${screenshotAvailable} (${screenshotSize} bytes)`);\n console.log(` HTML: ${finalHtmlContent.length} characters (raw: ${htmlLength})`);\n});\n\nconsole.log(`\\nšÆ Processing complete! Created ${outputItems.length} items`);\n\n// Summary\noutputItems.forEach((item, idx) => {\n const data = item.json;\n console.log(`Summary - Item ${idx + 1}:`);\n console.log(` Row: ${data.rowNumber}`);\n console.log(` Focus: ${data.focus_area}`);\n console.log(` Screenshot: ${data.has_visual_data}`);\n console.log(` HTML: ${data.has_html_data} (${data.html_length} chars)`);\n});\n\nreturn outputItems;"
},
"id": "c6b61d6c-cdc3-480e-a190-d0c9136df6bf",
"name": "Process Merged Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
496,
96
]
},
{
"parameters": {
"agent": "conversationalAgent",
"promptType": "define",
"text": "=You are analyzing a career website for audit compliance. You must analyze BOTH the visual screenshot AND the HTML content to provide a specific numbered decision (1, 2, or 3).\n\n**AUDIT DETAILS:**\n- Website: {{ $json.career_site_url }}\n- Focus Area: {{ $json.focus_area }}\n- Task: {{ $json.auditTask }}\n- Row: {{ $json.rowNumber }}\n- Target Cell: {{ $json.update_cell }}\n- Screenshot Available: {{ $json.screenshot_available }}\n- HTML Available: {{ $json.has_html_data }}\n\n**AVAILABLE OPTIONS:**\n{{ ($json.dropdownOptions || []).map((opt, i) => `${i + 1}. ${opt}`).join('\\n') }}\n\n**IMPORTANT: You have access to BOTH:**\n1. A visual screenshot of the website (attached as an image)\n2. The HTML source code (shown below)\n\n**YOU MUST ANALYZE BOTH THE SCREENSHOT AND HTML TO MAKE YOUR DECISION**\n\n{% if $json.screenshot_available %}\n**VISUAL SCREENSHOT ANALYSIS REQUIRED:**\n- Look at the attached screenshot image carefully\n- For Row 2: Check if job search elements are visible above the fold in the screenshot\n- For Row 3: Evaluate the visual flow and user interface in the screenshot\n- For Row 4: Look for visual accessibility indicators in the screenshot\n{% endif %}\n\n**HTML CONTENT ({{ $json.html_length || 0 }} characters):**\n```html\n{{ ($json.html_content || 'No content').substring(0, 4000) }}\n```\n\n**ANALYSIS GUIDELINES:**\n\n**For Row 2 (Job Search Homepage - {{ $json.focus_area == 'job_search_homepage' ? 'ACTIVE' : 'INACTIVE' }}):**\n- CHECK THE SCREENSHOT: Can you SEE search fields/buttons above the fold?\n- CHECK THE HTML: Are there search-related form elements?\n- Option 1: Search CANNOT be initiated from homepage (only redirect links)\n- Option 2: Search available but requires scrolling down (visible in HTML but not in screenshot above fold)\n- Option 3: Search is above the fold, immediately visible in screenshot\n\n**For Row 3 (Apply Process - {{ $json.focus_area == 'apply_task_flow' ? 'ACTIVE' : 'INACTIVE' }}):**\n- CHECK THE SCREENSHOT: Is the interface clean and intuitive?\n- CHECK THE HTML: Is the code well-structured with clear navigation?\n- Option 1: Very confusing, not intuitive\n- Option 2: Somewhat confusing, mixed navigation\n- Option 3: Intuitive and user-friendly\n\n**For Row 4 (Accessibility - {{ $json.focus_area == 'accessibility_compliance' ? 'ACTIVE' : 'INACTIVE' }}):**\n- CHECK THE SCREENSHOT: Are there visual accessibility features?\n- CHECK THE HTML: Look for ARIA labels, alt text, semantic HTML\n- Option 1: Does not meet accessibility guidelines\n- Option 2: Partially meets accessibility guidelines\n- Option 3: Fully meets WCAG 2.1 AA guidelines\n\n**INSTRUCTIONS:**\n1. FIRST: Analyze the SCREENSHOT (visual appearance)\n2. SECOND: Analyze the HTML CODE (structure and elements)\n3. COMBINE both analyses to make your decision\n4. Be conservative in assessment\n5. Provide clear reasoning mentioning BOTH visual and code evidence\n6. End with: FINAL DECISION: [1/2/3]\n\n**YOUR ANALYSIS:**",
"options": {
"systemMessage": "You are a professional website auditor. Always end your response with 'FINAL DECISION: [NUMBER]' where NUMBER is 1, 2, or 3. Be conservative and thorough in your analysis. Focus only on the specific audit criteria for each row."
}
},
"id": "8a59aafd-8c36-4616-bda5-f50bf9b846c2",
"name": "Visual AI Analyzer",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.6,
"position": [
720,
96
],
"executeOnce": false,
"alwaysOutputData": true
},
{
"parameters": {
"jsCode": "// Direct processing from AI Analyzer output using fetched validation options\nconst aiResponses = $input.all();\nconst batchUpdates = [];\n\nconsole.log('š Direct batch update preparation from AI responses...');\nconsole.log(`š Processing ${aiResponses.length} AI responses`);\n\n// Get the validation options from the \"Fetch Validation Options\" node\nconst validationData = $('Fetch Validation Options').first().json;\nconsole.log('š Fetched validation data:', validationData);\n\n// Process validation options from spreadsheet (same logic as \"Prepare Audit Tasks\")\nlet validationOptions = {\n 2: [\n \"Job Search CANNOT be Initiated from the Career Site Home Page i.e. You have to click Search Jobs rather than actually entering or selecting Search Criteria.\",\n \"Job Search Can Be Initiated on the Career Site Home Page, but one Needs to Scroll Down to Find These Options i.e. Keyword Search, Category Search, etc.\",\n \"Job Search Can Be Initiated on the Home Page and IS Above the Fold meaning you do not have to scroll to have access to a Keyword and/or Category Search options\"\n ],\n 3: [\n \"The task flow is very confusing and not intuitive at all\",\n \"The task flow is somewhat confusing and could be more intuitive\", \n \"The task flow for applying to a job is intuitive and user-friendly\"\n ],\n 4: [\n \"The website does not meet accessibility guidelines\",\n \"The website partially meets accessibility guidelines\",\n \"The website fully aligns with accessibility guidelines (WCAG 2.1 AA)\"\n ]\n};\n\n// Override with spreadsheet data if available\nif (validationData && validationData.values) {\n validationData.values.forEach((row, index) => {\n const rowNumber = index + 2;\n const options = row.filter(cell => cell && cell.trim() !== '' && cell !== '[empty]');\n if (options.length > 0) {\n validationOptions[rowNumber] = options;\n console.log(`š Row ${rowNumber}: Found ${options.length} validation options from spreadsheet`);\n options.forEach((opt, i) => {\n console.log(` ${i + 1}. \"${opt.substring(0, 80)}...\"`);\n });\n }\n });\n} else {\n console.log('ā ļø No validation data found, using defaults');\n}\n\n// Map index to focus area and row\nconst indexMapping = [\n { focusArea: 'job_search_homepage', row: 2, cell: 'G2' },\n { focusArea: 'apply_task_flow', row: 3, cell: 'G3' },\n { focusArea: 'accessibility_compliance', row: 4, cell: 'G4' }\n];\n\naiResponses.forEach((aiResponse, index) => {\n const aiData = aiResponse.json;\n const aiOutput = aiData.output || aiData.text || aiData.content || '';\n \n // Get mapping for this index\n const mapping = indexMapping[index] || indexMapping[0];\n const focusArea = mapping.focusArea;\n const rowNumber = mapping.row;\n const cellAddress = mapping.cell;\n \n console.log(`\\nš Processing AI Response ${index + 1}: ${focusArea} -> ${cellAddress}`);\n console.log(`š¤ AI Output Length: ${aiOutput.length} characters`);\n console.log(`šÆ Looking for decision in: \"${aiOutput.substring(0, 300)}...\"`);\n \n // Extract decision directly from AI output\n let selectedChoice = 2; // Default\n let foundDecision = false;\n \n // Multiple patterns to find the AI's decision\n const patterns = [\n /FINAL\\s+DECISION:\\s*(\\d+)/gi,\n /final\\s+decision:\\s*(\\d+)/gi,\n /decision:\\s*(\\d+)/gi,\n /\\*\\*Option\\s+(\\d+)\\*\\*/gi,\n /Option\\s+(\\d+):/gi,\n /\\bOption\\s+(\\d+)\\b/gi\n ];\n \n for (const pattern of patterns) {\n pattern.lastIndex = 0;\n const matches = [...aiOutput.matchAll(pattern)];\n \n if (matches.length > 0) {\n const lastMatch = matches[matches.length - 1];\n const optionNum = parseInt(lastMatch[1]);\n \n if (optionNum >= 1 && optionNum <= 3) {\n selectedChoice = optionNum;\n foundDecision = true;\n console.log(`ā
Found decision: \"${lastMatch[0]}\" -> Option ${selectedChoice}`);\n break;\n }\n }\n }\n \n if (!foundDecision) {\n console.log(`ā ļø No clear decision found, using default Option ${selectedChoice}`);\n }\n \n // Get the option text from fetched validation options\n const options = validationOptions[rowNumber] || [];\n const selectedOptionText = options[selectedChoice - 1] || `Option ${selectedChoice} (missing text)`;\n \n console.log(`š Selected Option ${selectedChoice}: \"${selectedOptionText.substring(0, 80)}...\"`);\n console.log(`š Available options for Row ${rowNumber}:`, options.length);\n \n // Add to batch updates\n batchUpdates.push({\n cell: cellAddress,\n value: selectedOptionText,\n rowNumber: rowNumber,\n focusArea: focusArea,\n aiChoice: selectedChoice,\n foundDecision: foundDecision,\n optionsCount: options.length\n });\n \n console.log(`ā
Prepared update: ${cellAddress} = Option ${selectedChoice}`);\n});\n\n// Create batch update payload\nconst updateData = {\n valueInputOption: 'USER_ENTERED',\n data: batchUpdates.map(update => ({\n range: `Audit Scoring!${update.cell}`,\n values: [[update.value]]\n }))\n};\n\nconsole.log('\\nšÆ Batch update prepared:');\nbatchUpdates.forEach(update => {\n console.log(` ${update.cell}: Option ${update.aiChoice} - \"${update.value.substring(0, 60)}...\"`);\n});\n\nconsole.log(`\\nš Success rate: ${batchUpdates.filter(u => u.foundDecision).length}/${batchUpdates.length} decisions found`);\n\nreturn [{\n json: {\n spreadsheet_id: '15aKx4vqudvXD7UR3HoLNh-XGogDy02YVKqeMm0FLhJ0',\n batch_update_data: updateData,\n total_updates: batchUpdates.length,\n updates_summary: batchUpdates,\n timestamp: new Date().toISOString(),\n success_rate: batchUpdates.filter(u => u.foundDecision).length,\n validation_source: 'fetched_from_spreadsheet'\n }\n}];"
},
"id": "2f26d78d-2e31-4c53-a0e5-a9efbdbdeed3",
"name": "Prepare Batch Update",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
96
]
},
{
"parameters": {
"method": "POST",
"url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $json.spreadsheet_id }}/values:batchUpdate",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleOAuth2Api",
"sendBody": true,
"contentType": "raw",
"body": "={{ JSON.stringify($json.batch_update_data) }}",
"options": {
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "b5a22e6a-cb7b-4702-abe9-3e445ae915cd",
"name": "Batch Update Spreadsheet",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1344,
96
],
"credentials": {
"googleOAuth2Api": {
"id": "viJmB6h4zreO9eck",
"name": "Google account New"
}
},
"continueOnFail": true
},
{
"parameters": {
"jsCode": "// Process batch update results\nconst batchResult = $('Batch Update Spreadsheet').item.json;\nconst prepData = $('Prepare Batch Update').item.json;\n\nconsole.log('š Processing batch update results...');\n\nconst response = {\n status: 'completed',\n total_updates: prepData.total_updates,\n spreadsheet_id: prepData.spreadsheet_id,\n updates_applied: prepData.updates_summary,\n batch_response: batchResult,\n timestamp: new Date().toISOString(),\n success: batchResult && !batchResult.error\n};\n\nif (response.success) {\n console.log(`ā
Batch update successful! Updated ${prepData.total_updates} cells:`);\n prepData.updates_summary.forEach(update => {\n console.log(` ${update.cell} (Row ${update.rowNumber}): \"${update.value}\"`);\n });\n} else {\n console.log('ā Batch update encountered issues:', batchResult.error || 'Unknown error');\n}\n\nreturn [{ json: response }];"
},
"id": "47c9a286-e045-4b18-a581-a35201d17883",
"name": "Process Batch Results",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1568,
96
]
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {
"responseHeaders": {
"entries": [
{
"name": "content-type",
"value": "application/json"
}
]
}
}
},
"id": "e7bcda0b-a53b-4a89-9d74-11276d17c02a",
"name": "Final Visual Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1792,
96
]
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4o",
"mode": "list",
"cachedResultName": "gpt-4o"
},
"options": {
"maxTokens": 1500,
"temperature": 0.1
}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
816,
320
],
"id": "29b90727-f73a-41eb-b632-6c523e30d3ff",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"id": "o7p2xNk9jqN93ZjV",
"name": "OpenAi account"
}
}
},
{
"parameters": {
"model": "deepseek/deepseek-chat-v3-0324:free",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"typeVersion": 1,
"position": [
608,
320
],
"id": "bded9a2a-13c7-4654-afa8-65f3f197c8f6",
"name": "OpenRouter Chat Model",
"credentials": {
"openRouterApi": {
"id": "ArAKCC28yYfdWYLw",
"name": "OpenRouter account"
}
}
}
],
"pinData": {},
"connections": {
"Chat Trigger": {
"main": [
[
{
"node": "Fetch Validation Options",
"type": "main",
"index": 0
}
]
]
},
"Fetch Validation Options": {
"main": [
[
{
"node": "Prepare Audit Tasks",
"type": "main",
"index": 0
}
]
]
},
"Prepare Audit Tasks": {
"main": [
[
{
"node": "Visual Scrape (Working)",
"type": "main",
"index": 0
},
{
"node": "Scrape HTML Content",
"type": "main",
"index": 0
}
]
]
},
"Visual Scrape (Working)": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Scrape HTML Content": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Process Merged Data",
"type": "main",
"index": 0
}
]
]
},
"Process Merged Data": {
"main": [
[
{
"node": "Visual AI Analyzer",
"type": "main",
"index": 0
}
]
]
},
"Visual AI Analyzer": {
"main": [
[
{
"node": "Prepare Batch Update",
"type": "main",
"index": 0
}
]
]
},
"Prepare Batch Update": {
"main": [
[
{
"node": "Batch Update Spreadsheet",
"type": "main",
"index": 0
}
]
]
},
"Batch Update Spreadsheet": {
"main": [
[
{
"node": "Process Batch Results",
"type": "main",
"index": 0
}
]
]
},
"Process Batch Results": {
"main": [
[
{
"node": "Final Visual Response",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Visual AI Analyzer",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "dc8b1029-a96a-47d0-9108-d555d26dd440",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "88430259e8a99df015e279921e0c55892e39e3110b4682842cf97b264a243256"
},
"id": "dVMEK6LmKQZZi97s",
"tags": []
}
