This workflow follows the Chainllm → Execute Workflow Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
304,
80
],
"id": "79f8564a-bc6c-4873-905f-e4de17c216d6",
"name": "When clicking \u2018Execute workflow\u2019"
},
{
"parameters": {
"modelName": "models/gemini-3.1-pro-preview-customtools",
"options": {
"maxOutputTokens": 2000
}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
416,
464
],
"id": "f33adfda-76b4-4d4d-bccb-fcae817fc1ec",
"name": "Google Gemini Chat Model",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "5ddeb4db-0381-4037-9498-9aeb348cd64e",
"name": "output",
"value": "={{ $('Function').item.json.output }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1200,
272
],
"id": "8741f8cf-0f56-40ba-b480-6b2fa38fb881",
"name": "Edit Fields"
},
{
"parameters": {
"jsCode": "const inputData = $input.first().json; \nconst texto_bruto = inputData.output || inputData.text || \"\";\n\nconst extraerTexto = (campo, fallback) => {\n const regex = new RegExp(`${campo}[:\\\\s]+([^\\\\n]+)`, 'i');\n const match = texto_bruto.match(regex);\n return match ? match[1].trim() : fallback;\n};\n\n// Extrae solo el PRIMER n\u00famero de la lista para BigQuery\nconst extraerPrimerNumero = (campo, fallback) => {\n const texto = extraerTexto(campo, \"\");\n if (!texto) return fallback;\n const primerNumero = texto.split(',')[0].trim(); \n return parseFloat(primerNumero.replace(',', '.')) || fallback;\n};\n\n// Extrae solo el PRIMER texto de una lista (Ej: \"N, NE\" -> \"N\")\nconst extraerPrimerTexto = (campo, fallback) => {\n const texto = extraerTexto(campo, \"\");\n if (!texto) return fallback;\n return texto.split(',')[0].trim() || fallback;\n};\n\nconst reporte_limpio = texto_bruto.replace(/\\[MOSTRAR_GRAFICO\\][\\s\\S]*/i, \"\").trim();\n\nreturn [{\n json: {\n // --- DATOS PARA BIGQUERY (datos_sectoriales) ---\n timestamp: new Date().toISOString(),\n session_id: inputData.session_id || `naval-${Date.now()}`,\n sector: \"NAVAL\", \n \n puerto: extraerTexto(\"puerto\", \"S/D\"),\n condiciones_mar: extraerTexto(\"condiciones_mar\", \"S/D\"),\n viento_velocidad: extraerPrimerNumero(\"viento_velocidad\", 0),\n viento_direccion: extraerPrimerTexto(\"viento_direccion\", \"VAR\"), \n temperatura_agua: extraerPrimerNumero(\"temperatura_agua\", 20),\n altura_olas: extraerPrimerNumero(\"altura_olas\", 0),\n visibilidad: extraerPrimerNumero(\"visibilidad\", 10),\n \n ai_analysis: reporte_limpio,\n \n metadata: JSON.stringify({ \n status: \"success\", \n trigger_input: inputData.query || \"Entrada Naval\" \n }),\n\n // --- DATOS PARA DJANGO Y CHART.JS (Con las listas completas) ---\n output: texto_bruto \n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
784,
272
],
"id": "41ebb8db-c4c7-4321-aece-0cc70fa7cb4b",
"name": "Function"
},
{
"parameters": {
"authentication": "serviceAccount",
"projectId": {
"__rl": true,
"value": "proyecto-bi-488218",
"mode": "list",
"cachedResultName": "proyecto-BI",
"cachedResultUrl": "https://console.cloud.google.com/bigquery?project=proyecto-bi-488218"
},
"sqlQuery": "INSERT INTO `proyecto-bi-488218.datos_clima.datos_sectoriales`\n(\n timestamp,\n session_id,\n sector,\n puerto,\n condiciones_mar,\n viento_velocidad,\n viento_direccion, \n temperatura_agua,\n altura_olas,\n visibilidad,\n ai_analysis,\n metadata\n)\nVALUES\n(\n -- \u2705 Timestamp con parsing seguro y fallback\n COALESCE(\n SAFE.PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%E*SZ', @timestamp),\n CURRENT_TIMESTAMP()\n ),\n \n -- \u2705 Session ID con sanitizaci\u00f3n\n COALESCE(\n NULLIF(TRIM(@session_id), ''), \n CONCAT('naval-auto-', FORMAT_TIMESTAMP('%Y%m%d-%H%M%S', CURRENT_TIMESTAMP()))\n ),\n \n -- \u2705 Sector fijo para NAVAL\n 'NAVAL',\n \n -- \u2705 Puerto con validaci\u00f3n de longitud\n CASE \n WHEN LENGTH(TRIM(@puerto)) > 100 THEN SUBSTR(TRIM(@puerto), 1, 100)\n WHEN TRIM(@puerto) IS NULL OR TRIM(@puerto) = '' THEN 'Puerto no especificado'\n ELSE TRIM(@puerto)\n END,\n \n -- \u2705 Condiciones mar normalizado\n CASE \n WHEN UPPER(TRIM(@condiciones_mar)) IN ('CALMO', 'CALMA', 'CALM') THEN 'Calmo'\n WHEN UPPER(TRIM(@condiciones_mar)) IN ('MODERADO', 'MODERATE') THEN 'Moderado'\n WHEN UPPER(TRIM(@condiciones_mar)) IN ('AGITADO', 'ROUGH') THEN 'Agitado'\n WHEN UPPER(TRIM(@condiciones_mar)) IN ('PELIGROSO', 'DANGEROUS') THEN 'Peligroso'\n WHEN TRIM(@condiciones_mar) IS NULL OR TRIM(@condiciones_mar) = '' THEN 'Sin datos'\n ELSE REGEXP_REPLACE(TRIM(@condiciones_mar), r'[^a-zA-Z\u00e1\u00e9\u00ed\u00f3\u00fa\u00c1\u00c9\u00cd\u00d3\u00da\u00f1\u00d1\\s]', '')\n END,\n \n -- \u2705 Velocidad viento - CONVERSI\u00d3N SEGURA STRING \u2192 FLOAT64\n CASE \n WHEN SAFE_CAST(@viento_velocidad AS FLOAT64) IS NOT NULL \n AND SAFE_CAST(@viento_velocidad AS FLOAT64) BETWEEN 0 AND 200 \n THEN SAFE_CAST(@viento_velocidad AS FLOAT64)\n ELSE 0.0\n END,\n \n -- \u2705 Direcci\u00f3n viento normalizado\n CASE \n WHEN UPPER(TRIM(@viento_direccion)) IN ('N', 'NORTE', 'NORTH') THEN 'Norte'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('S', 'SUR', 'SOUTH') THEN 'Sur'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('E', 'ESTE', 'EAST') THEN 'Este'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('O', 'W', 'OESTE', 'WEST') THEN 'Oeste'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('NE', 'NORESTE', 'NORTHEAST') THEN 'Noreste'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('NO', 'NW', 'NOROESTE', 'NORTHWEST') THEN 'Noroeste'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('SE', 'SURESTE', 'SOUTHEAST') THEN 'Sureste'\n WHEN UPPER(TRIM(@viento_direccion)) IN ('SO', 'SW', 'SUROESTE', 'SOUTHWEST') THEN 'Suroeste'\n ELSE 'Variable'\n END,\n \n -- \u2705 Temperatura agua - CONVERSI\u00d3N SEGURA STRING \u2192 FLOAT64\n CASE \n WHEN SAFE_CAST(@temperatura_agua AS FLOAT64) IS NOT NULL \n AND SAFE_CAST(@temperatura_agua AS FLOAT64) BETWEEN -5 AND 50 \n THEN SAFE_CAST(@temperatura_agua AS FLOAT64)\n ELSE 20.0\n END,\n \n -- \u2705 Altura olas - CONVERSI\u00d3N SEGURA STRING \u2192 FLOAT64\n CASE \n WHEN SAFE_CAST(@altura_olas AS FLOAT64) IS NOT NULL \n AND SAFE_CAST(@altura_olas AS FLOAT64) BETWEEN 0 AND 30 \n THEN SAFE_CAST(@altura_olas AS FLOAT64)\n ELSE 0.0\n END,\n \n -- \u2705 Visibilidad - CONVERSI\u00d3N SEGURA STRING \u2192 FLOAT64\n CASE \n WHEN SAFE_CAST(@visibilidad AS FLOAT64) IS NOT NULL \n AND SAFE_CAST(@visibilidad AS FLOAT64) BETWEEN 0 AND 50 \n THEN SAFE_CAST(@visibilidad AS FLOAT64)\n ELSE 10.0\n END,\n \n -- \u2705 An\u00e1lisis AI con sanitizaci\u00f3n y l\u00edmite\n CASE \n WHEN LENGTH(TRIM(@ai_analysis)) > 5000 THEN SUBSTR(TRIM(@ai_analysis), 1, 5000)\n WHEN TRIM(@ai_analysis) IS NULL OR TRIM(@ai_analysis) = '' THEN 'An\u00e1lisis no disponible'\n ELSE REGEXP_REPLACE(TRIM(@ai_analysis), r'[^\\w\\s\u00e1\u00e9\u00ed\u00f3\u00fa\u00c1\u00c9\u00cd\u00d3\u00da\u00f1\u00d1.,;:()\\-\\n\\r]', '')\n END,\n \n -- \u2705 Metadata JSON con validaci\u00f3n\n CASE \n WHEN @metadata IS NOT NULL AND @metadata != '' THEN @metadata\n ELSE '{\"source\":\"default\",\"processed\":true}'\n END\n);\n\n-- \u2705 SELECT para generar OUTPUT y continuar flujo\nSELECT \n 'SUCCESS' as status,\n 'Datos navales insertados correctamente' as message,\n CURRENT_TIMESTAMP() as processed_at,\n 'NAVAL' as sector;\n\n",
"options": {
"queryParameters": {
"namedParameters": [
{
"name": "timestamp",
"value": "={{ $('Function').item.json.timestamp }}"
},
{
"name": "session_id",
"value": "={{ $('Function').item.json.session_id}}"
},
{
"name": "puerto",
"value": "={{ $('Function').item.json.puerto }}"
},
{
"name": "condiciones_mar",
"value": "={{ $('Function').item.json.condiciones_mar }}"
},
{
"name": "viento_velocidad",
"value": "= {{ $('Function').item.json.viento_velocidad }}"
},
{
"name": "viento_direccion",
"value": "={{ $('Function').item.json.viento_direccion }}"
},
{
"name": "temperatura_agua",
"value": "={{ $('Function').item.json.temperatura_agua }}"
},
{
"name": "altura_olas",
"value": "= {{ $('Function').item.json.altura_olas }}"
},
{
"name": "visibilidad",
"value": "={{ $('Function').item.json.visibilidad }}"
},
{
"name": "ai_analysis",
"value": "={{ $('Function').item.json.ai_analysis }}"
},
{
"name": "metadata",
"value": "={{ ($('Function').item.json.metadata) }}"
},
{
"name": "sector",
"value": "={{ ($('Function').item.json.sector) }}"
}
]
}
}
},
"type": "n8n-nodes-base.googleBigQuery",
"typeVersion": 2.1,
"position": [
976,
272
],
"id": "973f8b57-b4ee-4c5b-98d4-ef4cd973869a",
"name": "Execute a SQL query",
"alwaysOutputData": true,
"credentials": {
"googleApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"workflowInputs": {
"values": [
{
"name": "query"
}
]
}
},
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"position": [
336,
272
],
"id": "27d54279-9a75-4a79-8e71-39ae619d6763",
"name": "When Executed by Another Workflow"
},
{
"parameters": {
"promptType": "define",
"text": "=Eres el Especialista en Visualizaci\u00f3n Naval de TuClima. La IA principal ya decidi\u00f3 enviarte aqu\u00ed porque el usuario solicit\u00f3 gr\u00e1ficos/visualizaciones navales.\n\n**\ud83c\udfaf TU \u00daNICA FUNCI\u00d3N:**\n1. **RECIBIR:** Datos mar\u00edtimos de la IA principal \n2. **PROCESAR:** Aplicar motores de c\u00e1lculo navales\n3. **VISUALIZAR:** Generar la estructura de datos para el dashboard interactivo.\n4. **ENVIAR:** Al siguiente nodo con los par\u00e1metros listos.\n\n**\ud83d\udeab NO BUSQUES PALABRAS CLAVE:** La IA principal ya filtr\u00f3. Tu asumes que SIEMPRE debes generar los par\u00e1metros del gr\u00e1fico.\n\n### \ud83c\udfaf MISI\u00d3N ESPEC\u00cdFICA:\nYa est\u00e1s activado para crear visualizaciones navales. Procesa los datos recibidos y genera:\n- Tendencias de oleaje y viento\n- Estados de puertos y seguridad \n- Rutas navales\n- Dashboards mar\u00edtimos completos\n\n### \ud83e\uddee MOTORES DE C\u00c1LCULO OBLIGATORIOS:\n- NUNCA mezcles unidades. Si es una l\u00ednea de tiempo (varios datos), calcula todos. Si es un dato puntual, enf\u00f3cate solo en ese.\n\n**1. \ud83c\udf2c\ufe0f VIENTO NAVAL (Escala Beaufort):**\n- Conversi\u00f3n OBLIGATORIA: km/h \u00d7 0.5399 = NUDOS (kt)\n- 34+ kt: TEMPORAL (\u26d4 Peligro - Puerto cerrado)\n\n**2. \ud83c\udf0a ESTADO DEL MAR (Escala Douglas):**\n- >2.5m: GRUESA (\u26d4 Peligro)\n\n**3. \ud83d\udea6 SEM\u00c1FORO DE PUERTO (Protocolo de Seguridad):**\n- \ud83d\udfe2 ABIERTO: Condiciones normales\n- \ud83d\udfe1 PRECAUCI\u00d3N: Olas 1.5-2.5m O Viento 15-25 kt\n- \ud83d\udd34 CERRADO: Olas >2.5m O Viento >25 kt\n\n**4. \ud83e\uddea F\u00cdSICA MARINA Y SUPERVIVENCIA:**\n- Visibilidad: Metros \u00f7 1852 = Millas N\u00e1uticas (NM)\n- Temperatura agua (Estimada): (Temp_Aire \u00d7 0.85) + 2\n\n### \ud83d\udcca FORMATO DE RESPUESTA OBLIGATORIO:\n\n[Frase introductoria profesional sobre la situaci\u00f3n mar\u00edtima]\n\n\u2693 REPORTE NAVAL COMMAND: [Ubicaci\u00f3n especificada]\n\n\ud83d\udccd POSICI\u00d3N Y REFERENCIAS:\nCoordenadas: [Lat], [Lon]\nPuerto Base: [Puerto m\u00e1s cercano]\n\n\ud83d\udea6 CONDICI\u00d3N OPERATIVA:\nEstado de Puerto: [\ud83d\udfe2 ABIERTO / \ud83d\udfe1 PRECAUCI\u00d3N / \ud83d\udd34 CERRADO]\nRaz\u00f3n: [Justificaci\u00f3n t\u00e9cnica]\n\n\ud83c\udf0a CONDICIONES DE MAR (Douglas):\nAltura Ola: [X]m\n\ud83c\udf2c\ufe0f VIENTO (Beaufort):\nViento Sostenido: [X] kt\nDirecci\u00f3n: [X]\u00b0\n\n\u26a0\ufe0f RECOMENDACIONES NAVALES:\n\u2022 SEGURIDAD: [Medida de seguridad principal]\n\n\n### \ud83d\udeab RESTRICCIONES CR\u00cdTICAS:\n- NUNCA analices condiciones agr\u00edcolas, a\u00e9reas o energ\u00e9ticas\n- SIEMPRE usa NUDOS para viento en los gr\u00e1ficos.\n\n**\ud83d\udcca FORMATO FINAL OBLIGATORIO PARA EL GR\u00c1FICO:**\nPara que el sistema dibuje el gr\u00e1fico interactivo, DEBES terminar tu respuesta EXACTAMENTE con este bloque. \n\nREGLA DE TIEMPO CR\u00cdTICA: \n- Si piden varios datos temporales (horas o d\u00edas), TIENES ESTRICTAMENTE PROHIBIDO rellenar la lista con ceros o inventar los datos (ej: 15.8, 0, 0, 0, 0). \n- DEBES leer el pron\u00f3stico completo y extraer o calcular el valor REAL para CADA UNO de los puntos en el tiempo. \n- La cantidad de etiquetas debe ser IGUAL a la cantidad de n\u00fameros, y TODOS los n\u00fameros deben ser reales.\n- Si piden un dato est\u00e1tico/actual: Pon un solo valor por l\u00ednea.\n\n\ud83d\udea8 REGLA DE FORMATO CR\u00cdTICA Y OBLIGATORIA (INQUEBRANTABLE):\nTu respuesta final DEBE terminar SIEMPRE con el siguiente bloque de datos. \nREGLAS ESTRICTAS:\n1. Debes escribir literalmente la etiqueta \"[MOSTRAR_GRAFICO]\".\n2. Cada variable DEBE ir en una NUEVA L\u00cdNEA (Prohibido escribir todo en un solo p\u00e1rrafo).\n3. Escribe solo los n\u00fameros separados por comas. \u00a1NO USES CORCHETES `[]` ALREDEDOR DE LOS N\u00daMEROS!\n\nEJEMPLO EXACTO DE C\u00d3MO DEBES RESPONDER AL FINAL:\n\"[MOSTRAR_GRAFICO]\"\nchart_type: [line / bar]\netiquetas: [Ej: Lunes, Martes... o 08:00, 12:00...]\npuerto: [Nombre del puerto o zona]\ncondiciones_mar: [Estado general]\nviento_velocidad: [Ej: 15, 20, 25, 15]\nviento_direccion: [Ej: 360, 45, 90, 180, 270] (\u00a1USAR SOLO GRADOS DE 0 a 360, NO USAR LETRAS!)\ntemperatura_agua: [Ej: 18, 18.5, 19, 18]\naltura_olas: [Ej: 1.2, 1.5, 2.0, 1.5]\nvisibilidad: [Ej: 10, 8, 5, 10]\n\n4. ANTI-BOTONES VAC\u00cdOS (OMISI\u00d3N RADICAL): Si no tienes los datos reales para una variable espec\u00edfica (por ejemplo, si te piden visibilidad_nm pero solo tienes en km, o si faltan datos de olas), TIENES ESTRICTAMENTE PROHIBIDO incluir el nombre de esa variable en tu lista final. BORRA la l\u00ednea por completo. Si incluyes una variable vac\u00eda o con ceros, el frontend crear\u00e1 un bot\u00f3n roto. Solo imprime las variables que tengan datos reales.\n\n---\nDatos a analizar:\n{{ $json.query || $json.text || $json.chatInput || \"Genera el reporte para el gr\u00e1fico solicitado con estos datos: \" + JSON.stringify($json) }}",
"batching": {}
},
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"typeVersion": 1.9,
"position": [
480,
272
],
"id": "ec678f00-f5d1-4f0d-8c9d-6aba3c611494",
"name": "Basic LLM Chain"
}
],
"connections": {
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Basic LLM Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[]
]
},
"Function": {
"main": [
[
{
"node": "Execute a SQL query",
"type": "main",
"index": 0
}
]
]
},
"Execute a SQL query": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"When Executed by Another Workflow": {
"main": [
[
{
"node": "Basic LLM Chain",
"type": "main",
"index": 0
}
]
]
},
"Basic LLM Chain": {
"main": [
[
{
"node": "Function",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleApigooglePalmApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Naval Bi. Uses lmChatGoogleGemini, googleBigQuery, executeWorkflowTrigger, chainLlm. Event-driven trigger; 7 nodes.
Source: https://github.com/santinote9-droid/tuclima-mundial/blob/8a042ba155cd628889eb8eef2b54df6fdde37101/n8n/naval_BI.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Energia Bi. Uses lmChatGoogleGemini, googleBigQuery, executeWorkflowTrigger, chainLlm. Event-driven trigger; 7 nodes.
Aereo Bi. Uses lmChatGoogleGemini, googleBigQuery, executeWorkflowTrigger, chainLlm. Event-driven trigger; 7 nodes.
Content - Newsletter Agent. Uses formTrigger, chainLlm, outputParserStructured, httpRequest. Event-driven trigger; 91 nodes.
This template attempts to replicate OpenAI's DeepResearch feature which, at time of writing, is only available to their pro subscribers.
🤖🧑💻 AI Agent for Top n8n Creators Leaderboard Reporting. Uses httpRequest, lmChatOpenAi, executeWorkflowTrigger, toolWorkflow. Event-driven trigger; 49 nodes.