Guest User

Untitled

a guest
Apr 28th, 2025
31
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JSON 37.22 KB | Source Code | 0 0
  1. {
  2.   "nodes": [
  3.     {
  4.       "parameters": {
  5.         "url": "https://raw.githubusercontent.com/Xavi1995/models_pricing/refs/heads/main/prices",
  6.         "options": {
  7.           "response": {
  8.             "response": {
  9.               "responseFormat": "json"
  10.             }
  11.           }
  12.         }
  13.       },
  14.       "type": "n8n-nodes-base.httpRequest",
  15.       "typeVersion": 4.2,
  16.       "position": [
  17.         0,
  18.         0
  19.       ],
  20.       "id": "be640f98-e0e1-48ad-973f-f2676e1ba4dc",
  21.       "name": "get_model_prices"
  22.     },
  23.     {
  24.       "parameters": {
  25.         "options": {
  26.           "reset": false
  27.         }
  28.       },
  29.       "type": "n8n-nodes-base.splitInBatches",
  30.       "typeVersion": 3,
  31.       "position": [
  32.         160,
  33.         660
  34.       ],
  35.       "id": "c68fb8bf-3280-4f4a-a23c-1d7afec7a028",
  36.       "name": "Loop Over Items"
  37.     },
  38.     {
  39.       "parameters": {
  40.         "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nreturn $input.first().json.sub_workflows"
  41.       },
  42.       "type": "n8n-nodes-base.code",
  43.       "typeVersion": 2,
  44.       "position": [
  45.         -100,
  46.         660
  47.       ],
  48.       "id": "0d60033d-95a2-49db-a25b-0218da3d9739",
  49.       "name": "Code2"
  50.     },
  51.     {
  52.       "parameters": {
  53.         "conditions": {
  54.           "options": {
  55.             "caseSensitive": true,
  56.             "leftValue": "",
  57.             "typeValidation": "strict",
  58.             "version": 2
  59.           },
  60.           "conditions": [
  61.             {
  62.               "id": "ab2b0724-8418-4e58-b6c3-0b91ae2498da",
  63.               "leftValue": "={{ $json.sub_workflows }}",
  64.               "rightValue": "",
  65.               "operator": {
  66.                 "type": "array",
  67.                 "operation": "exists",
  68.                 "singleValue": true
  69.               }
  70.             },
  71.             {
  72.               "id": "7f008679-ae34-4f6f-86ab-15f50d013607",
  73.               "leftValue": "={{ $json.sub_workflows }}",
  74.               "rightValue": "",
  75.               "operator": {
  76.                 "type": "array",
  77.                 "operation": "notEmpty",
  78.                 "singleValue": true
  79.               }
  80.             }
  81.           ],
  82.           "combinator": "and"
  83.         },
  84.         "options": {}
  85.       },
  86.       "type": "n8n-nodes-base.if",
  87.       "typeVersion": 2.2,
  88.       "position": [
  89.         900,
  90.         1160
  91.       ],
  92.       "id": "055f1835-74de-4104-ba13-8ef4987c5b4b",
  93.       "name": "If2"
  94.     },
  95.     {
  96.       "parameters": {
  97.         "jsCode": "const prices = $input.first().json\nconst aiNodes = $('has_ai_node').first().json.ai_nodes\nconst aiNodesInfo = []\nconst nodesData = $('get_execution_info').first().json.data.resultData.runData\nconst nodeWorkflowData = $('get_execution_info').first().json.workflowData.nodes\n\n\nfor(let i = 0; i < aiNodes.length; i++) {\n  const aiNode = aiNodes[i]\n  const nodeData = nodesData[aiNode.node_name]\n  for(let y = 0; y < nodeWorkflowData.length ; y ++) {\n    if(aiNode.node_name == nodeWorkflowData[y].name) {\n      aiNodesInfo.push(getAiNodesInfo(aiNode, nodeWorkflowData[y], nodeData))\n    }\n  }\n}\n\nfunction getAiNodesInfo(node, workflowData, nodeData){\n  if(node.model_used == null) {\n    return {\n      \"name\" : node.node_name,\n      \"type\" : workflowData.type,\n      \"execution_time\" : nodeData[0].executionTime\n    }\n  } else {\n    return {\n    \"name\" : node.node_name,\n    \"type\" : workflowData.type,\n    \"execution_time\" : nodeData[0].executionTime,\n    \"ai_details\" : {\n      \"model_used\" : node.model_used,\n      \"completion_tokens\" : node.completion_tokens,\n      \"prompt_tokens\" : node.prompt_tokens,\n      \"total_tokens\" : node.total_tokens,\n      \"cost_execution\" : getExecutionCost(node)\n    }\n  }\n  }\n}\n\nfunction getExecutionCost(node){\n    const completionTokens = node.completion_tokens\n    const promptTokens = node.prompt_tokens\n    let price = {}\n    \n    const model = node.model_used;\n    if(model == 'deepseek-chat' || model == 'deepseek-reasoner') {\n      const now = new Date($('get_execution_info').first().json.startedAt)\n      const currentHour = now.getUTCHours()\n      const currentMinute = now.getUTCMinutes()\n\n      const totalMinutes = currentHour * 60 + currentMinute\n\n      const startThreshold = 0 * 60 + 30\n      const endThreshold = 16 * 60 + 30\n      \n      if(totalMinutes >= startThreshold && totalMinutes < endThreshold) {\n        price = prices[`${model}-standard`]\n      } else {\n        price = prices[`${model}-discount`]\n      }\n    } else {\n      price = prices[model];\n    }\n  if(price == null) {\n    return 'Price not available';\n  } else {\n    const nodePrice = (completionTokens / 1000) * price.completion + (promptTokens / 1000) * price.prompt;\n    if(nodePrice >= 0.01) {\n      return `$${nodePrice.toFixed(2)}`;\n    } else if(nodePrice >= 0.0001){\n      return `$${nodePrice.toFixed(4)}`;\n    } else {\n      return `$${nodePrice.toFixed(8)}`\n    }  \n  }    \n}\n\nconst output = aiNodesInfo\n\nreturn {\n  \"ai_nodes\" : output\n}"
  98.       },
  99.       "type": "n8n-nodes-base.code",
  100.       "typeVersion": 2,
  101.       "position": [
  102.         240,
  103.         0
  104.       ],
  105.       "id": "30fb7088-25d3-42b7-96cd-18f6942c5121",
  106.       "name": "get_ai_nodes_info"
  107.     },
  108.     {
  109.       "parameters": {
  110.         "mode": "combine",
  111.         "combineBy": "combineByPosition",
  112.         "options": {}
  113.       },
  114.       "type": "n8n-nodes-base.merge",
  115.       "typeVersion": 3.1,
  116.       "position": [
  117.         400,
  118.         300
  119.       ],
  120.       "id": "805e4501-fc71-4eb2-98da-54e4d5114b89",
  121.       "name": "Merge"
  122.     },
  123.     {
  124.       "parameters": {
  125.         "url": "=https://xavicascoll.app.n8n.cloud/api/v1/executions/{{ $json.execution_id }}",
  126.         "sendQuery": true,
  127.         "specifyQuery": "json",
  128.         "jsonQuery": "{\n  \"includeData\" : true\n}",
  129.         "sendHeaders": true,
  130.         "specifyHeaders": "json",
  131.         "jsonHeaders": "{\n  \"X-N8N-API-KEY\" : \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYzAyNzRkMy03YWYyLTQzZjktYTU4NS1mY2M0ODQ3NDdjYzkiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzQzODQ2NDE3LCJleHAiOjE3NDYzOTYwMDB9.7fm0EcDsC7DikCxLWPS5HYp80jPPK2RpOMpQje-7t3o\",\n  \"Accept\" : \"application/json\"\n}",
  132.         "options": {}
  133.       },
  134.       "type": "n8n-nodes-base.httpRequest",
  135.       "typeVersion": 4.2,
  136.       "position": [
  137.         340,
  138.         920
  139.       ],
  140.       "id": "d7be0b01-8756-495f-8fab-e638181477c8",
  141.       "name": "HTTP Request2"
  142.     },
  143.     {
  144.       "parameters": {
  145.         "jsCode": "const input = $input.first().json;\nconst data = input.data;\nconst workflowInfo = input.workflowData;\nlet nodes = []\nlet nonAiNodes = []\nlet subworkflowNodes = []\nlet aiNodes = []\n\ngetNodesInfo(data)\n\nconst workFlowData = {\n  \"nombre\" : workflowInfo.name,\n  \"fecha_ejecucion\" : formatDate(input.startedAt),\n  \"duracion\" : getExecutionDuration(input.startedAt, input.stoppedAt),\n  \"ai_nodes\" : aiNodes,\n  \"normal_nodes\" : nonAiNodes,\n  \"sub_workflows\" : subworkflowNodes\n  \n}\n\n\nfunction getNodesInfo(data) {\n  let nodesCount = 0\n  \n  const object = Object.keys(data.resultData.runData);\n  object.map((node) => {\n    if( data.resultData.runData[node][0].executionStatus == 'success') {\n      nodesCount += 1;\n      nodes.push(node)\n      \n    }\n  })\n\n  for (let i = 0; i < nodes.length; i++) {\n    const element = nodes[i];\n    \n    getTypeNode(element)\n    \n  }\n}\n\nfunction getTypeNode(node){\n  for (let index = 0; index < $input.first().json.workflowData.nodes.length; index++) {\n    const element = $input.first().json.workflowData.nodes[index];\n    if(element.name == node) {\n      if(element.type.includes('lmChat')) {\n        // AI Node\n        const nodeTokenInfo = $input.first().json.data.resultData.runData[element.name][0].data.ai_languageModel[0][0].json.tokenUsage\n        const modelUsed =  $input.first().json.data.resultData.runData[element.name][0].inputOverride.ai_languageModel[0][0].json.options.model\n        aiNodes.push({\n          \"node_name\" : node,\n          \"model_used\" : modelUsed,\n          \"completion_tokens\" : nodeTokenInfo.completionTokens,\n          \"prompt_tokens\" : nodeTokenInfo.promptTokens,\n          \"total_tokens\" : nodeTokenInfo.totalTokens\n        })\n      } else if( element.type.includes('agent')) {\n        aiNodes.push({\n          \"node_name\" : node,\n          \"executionTime\" :  $input.first().json.data.resultData.runData[element.name][0].executionTime\n        })\n      } \n      \n      else if(element.type.includes('toolWorkflow') || element.type.includes('executeWorkflow') && !element.type.includes('executeWorkflowTrigger')) {\n        let nodeObject = {}\n        const runDataNode = $input.first().json.data.resultData.runData[element.name][0].metadata.subExecution\n        nodeObject[\"nombre\"] = node\n        nodeObject[\"execution_id\"] = runDataNode.executionId\n        nodeObject[\"workflow_id\"] = runDataNode.workflowId\n        subworkflowNodes.push(nodeObject)\n      } else if(element.type.includes('executeWorkflowTrigger')){\n        break\n      }\n      else {\n        // Non AI Workflow\n        nonAiNodes.push(node)\n      }\n    } \n  }\n\n}\n\nfunction formatDate(date){\n    const dateO = new Date(date);\n\n  const day = String(dateO.getUTCDate()).padStart(2, '0');\n  const month = String(dateO.getUTCMonth() + 1).padStart(2, '0');\n  const year = dateO.getUTCFullYear();\n\n  const hours = String(dateO.getUTCHours()).padStart(2, '0');\n  const minutes = String(dateO.getUTCMinutes()).padStart(2, '0');\n  const seconds = String(dateO.getUTCSeconds()).padStart(2, '0');\n\n  return `${day}/${month}/${year} - ${hours}:${minutes}:${seconds}`;\n}\nfunction getExecutionDuration(started, ended) {\n  const startDate = new Date(started)\n  const endDate = new Date(ended)\n  const durationMS = endDate - startDate\n  return durationMS / 1000;\n}\n\n\nreturn  workFlowData\n\n"
  146.       },
  147.       "type": "n8n-nodes-base.code",
  148.       "typeVersion": 2,
  149.       "position": [
  150.         540,
  151.         920
  152.       ],
  153.       "id": "bb4340ad-74a6-457f-b578-5623eeda9897",
  154.       "name": "Code5"
  155.     },
  156.     {
  157.       "parameters": {},
  158.       "type": "n8n-nodes-base.merge",
  159.       "typeVersion": 3.1,
  160.       "position": [
  161.         880,
  162.         320
  163.       ],
  164.       "id": "94f9af63-3c65-4b72-bb77-03bdf19aca38",
  165.       "name": "Merge2"
  166.     },
  167.     {
  168.       "parameters": {
  169.         "conditions": {
  170.           "options": {
  171.             "caseSensitive": true,
  172.             "leftValue": "",
  173.             "typeValidation": "strict",
  174.             "version": 2
  175.           },
  176.           "conditions": [
  177.             {
  178.               "id": "a8e9a937-7e5b-4145-aabe-71c7077045bd",
  179.               "leftValue": "={{ $json.ai_nodes[0] }}",
  180.               "rightValue": "",
  181.               "operator": {
  182.                 "type": "object",
  183.                 "operation": "exists",
  184.                 "singleValue": true
  185.               }
  186.             },
  187.             {
  188.               "id": "98db7a1a-3af3-465a-813d-1a17416b02c3",
  189.               "leftValue": "={{ $json.ai_nodes[0] }}",
  190.               "rightValue": "",
  191.               "operator": {
  192.                 "type": "object",
  193.                 "operation": "notEmpty",
  194.                 "singleValue": true
  195.               }
  196.             }
  197.           ],
  198.           "combinator": "and"
  199.         },
  200.         "options": {}
  201.       },
  202.       "type": "n8n-nodes-base.if",
  203.       "typeVersion": 2.2,
  204.       "position": [
  205.         700,
  206.         760
  207.       ],
  208.       "id": "d92efab1-d565-4ee6-8649-c90b1dba1df3",
  209.       "name": "If3"
  210.     },
  211.     {
  212.       "parameters": {
  213.         "jsCode": "const normal_nodes = $input.first().json.normal_nodes\nconst nodes_runData = $('HTTP Request2').first().json.data.resultData.runData\nconst workflowData  = $('HTTP Request2').first().json.workflowData.nodes\nconst executedNodes = []\nconst nonAiNodes = []\ngetExecutedNodesWorkFlowData()\nfunction getExecutedNodesWorkFlowData(){\n  for(let y = 0; y < normal_nodes.length ; y ++) {\n    const nodeName = normal_nodes[y]\n    const workflowDataNode = workflowData.find((element, index) => element.name == nodeName)\n    let runDataNode = {}\n    for(const key in nodes_runData) {\n      if (key === nodeName) {\n        runDataNode = nodes_runData[key][0]\n        break\n      }\n    }\n    nonAiNodes.push({\n      \"name\" : nodeName,\n      \"type\" : workflowDataNode.type,\n      \"execution_time\" : runDataNode.executionTime\n    })\n  }  \n}\n\n\nreturn {\n  \"non_ai_nodes\" : nonAiNodes\n}"
  214.       },
  215.       "type": "n8n-nodes-base.code",
  216.       "typeVersion": 2,
  217.       "position": [
  218.         900,
  219.         920
  220.       ],
  221.       "id": "68b31f5f-c55f-4d84-81af-77266103a95d",
  222.       "name": "Code3"
  223.     },
  224.     {
  225.       "parameters": {
  226.         "url": "https://raw.githubusercontent.com/Xavi1995/models_pricing/refs/heads/main/prices",
  227.         "options": {
  228.           "response": {
  229.             "response": {
  230.               "responseFormat": "json"
  231.             }
  232.           }
  233.         }
  234.       },
  235.       "type": "n8n-nodes-base.httpRequest",
  236.       "typeVersion": 4.2,
  237.       "position": [
  238.         1020,
  239.         740
  240.       ],
  241.       "id": "230ce152-ff64-4a37-ad69-407efa3125e0",
  242.       "name": "get_model_prices1"
  243.     },
  244.     {
  245.       "parameters": {
  246.         "jsCode": "const prices = $input.first().json\nconst aiNodes = $('If3').first().json.ai_nodes\nconst aiNodesInfo = []\nconst nodesData = $('HTTP Request2').first().json.data.resultData.runData\nconst nodeWorkflowData = $('HTTP Request2').first().json.workflowData.nodes\n\n\nfor(let i = 0; i < aiNodes.length; i++) {\n  const aiNode = aiNodes[i]\n  const nodeData = nodesData[aiNode.node_name]\n  for(let y = 0; y < nodeWorkflowData.length ; y ++) {\n    if(aiNode.node_name == nodeWorkflowData[y].name) {\n      aiNodesInfo.push(getAiNodesInfo(aiNode, nodeWorkflowData[y], nodeData))\n    }\n  }\n}\n\nfunction getAiNodesInfo(node, workflowData, nodeData){\n  if(node.model_used == null) {\n    return {\n      \"name\" : node.node_name,\n      \"type\" : workflowData.type,\n      \"execution_time\" : nodeData[0].executionTime\n    }\n  } else {\n    return {\n    \"name\" : node.node_name,\n    \"type\" : workflowData.type,\n    \"execution_time\" : nodeData[0].executionTime,\n    \"ai_details\" : {\n      \"model_used\" : node.model_used,\n      \"completion_tokens\" : node.completion_tokens,\n      \"prompt_tokens\" : node.prompt_tokens,\n      \"total_tokens\" : node.total_tokens,\n      \"cost_execution\" : getExecutionCost(node)\n    }\n  }\n  }\n}\n\nfunction getExecutionCost(node){\n    const completionTokens = node.completion_tokens\n    const promptTokens = node.prompt_tokens\n    let price = {}\n    \n    const model = node.model_used;\n    if(model == 'deepseek-chat' || model == 'deepseek-reasoner') {\n      const now = new Date($('get_execution_info').first().json.startedAt)\n      const currentHour = now.getUTCHours()\n      const currentMinute = now.getUTCMinutes()\n\n      const totalMinutes = currentHour * 60 + currentMinute\n\n      const startThreshold = 0 * 60 + 30\n      const endThreshold = 16 * 60 + 30\n      \n      if(totalMinutes >= startThreshold && totalMinutes < endThreshold) {\n        price = prices[`${model}-standard`]\n      } else {\n        price = prices[`${model}-discount`]\n      }\n    } else {\n      price = prices[model];\n    }\n    const nodePrice = (completionTokens / 1000) * price.completion + (promptTokens / 1000) * price.prompt;\n    if(nodePrice >= 0.01) {\n      return `$${nodePrice.toFixed(2)}`;\n    } else if(nodePrice >= 0.0001){\n      return `$${nodePrice.toFixed(4)}`;\n    } else {\n      return `$${nodePrice.toFixed(8)}`\n    }\n   \n    \n}\n\nconst output = aiNodesInfo\n\nreturn {\n  \"ai_nodes\" : output\n}"
  247.       },
  248.       "type": "n8n-nodes-base.code",
  249.       "typeVersion": 2,
  250.       "position": [
  251.         1280,
  252.         740
  253.       ],
  254.       "id": "e05f8040-41b6-4bff-b7db-83cbafc4a856",
  255.       "name": "get_ai_nodes_info1"
  256.     },
  257.     {
  258.       "parameters": {
  259.         "mode": "combine",
  260.         "combineBy": "combineByPosition",
  261.         "options": {
  262.           "includeUnpaired": true
  263.         }
  264.       },
  265.       "type": "n8n-nodes-base.merge",
  266.       "typeVersion": 3.1,
  267.       "position": [
  268.         1500,
  269.         900
  270.       ],
  271.       "id": "de73497a-cc00-41ff-97be-3a88ff013287",
  272.       "name": "Merge1",
  273.       "alwaysOutputData": true
  274.     },
  275.     {
  276.       "parameters": {
  277.         "jsCode": "const input = $input.first().json\nconst output = {}\n\noutput[\"workflow_name\"] = $('Code5').first().json.nombre\noutput[\"workflow_id\"] = $('HTTP Request2').first().json.workflowId\noutput[\"execution_id\"] = $('HTTP Request2').first().json.id\noutput[\"finished\"] = $('HTTP Request2').first().json.finished\noutput[\"execution_status\"] = $('HTTP Request2').first().json.status\noutput[\"execution_date\"] = $('Code5').first().json.fecha_ejecucion\noutput[\"execution_duration\"] = $('Code5').first().json.duracion\nif($input.first().json.ai_nodes != null) {\n  output[\"total_cost\"] = getTotalCost($input.first().json.ai_nodes)\n}\noutput[\"nodes\"] = input\n\nfunction getTotalCost(aiNodes) {\n  let totalCost = 0.0\n  for(let i=0 ; i < aiNodes.length; i++) {\n    const aiNode = aiNodes[i]\n\n    if(aiNode.ai_details != null) {\n      let aiNodeCost\n      aiNodeCost = aiNode.ai_details.cost_execution.replace('$', '')\n      totalCost += parseFloat(aiNodeCost)\n    }\n  }\n\n  return totalCost\n}\n\nreturn output"
  278.       },
  279.       "type": "n8n-nodes-base.code",
  280.       "typeVersion": 2,
  281.       "position": [
  282.         1680,
  283.         900
  284.       ],
  285.       "id": "7334d5de-5bd8-44c7-a44e-e447c149f4f6",
  286.       "name": "Code6"
  287.     },
  288.     {
  289.       "parameters": {
  290.         "aggregate": "aggregateAllItemData",
  291.         "destinationFieldName": "",
  292.         "options": {}
  293.       },
  294.       "type": "n8n-nodes-base.aggregate",
  295.       "typeVersion": 1,
  296.       "position": [
  297.         360,
  298.         500
  299.       ],
  300.       "id": "3304ef9b-8b59-4542-b37d-1b58ebf1cf63",
  301.       "name": "Aggregate"
  302.     },
  303.     {
  304.       "parameters": {
  305.         "jsCode": "const input = $input.first().json;\nconst data = input.data;\nconst workflowInfo = input.workflowData;\nlet nodes = []\nlet nonAiNodes = []\nlet subworkflowNodes = []\nlet aiNodes = []\n\ngetNodesInfo(data)\n\nconst workFlowData = {\n  \"nombre\" : workflowInfo.name,\n  \"fecha_ejecucion\" : formatDate(input.startedAt),\n  \"duracion\" : getExecutionDuration(input.startedAt, input.stoppedAt),\n  \"ai_nodes\" : aiNodes,\n  \"normal_nodes\" : nonAiNodes,\n  \"sub_workflows\" : subworkflowNodes\n  \n}\n\n\nfunction getNodesInfo(data) {\n  let nodesCount = 0\n  \n  const object = Object.keys(data.resultData.runData);\n  object.map((node) => {\n    if( data.resultData.runData[node][0].executionStatus == 'success') {\n      nodesCount += 1;\n      nodes.push(node)\n      \n    }\n  })\n\n  for (let i = 0; i < nodes.length; i++) {\n    const element = nodes[i];\n    \n    getTypeNode(element)\n    \n  }\n}\n\nfunction getTypeNode(node){\n  for (let index = 0; index < $input.first().json.workflowData.nodes.length; index++) {\n    const element = $input.first().json.workflowData.nodes[index];\n    if(element.name == node) {\n      \n      if(element.type.includes('lmChat')) {\n        var nodeTokenInfo\n        var modelUsed\n        // AI Node\n        if(element.type.includes('Gemini') || element.type.includes('gemini')) {\n            nodeTokenInfo = $input.first().json.data.resultData.runData[element.name][0].data.ai_languageModel[0][0].json.tokenUsageEstimate\n          modelUsed = $input.first().json.data.resultData.runData[element.name][0].inputOverride.ai_languageModel[0][0].json.options.model_name\n        } else {\n          modelUsed = $input.first().json.data.resultData.runData[element.name][0].inputOverride.ai_languageModel[0][0].json.options.model\n          nodeTokenInfo = $input.first().json.data.resultData.runData[element.name][0].data.ai_languageModel[0][0].json.tokenUsage\n        }\n        \n        aiNodes.push({\n          \"node_name\" : node,\n          \"model_used\" : modelUsed,\n          \"completion_tokens\" : nodeTokenInfo.completionTokens,\n          \"prompt_tokens\" : nodeTokenInfo.promptTokens,\n          \"total_tokens\" : nodeTokenInfo.totalTokens\n        })\n      } else if( element.type.includes('agent')) {\n        aiNodes.push({\n          \"node_name\" : node,\n          \"executionTime\" :  $input.first().json.data.resultData.runData[element.name][0].executionTime\n        })\n      } \n      \n      else if(element.type.includes('toolWorkflow') || element.type.includes('executeWorkflow')&& !element.type.includes('executeWorkflowTrigger')) {\n        let nodeObject = {}\n        const runDataNode = $input.first().json.data.resultData.runData[element.name][0].metadata.subExecution\n        nodeObject[\"nombre\"] = node\n        nodeObject[\"execution_id\"] = runDataNode.executionId\n        nodeObject[\"workflow_id\"] = runDataNode.workflowId\n        subworkflowNodes.push(nodeObject)\n      } \n      else {\n        // Non AI Workflow\n        nonAiNodes.push(node)\n      }\n    } \n  }\n\n}\n\nfunction formatDate(date){\n    const dateO = new Date(date);\n\n  const day = String(dateO.getUTCDate()).padStart(2, '0');\n  const month = String(dateO.getUTCMonth() + 1).padStart(2, '0');\n  const year = dateO.getUTCFullYear();\n\n  const hours = String(dateO.getUTCHours()).padStart(2, '0');\n  const minutes = String(dateO.getUTCMinutes()).padStart(2, '0');\n  const seconds = String(dateO.getUTCSeconds()).padStart(2, '0');\n\n  return `${day}/${month}/${year} - ${hours}:${minutes}:${seconds}`;\n}\nfunction getExecutionDuration(started, ended) {\n  const startDate = new Date(started)\n  const endDate = new Date(ended)\n  const durationMS = endDate - startDate\n  return durationMS / 1000;\n}\n\n\nreturn  workFlowData\n\n"
  306.       },
  307.       "type": "n8n-nodes-base.code",
  308.       "typeVersion": 2,
  309.       "position": [
  310.         -560,
  311.         320
  312.       ],
  313.       "id": "f0f66afb-f63f-4aa0-8b52-261dd8445a87",
  314.       "name": "node_by_type"
  315.     },
  316.     {
  317.       "parameters": {
  318.         "conditions": {
  319.           "options": {
  320.             "caseSensitive": true,
  321.             "leftValue": "",
  322.             "typeValidation": "strict",
  323.             "version": 2
  324.           },
  325.           "conditions": [
  326.             {
  327.               "id": "a8e9a937-7e5b-4145-aabe-71c7077045bd",
  328.               "leftValue": "={{ $json.ai_nodes[0] }}",
  329.               "rightValue": "",
  330.               "operator": {
  331.                 "type": "object",
  332.                 "operation": "exists",
  333.                 "singleValue": true
  334.               }
  335.             },
  336.             {
  337.               "id": "98db7a1a-3af3-465a-813d-1a17416b02c3",
  338.               "leftValue": "={{ $json.ai_nodes[0] }}",
  339.               "rightValue": "",
  340.               "operator": {
  341.                 "type": "object",
  342.                 "operation": "notEmpty",
  343.                 "singleValue": true
  344.               }
  345.             }
  346.           ],
  347.           "combinator": "and"
  348.         },
  349.         "options": {}
  350.       },
  351.       "type": "n8n-nodes-base.if",
  352.       "typeVersion": 2.2,
  353.       "position": [
  354.         -320,
  355.         20
  356.       ],
  357.       "id": "216f9ba8-8c89-4f51-b332-e63056011b36",
  358.       "name": "has_ai_node"
  359.     },
  360.     {
  361.       "parameters": {
  362.         "conditions": {
  363.           "options": {
  364.             "caseSensitive": true,
  365.             "leftValue": "",
  366.             "typeValidation": "strict",
  367.             "version": 2
  368.           },
  369.           "conditions": [
  370.             {
  371.               "id": "293cacaf-64ca-429f-a21e-548967e6f5d1",
  372.               "leftValue": "={{ $json.sub_workflows }}",
  373.               "rightValue": "",
  374.               "operator": {
  375.                 "type": "array",
  376.                 "operation": "exists",
  377.                 "singleValue": true
  378.               }
  379.             },
  380.             {
  381.               "id": "7ef27dab-d33b-4107-bb8f-839b58b5d8db",
  382.               "leftValue": "={{ $json.sub_workflows }}",
  383.               "rightValue": "",
  384.               "operator": {
  385.                 "type": "array",
  386.                 "operation": "notEmpty",
  387.                 "singleValue": true
  388.               }
  389.             }
  390.           ],
  391.           "combinator": "and"
  392.         },
  393.         "options": {}
  394.       },
  395.       "type": "n8n-nodes-base.if",
  396.       "typeVersion": 2.2,
  397.       "position": [
  398.         -400,
  399.         680
  400.       ],
  401.       "id": "6081decd-c820-481f-85d1-ccfbfba4338d",
  402.       "name": "has_sub_workflows"
  403.     },
  404.     {
  405.       "parameters": {
  406.         "jsCode": "const normal_nodes = $input.first().json.normal_nodes\nconst nodes_runData = $('get_execution_info').first().json.data.resultData.runData\nconst workflowData  = $('get_execution_info').first().json.workflowData.nodes\nconst executedNodes = []\nconst nonAiNodes = []\ngetExecutedNodesWorkFlowData()\nfunction getExecutedNodesWorkFlowData(){\n  for(let y = 0; y < normal_nodes.length ; y ++) {\n    const nodeName = normal_nodes[y]\n    const workflowDataNode = workflowData.find((element, index) => element.name == nodeName)\n    let runDataNode = {}\n    for(const key in nodes_runData) {\n      if (key === nodeName) {\n        runDataNode = nodes_runData[key][0]\n        break\n      }\n    }\n    nonAiNodes.push({\n      \"name\" : nodeName,\n      \"type\" : workflowDataNode.type,\n      \"execution_time\" : runDataNode.executionTime\n    })\n  }  \n}\n\n\nreturn {\n  \"non_ai_nodes\" : nonAiNodes\n}"
  407.       },
  408.       "type": "n8n-nodes-base.code",
  409.       "typeVersion": 2,
  410.       "position": [
  411.         0,
  412.         320
  413.       ],
  414.       "id": "1a263f3b-d8c6-4acc-9676-dae127aca106",
  415.       "name": "non_ai_nodes_info"
  416.     },
  417.     {
  418.       "parameters": {
  419.         "jsCode": "const input = $input.first().json\nconst output = {}\n\noutput[\"workflow_name\"] = $('node_by_type').first().json.nombre\noutput[\"workflow_id\"] = $('get_execution_info').first().json.workflowId\noutput[\"execution_id\"] = $('get_execution_info').first().json.id\noutput[\"finished\"] = $('get_execution_info').first().json.finished\noutput[\"execution_status\"] = $('get_execution_info').first().json.status\noutput[\"execution_date\"] = $('node_by_type').first().json.fecha_ejecucion\noutput[\"execution_duration\"] = $('node_by_type').first().json.duracion\nif($input.first().json.ai_nodes != null) {\n  output[\"total_cost\"] = getTotalCost($input.first().json.ai_nodes)\n}\noutput[\"nodes\"] = input\n\n\n\nfunction getTotalCost(aiNodes) {\n  let totalCost = 0.0\n  for(let i=0 ; i < aiNodes.length; i++) {\n    const aiNode = aiNodes[i]\n\n    if(aiNode.ai_details != null) {\n      let aiNodeCost\n      aiNodeCost = aiNode.ai_details.cost_execution.replace('$', '')\n      totalCost += parseFloat(aiNodeCost)\n    }\n  }\n\n  return totalCost\n}\n\nreturn output"
  420.       },
  421.       "type": "n8n-nodes-base.code",
  422.       "typeVersion": 2,
  423.       "position": [
  424.         580,
  425.         300
  426.       ],
  427.       "id": "4d39d802-411b-4a90-bf64-f8b8e9970d8a",
  428.       "name": "clean_data"
  429.     },
  430.     {
  431.       "parameters": {
  432.         "jsCode": "const input = $input\nconst output = input.first().json\nconst subworkflows = input.last().json\n\nfor(const key in output) {\n  if(key == \"sub_workflows\") {\n    var sub_workflows = $input.first().json\n    output[\"sub_workflows\"] = sub_workflows  \n  }\n}\n\noutput[\"total_cost\"] = getTotalWorkflowCost()\noutput[\"sub_workflows\"] = getSubWorkflows()\n\nfunction getSubWorkflows(){\n  if($input.all().length > 1) {\n    return $input.last().json\n  } \n}\n  \n\nfunction getTotalWorkflowCost(){\n  let totalCost = 0.0\n  let sub_workflow_total_cost = 0.0\n  \n  \n  if(sub_workflows != null) {\n    for(let i = 0; i < sub_workflows[\"\"].length ; i++) {\n      const subWorkflow = sub_workflows[\"\"][i]\n      if(subWorkflow.total_cost != null) {\n        sub_workflow_total_cost += subWorkflow.total_cost  \n      }\n    }\n  }\n  if(output.total_cost != null) {\n    totalCost = output.total_cost\n  }\n  \n    if(totalCost >= 0.01) {\n      return `$${totalCost.toFixed(2)}`;\n    } else if(totalCost >= 0.0001){\n      return `$${totalCost.toFixed(4)}`;\n    } else {\n      return `$${totalCost.toFixed(8)}`\n    }\n\n  \n}\nreturn output\n"
  433.       },
  434.       "type": "n8n-nodes-base.code",
  435.       "typeVersion": 2,
  436.       "position": [
  437.         1120,
  438.         320
  439.       ],
  440.       "id": "3bae76ca-1445-495a-abc1-aa95860c99da",
  441.       "name": "output_json"
  442.     },
  443.     {
  444.       "parameters": {
  445.         "operation": "toJson",
  446.         "options": {}
  447.       },
  448.       "type": "n8n-nodes-base.convertToFile",
  449.       "typeVersion": 1.1,
  450.       "position": [
  451.         1340,
  452.         320
  453.       ],
  454.       "id": "5df5608e-de55-4186-a225-2d4cfeb37ab5",
  455.       "name": "get_json_file"
  456.     },
  457.     {
  458.       "parameters": {
  459.         "resource": "execution",
  460.         "operation": "get",
  461.         "executionId": "={{ $json['Execution ID'] }}",
  462.         "options": {
  463.           "activeWorkflows": true
  464.         },
  465.         "requestOptions": {}
  466.       },
  467.       "type": "n8n-nodes-base.n8n",
  468.       "typeVersion": 1,
  469.       "position": [
  470.         -740,
  471.         320
  472.       ],
  473.       "id": "54fcefa2-7b0c-4897-8231-09b6cdf55827",
  474.       "name": "get_execution_info",
  475.       "credentials": {
  476.         "n8nApi": {
  477.           "id": "7ZkDCy01RhiM5MZT",
  478.           "name": "n8n account"
  479.         }
  480.       }
  481.     },
  482.     {
  483.       "parameters": {
  484.         "command": "cd /tmp\npython3 /tmp/report_generator_script.py /tmp/file.json\n"
  485.       },
  486.       "type": "n8n-nodes-base.executeCommand",
  487.       "typeVersion": 1,
  488.       "position": [
  489.         1760,
  490.         320
  491.       ],
  492.       "id": "a7512663-e773-4be7-b44b-361c3a5695ff",
  493.       "name": "Execute Command"
  494.     },
  495.     {
  496.       "parameters": {
  497.         "operation": "write",
  498.         "fileName": "=/tmp/file.json",
  499.         "options": {
  500.           "append": false
  501.         }
  502.       },
  503.       "type": "n8n-nodes-base.readWriteFile",
  504.       "typeVersion": 1,
  505.       "position": [
  506.         1540,
  507.         320
  508.       ],
  509.       "id": "e474265f-21e0-4af3-bfa9-95e8017d2734",
  510.       "name": "Write Files to Disk"
  511.     },
  512.     {
  513.       "parameters": {
  514.         "formTitle": "Workflow Auditor",
  515.         "formDescription": "Enter a workflow execution ID to get a report.",
  516.         "formFields": {
  517.           "values": [
  518.             {
  519.               "fieldLabel": "Execution ID",
  520.               "fieldType": "number",
  521.               "placeholder": "12345",
  522.               "requiredField": true
  523.             }
  524.           ]
  525.         },
  526.         "options": {
  527.           "path": "beepboop"
  528.         }
  529.       },
  530.       "type": "n8n-nodes-base.formTrigger",
  531.       "typeVersion": 2.2,
  532.       "position": [
  533.         -960,
  534.         320
  535.       ],
  536.       "id": "683f70d6-9edc-44da-b2ed-1d80625f78d0",
  537.       "name": "On form submission",
  538.       "webhookId": "6ad3c538-ac7e-486e-8f74-b7849f44ed33"
  539.     },
  540.     {
  541.       "parameters": {
  542.         "operation": "completion",
  543.         "respondWith": "returnBinary",
  544.         "completionTitle": "Done",
  545.         "completionMessage": "done",
  546.         "options": {}
  547.       },
  548.       "type": "n8n-nodes-base.form",
  549.       "typeVersion": 1,
  550.       "position": [
  551.         2200,
  552.         320
  553.       ],
  554.       "id": "61e272c7-4594-470e-89a9-2588af8d2ac2",
  555.       "name": "Form",
  556.       "webhookId": "b526c67c-32ef-488c-b99a-479d755f2595"
  557.     },
  558.     {
  559.       "parameters": {
  560.         "fileSelector": "/tmp/workflow_report.pdf",
  561.         "options": {}
  562.       },
  563.       "type": "n8n-nodes-base.readWriteFile",
  564.       "typeVersion": 1,
  565.       "position": [
  566.         1980,
  567.         320
  568.       ],
  569.       "id": "05d1e123-6ea9-40e9-b449-dc0765064d29",
  570.       "name": "Read Report from Disk"
  571.     }
  572.   ],
  573.   "connections": {
  574.     "get_model_prices": {
  575.       "main": [
  576.         [
  577.           {
  578.             "node": "get_ai_nodes_info",
  579.             "type": "main",
  580.             "index": 0
  581.           }
  582.         ]
  583.       ]
  584.     },
  585.     "Loop Over Items": {
  586.       "main": [
  587.         [
  588.           {
  589.             "node": "Aggregate",
  590.             "type": "main",
  591.             "index": 0
  592.           }
  593.         ],
  594.         [
  595.           {
  596.             "node": "HTTP Request2",
  597.             "type": "main",
  598.             "index": 0
  599.           }
  600.         ]
  601.       ]
  602.     },
  603.     "Code2": {
  604.       "main": [
  605.         [
  606.           {
  607.             "node": "Loop Over Items",
  608.             "type": "main",
  609.             "index": 0
  610.           }
  611.         ]
  612.       ]
  613.     },
  614.     "get_ai_nodes_info": {
  615.       "main": [
  616.         [
  617.           {
  618.             "node": "Merge",
  619.             "type": "main",
  620.             "index": 0
  621.           }
  622.         ]
  623.       ]
  624.     },
  625.     "Merge": {
  626.       "main": [
  627.         [
  628.           {
  629.             "node": "clean_data",
  630.             "type": "main",
  631.             "index": 0
  632.           }
  633.         ]
  634.       ]
  635.     },
  636.     "HTTP Request2": {
  637.       "main": [
  638.         [
  639.           {
  640.             "node": "Code5",
  641.             "type": "main",
  642.             "index": 0
  643.           }
  644.         ]
  645.       ]
  646.     },
  647.     "Code5": {
  648.       "main": [
  649.         [
  650.           {
  651.             "node": "If2",
  652.             "type": "main",
  653.             "index": 0
  654.           },
  655.           {
  656.             "node": "If3",
  657.             "type": "main",
  658.             "index": 0
  659.           },
  660.           {
  661.             "node": "Code3",
  662.             "type": "main",
  663.             "index": 0
  664.           }
  665.         ]
  666.       ]
  667.     },
  668.     "Merge2": {
  669.       "main": [
  670.         [
  671.           {
  672.             "node": "output_json",
  673.             "type": "main",
  674.             "index": 0
  675.           }
  676.         ]
  677.       ]
  678.     },
  679.     "If3": {
  680.       "main": [
  681.         [
  682.           {
  683.             "node": "get_model_prices1",
  684.             "type": "main",
  685.             "index": 0
  686.           }
  687.         ]
  688.       ]
  689.     },
  690.     "Code3": {
  691.       "main": [
  692.         [
  693.           {
  694.             "node": "Merge1",
  695.             "type": "main",
  696.             "index": 1
  697.           }
  698.         ]
  699.       ]
  700.     },
  701.     "get_model_prices1": {
  702.       "main": [
  703.         [
  704.           {
  705.             "node": "get_ai_nodes_info1",
  706.             "type": "main",
  707.             "index": 0
  708.           }
  709.         ]
  710.       ]
  711.     },
  712.     "get_ai_nodes_info1": {
  713.       "main": [
  714.         [
  715.           {
  716.             "node": "Merge1",
  717.             "type": "main",
  718.             "index": 0
  719.           }
  720.         ]
  721.       ]
  722.     },
  723.     "Merge1": {
  724.       "main": [
  725.         [
  726.           {
  727.             "node": "Code6",
  728.             "type": "main",
  729.             "index": 0
  730.           }
  731.         ]
  732.       ]
  733.     },
  734.     "Code6": {
  735.       "main": [
  736.         [
  737.           {
  738.             "node": "Loop Over Items",
  739.             "type": "main",
  740.             "index": 0
  741.           }
  742.         ]
  743.       ]
  744.     },
  745.     "Aggregate": {
  746.       "main": [
  747.         [
  748.           {
  749.             "node": "Merge2",
  750.             "type": "main",
  751.             "index": 1
  752.           }
  753.         ]
  754.       ]
  755.     },
  756.     "node_by_type": {
  757.       "main": [
  758.         [
  759.           {
  760.             "node": "has_ai_node",
  761.             "type": "main",
  762.             "index": 0
  763.           },
  764.           {
  765.             "node": "non_ai_nodes_info",
  766.             "type": "main",
  767.             "index": 0
  768.           },
  769.           {
  770.             "node": "has_sub_workflows",
  771.             "type": "main",
  772.             "index": 0
  773.           }
  774.         ]
  775.       ]
  776.     },
  777.     "has_ai_node": {
  778.       "main": [
  779.         [
  780.           {
  781.             "node": "get_model_prices",
  782.             "type": "main",
  783.             "index": 0
  784.           }
  785.         ]
  786.       ]
  787.     },
  788.     "has_sub_workflows": {
  789.       "main": [
  790.         [
  791.           {
  792.             "node": "Code2",
  793.             "type": "main",
  794.             "index": 0
  795.           }
  796.         ]
  797.       ]
  798.     },
  799.     "non_ai_nodes_info": {
  800.       "main": [
  801.         [
  802.           {
  803.             "node": "Merge",
  804.             "type": "main",
  805.             "index": 1
  806.           }
  807.         ]
  808.       ]
  809.     },
  810.     "clean_data": {
  811.       "main": [
  812.         [
  813.           {
  814.             "node": "Merge2",
  815.             "type": "main",
  816.             "index": 0
  817.           }
  818.         ]
  819.       ]
  820.     },
  821.     "output_json": {
  822.       "main": [
  823.         [
  824.           {
  825.             "node": "get_json_file",
  826.             "type": "main",
  827.             "index": 0
  828.           }
  829.         ]
  830.       ]
  831.     },
  832.     "get_json_file": {
  833.       "main": [
  834.         [
  835.           {
  836.             "node": "Write Files to Disk",
  837.             "type": "main",
  838.             "index": 0
  839.           }
  840.         ]
  841.       ]
  842.     },
  843.     "get_execution_info": {
  844.       "main": [
  845.         [
  846.           {
  847.             "node": "node_by_type",
  848.             "type": "main",
  849.             "index": 0
  850.           }
  851.         ]
  852.       ]
  853.     },
  854.     "Execute Command": {
  855.       "main": [
  856.         [
  857.           {
  858.             "node": "Read Report from Disk",
  859.             "type": "main",
  860.             "index": 0
  861.           }
  862.         ]
  863.       ]
  864.     },
  865.     "Write Files to Disk": {
  866.       "main": [
  867.         [
  868.           {
  869.             "node": "Execute Command",
  870.             "type": "main",
  871.             "index": 0
  872.           }
  873.         ]
  874.       ]
  875.     },
  876.     "On form submission": {
  877.       "main": [
  878.         [
  879.           {
  880.             "node": "get_execution_info",
  881.             "type": "main",
  882.             "index": 0
  883.           }
  884.         ]
  885.       ]
  886.     },
  887.     "Read Report from Disk": {
  888.       "main": [
  889.         [
  890.           {
  891.             "node": "Form",
  892.             "type": "main",
  893.             "index": 0
  894.           }
  895.         ]
  896.       ]
  897.     }
  898.   },
  899.   "pinData": {},
  900.   "meta": {
  901.     "templateCredsSetupCompleted": true,
  902.     "instanceId": "244bb51ba30d6d9051ed76fcc82ed11719af55cd3f23b418fae148137cfe5048"
  903.   }
  904. }
Advertisement
Add Comment
Please, Sign In to add comment