{
  "name": "UFRO Orquestador PP3 Workflow - Corrected",
  "nodes": [
    {
      "parameters": {
        "path": "identify-and-answer",
        "httpMethod": "POST",
        "options": {
          "allowedOrigins": "*"
        }
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "rightValue": ""
          },
          "conditions": [
            {
              "id": "has-image",
              "leftValue": "={{ $json.files && $json.files.image !== undefined }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        },
        "combineConditions": "any"
      },
      "id": "check-image",
      "name": "Check Image",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        300,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { error: 'No image provided', status: 400 } }}",
        "responseCode": 400,
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "error-no-image",
      "name": "Error: No Image",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        500,
        180
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://54.205.31.20:5000/verify",
        "sendBody": true,
        "contentType": "form",
        "bodyParameters": {
          "parameters": [
            {
              "name": "image",
              "value": "={{ $json.files.image.data }}"
            }
          ]
        },
        "options": {
          "timeout": 8000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "pp2-verification-1",
      "name": "PP2-1 Verification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        600,
        200
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://52.201.69.13:5000/verify",
        "sendBody": true,
        "contentType": "form",
        "bodyParameters": {
          "parameters": [
            {
              "name": "image",
              "value": "={{ $json.files.image.data }}"
            }
          ]
        },
        "options": {
          "timeout": 8000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "pp2-verification-2",
      "name": "PP2-2 Verification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        600,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://example-pp2-3.com:5000/verify",
        "sendBody": true,
        "contentType": "form",
        "bodyParameters": {
          "parameters": [
            {
              "name": "image",
              "value": "={{ $json.files.image.data }}"
            }
          ]
        },
        "options": {
          "timeout": 8000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "pp2-verification-3",
      "name": "PP2-3 Verification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        600,
        400
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "multiplex"
      },
      "id": "merge-pp2-results",
      "name": "Merge PP2 Results",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2,
      "position": [
        800,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const THRESHOLD = 0.75;\nconst MARGIN = 0.2;\n\n// Procesar todos los resultados de PP2\nconst items = $input.all();\nconst allCandidates = [];\n\n// Extraer candidatos de todas las respuestas PP2\nitems.forEach((item, index) => {\n  const data = item.json;\n  if (data && !data.error) {\n    if (data.candidates && Array.isArray(data.candidates)) {\n      allCandidates.push(...data.candidates);\n    } else if (data.name && data.score !== undefined) {\n      // Formato directo\n      allCandidates.push({ name: data.name, score: data.score, service: `PP2-${index+1}` });\n    } else if (data.is_me === true && data.score) {\n      // Otro formato posible\n      allCandidates.push({ name: `User-${index+1}`, score: data.score, service: `PP2-${index+1}` });\n    }\n  }\n});\n\n// Agrupar por nombre y tomar el mejor score\nconst candidateMap = {};\nallCandidates.forEach(candidate => {\n  const name = candidate.name;\n  if (!candidateMap[name] || candidateMap[name].score < candidate.score) {\n    candidateMap[name] = candidate;\n  }\n});\n\nconst finalCandidates = Object.values(candidateMap).sort((a, b) => b.score - a.score);\n\n// Aplicar l\u00f3gica de fusi\u00f3n\nlet decision, identity, confidence;\n\nif (finalCandidates.length === 0) {\n  decision = 'unknown';\n  identity = null;\n  confidence = 0;\n} else {\n  const topCandidate = finalCandidates[0];\n  const secondCandidate = finalCandidates[1];\n  \n  if (topCandidate.score >= THRESHOLD) {\n    if (!secondCandidate || (topCandidate.score - secondCandidate.score) >= MARGIN) {\n      decision = 'identified';\n      identity = {\n        name: topCandidate.name,\n        score: topCandidate.score\n      };\n      confidence = topCandidate.score;\n    } else {\n      decision = 'ambiguous';\n      identity = {\n        name: topCandidate.name,\n        score: topCandidate.score\n      };\n      confidence = topCandidate.score - (secondCandidate ? secondCandidate.score : 0);\n    }\n  } else {\n    decision = 'unknown';\n    identity = null;\n    confidence = topCandidate.score;\n  }\n}\n\n// Agregar informaci\u00f3n original de entrada para el siguiente nodo\nconst originalData = items[0] ? items[0].json : {};\n\nreturn {\n  ...originalData,\n  pp2_results: {\n    decision: decision,\n    identity: identity,\n    candidates: finalCandidates.slice(0, 5),\n    confidence: confidence,\n    fusion_params: {\n      threshold: THRESHOLD,\n      margin: MARGIN\n    },\n    total_services: items.length,\n    successful_services: items.filter(item => item.json && !item.json.error).length\n  }\n};"
      },
      "id": "fusion-pp2",
      "name": "Fusion PP2 Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1000,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "rightValue": ""
          },
          "conditions": [
            {
              "id": "has-question",
              "leftValue": "={{ $json.query && $json.query.question && $json.query.question.trim() !== '' }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        },
        "combineConditions": "any"
      },
      "id": "check-question",
      "name": "Check Question",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1200,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://52.201.69.13:5000/api/chat",
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ message: $json.query.question }) }}",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "pp1-normativa",
      "name": "PP1 Normativa Query",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1400,
        250
      ]
    },
    {
      "parameters": {
        "mode": "append"
      },
      "id": "merge-final-results",
      "name": "Merge Final Results",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2,
      "position": [
        1600,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const startTime = Date.now();\nconst items = $input.all();\n\n// El primer item tiene los resultados de PP2 fusion\nconst pp2Data = items[0] ? items[0].json : {};\nconst pp2Results = pp2Data.pp2_results || {};\n\n// El segundo item (si existe) tiene los resultados de PP1\nlet pp1Results = null;\nif (items.length > 1 && items[1] && items[1].json && items[1].json.result) {\n  const pp1Data = items[1].json.result;\n  pp1Results = {\n    text: pp1Data.answer || 'No answer provided',\n    citations: pp1Data.sources || [],\n    confidence: pp1Data.metrics ? pp1Data.metrics.confidence : 0.8,\n    provider: pp1Data.provider || 'unknown'\n  };\n}\n\n// Construir respuesta final seg\u00fan especificaci\u00f3n PP3\nconst response = {\n  request_id: Math.random().toString(36).substring(7),\n  timestamp: new Date().toISOString(),\n  decision: pp2Results.decision || 'unknown',\n  identity: pp2Results.identity || null,\n  candidates: pp2Results.candidates || [],\n  normativa_answer: pp1Results,\n  timing_ms: Date.now() - startTime,\n  source: 'n8n-workflow',\n  fusion_params: pp2Results.fusion_params || { threshold: 0.75, margin: 0.2 },\n  pp2_summary: {\n    queried: pp2Results.total_services || 0,\n    successful: pp2Results.successful_services || 0,\n    timeouts: (pp2Results.total_services || 0) - (pp2Results.successful_services || 0)\n  },\n  pp1_used: pp1Results !== null\n};\n\nreturn response;"
      },
      "id": "final-response",
      "name": "Final Response Builder",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1800,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        }
      },
      "id": "webhook-response",
      "name": "Webhook Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        2000,
        300
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Check Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Image": {
      "main": [
        [
          {
            "node": "PP2-1 Verification",
            "type": "main",
            "index": 0
          },
          {
            "node": "PP2-2 Verification",
            "type": "main",
            "index": 0
          },
          {
            "node": "PP2-3 Verification",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error: No Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PP2-1 Verification": {
      "main": [
        [
          {
            "node": "Merge PP2 Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PP2-2 Verification": {
      "main": [
        [
          {
            "node": "Merge PP2 Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "PP2-3 Verification": {
      "main": [
        [
          {
            "node": "Merge PP2 Results",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Merge PP2 Results": {
      "main": [
        [
          {
            "node": "Fusion PP2 Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fusion PP2 Results": {
      "main": [
        [
          {
            "node": "Check Question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Question": {
      "main": [
        [
          {
            "node": "PP1 Normativa Query",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge Final Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PP1 Normativa Query": {
      "main": [
        [
          {
            "node": "Merge Final Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Final Results": {
      "main": [
        [
          {
            "node": "Final Response Builder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Final Response Builder": {
      "main": [
        [
          {
            "node": "Webhook Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "createdAt": "2025-11-29T00:00:00.000Z",
      "updatedAt": "2025-11-29T00:00:00.000Z",
      "id": "ufro-orquestador-v2",
      "name": "UFRO Orquestador V2"
    }
  ],
  "triggerCount": 1,
  "updatedAt": "2025-11-29T00:00:00.000Z",
  "versionId": "2.0.0",
  "meta": {
    "description": "UFRO Orquestador PP3 Workflow - Fixed version with proper sequential flow and parallel PP2 calls",
    "author": "UFRO Computer Science Department",
    "created": "2025-11-29T00:00:00.000Z",
    "version": "2.0.0",
    "tags": [
      "ufro",
      "identity",
      "normativa",
      "pp2",
      "pp1",
      "orquestador",
      "fixed"
    ]
  }
}