Merge branch 'main' of https://git.cesconsultoria.com/ldiaz/Agent-IA
This commit is contained in:
BIN
Python/pdf-extractor-api/__pycache__/main.cpython-314.pyc
Normal file
BIN
Python/pdf-extractor-api/__pycache__/main.cpython-314.pyc
Normal file
Binary file not shown.
63
Python/pdf-extractor-api/main.py
Normal file
63
Python/pdf-extractor-api/main.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from fastapi import FastAPI, UploadFile, File
|
||||
import pdfplumber
|
||||
import docx
|
||||
import io
|
||||
import re
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# 🔥 CONFIGURACIÓN CLAVE
|
||||
MAX_PAGES = 3
|
||||
MAX_CHARS = 15000
|
||||
|
||||
@app.post("/extract-text/")
|
||||
async def extract_text(file: UploadFile = File(...)):
|
||||
content = await file.read()
|
||||
|
||||
filename = file.filename.lower()
|
||||
|
||||
if filename.endswith(".pdf"):
|
||||
text = extract_pdf(content)
|
||||
elif filename.endswith(".docx"):
|
||||
text = extract_docx(content)
|
||||
else:
|
||||
return {"error": "Formato no soportado"}
|
||||
|
||||
return {"text": clean_text(text)}
|
||||
|
||||
|
||||
# ✅ PDF (eficiente en memoria)
|
||||
def extract_pdf(content):
|
||||
text = ""
|
||||
|
||||
with pdfplumber.open(io.BytesIO(content)) as pdf:
|
||||
pages = min(len(pdf.pages), MAX_PAGES)
|
||||
|
||||
for i in range(pages):
|
||||
page = pdf.pages[i]
|
||||
extracted = page.extract_text()
|
||||
|
||||
if extracted:
|
||||
text += extracted + "\n"
|
||||
|
||||
# 🔥 cortar si ya es muy grande
|
||||
if len(text) > MAX_CHARS:
|
||||
break
|
||||
|
||||
return text[:MAX_CHARS]
|
||||
|
||||
|
||||
# ✅ DOCX
|
||||
def extract_docx(content):
|
||||
doc = docx.Document(io.BytesIO(content))
|
||||
|
||||
text = "\n".join([p.text for p in doc.paragraphs])
|
||||
|
||||
return text[:MAX_CHARS]
|
||||
|
||||
|
||||
# ✅ Limpieza estándar
|
||||
def clean_text(text):
|
||||
text = re.sub(r'\n+', '\n', text)
|
||||
text = re.sub(r'\s+', ' ', text)
|
||||
return text.strip()
|
||||
382
n8n/CV Parser Completo - Experiencia + Estudios + Skills.json
Normal file
382
n8n/CV Parser Completo - Experiencia + Estudios + Skills.json
Normal file
@@ -0,0 +1,382 @@
|
||||
{
|
||||
"name": "CV Parser Completo - Experiencia + Estudios + Skills",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "upload-cv-completo",
|
||||
"responseMode": "lastNode",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [
|
||||
-1824,
|
||||
0
|
||||
],
|
||||
"id": "a4f36329-15d7-42f8-855e-41aadc252dcc",
|
||||
"name": "Upload CV",
|
||||
"webhookId": "7563f4a0-2bd3-4d24-8647-d8da39ec086f"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://10.12.152.223:8000/extract-text/",
|
||||
"sendBody": true,
|
||||
"contentType": "multipart-form-data",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"parameterType": "formBinaryData",
|
||||
"name": "file",
|
||||
"inputDataFieldName": "data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.3,
|
||||
"position": [
|
||||
-1552,
|
||||
0
|
||||
],
|
||||
"id": "61c685c9-312f-4404-9dc3-9976f374ce07",
|
||||
"name": "Extract Text Python"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Extrae la siguiente información del texto del CV y devuelve ÚNICAMENTE un JSON válido sin formato adicional.\n\nREGLAS ESTRICTAS:\n- NO uses ```, ```json, ni ningún bloque de código\n- NO agregues texto antes ni después\n- Responde únicamente con el JSON, nada más\n\nTexto:\n{{ $json.text }}\n\nEstructura EXACTA:\n{\n \"nombre_completo\": string | null,\n \"perfil\": string | null,\n \"teléfono\": string | null,\n \"email\": string | null,\n \"habilidades\": string | null,\n \"experiencia_laboral\": string | null,\n \"linkedln\": string | null,\n \"estudios\": string | null,\n \"nombres\": string | null,\n \"apellidos\": string | null\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-1360,
|
||||
0
|
||||
],
|
||||
"id": "cadd3f39-a648-4b95-9575-df73b7d64dbf",
|
||||
"name": "Extraction Ollama",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nif (typeof raw === 'object') {\n raw = JSON.stringify(raw);\n}\nraw = raw.trim();\nraw = raw.replace(/```json\\s*/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nreturn [{ json: { output: parsed } }];\n} catch (e) {\nreturn [{ json: { output: { nombre_completo: null, perfil: null, teléfono: null, email: null, habilidades: null, experiencia_laboral: null, linkedln: null, estudios: null, nombres: null, apellidos: null } } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-1072,
|
||||
0
|
||||
],
|
||||
"id": "2b10d2fd-6c72-48b7-89e2-dddc4b591af4",
|
||||
"name": "Clean JSON Output"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Convierte la siguiente experiencia laboral en JSON estructurado.\n\nIMPORTANTE:\n- TODAS las experiencias deben ir DENTRO del array experiencia_laboral\n- NO devuelvas objetos fuera de ese array\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existe un dato usa null\n- actividades debe ser un array de strings\n\nTexto:\n{{ $json.output.experiencia_laboral }}\n\nEstructura EXACTA:\n\n{\n \"experiencia_laboral\": [\n {\n \"empresa\": string,\n \"cargo\": string,\n \"fecha_inicio\": string,\n \"fecha_fin\": string,\n \"actividades\": [string]\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-928,
|
||||
-128
|
||||
],
|
||||
"id": "14778ed3-2451-4cf6-91a2-3de749fed119",
|
||||
"name": "Experiencia LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.experiencia_laboral)) {\n parsed.experiencia_laboral = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { experiencia_laboral: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-608,
|
||||
-128
|
||||
],
|
||||
"id": "75c2ef39-6af7-4a9b-a952-cbac82530889",
|
||||
"name": "Parse Experiencia"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Convierte el siguiente texto en estudios estructurados.\n\nIMPORTANTE:\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existe un dato usa null\n- TODOS los estudios deben ir dentro del array titulos\n\nTexto:\n{{ $json.output.estudios }}\n\nEstructura EXACTA:\n\n{\n \"titulos\": [\n {\n \"nivel_educativo\": string,\n \"titulo\": string,\n \"fecha_culminacion\": string\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-928,
|
||||
16
|
||||
],
|
||||
"id": "9727bce8-9e8c-4d9a-b4b8-2b07f0a0336d",
|
||||
"name": "Estudios LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.titulos)) {\n parsed.titulos = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { titulos: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-608,
|
||||
16
|
||||
],
|
||||
"id": "821e45af-1776-4ba0-bdd3-c1468bb83960",
|
||||
"name": "Parse Estudios"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Extrae las habilidades del siguiente texto como un JSON estructurado.\n\nIMPORTANTE:\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existen habilidades devuelve un array vacío\n- Todas las habilidades deben ir dentro del array skills\n\nTexto:\n{{ $json.output.habilidades }}\n\nEstructura EXACTA:\n\n{\n \"skills\": [string]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-928,
|
||||
160
|
||||
],
|
||||
"id": "cba8f163-a79a-4644-a149-9173a3314c81",
|
||||
"name": "Skills LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.skills)) {\n parsed.skills = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { skills: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-608,
|
||||
160
|
||||
],
|
||||
"id": "7b096606-2e1b-4ef0-8e47-525c68a2758c",
|
||||
"name": "Parse Skills"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const base = $('Clean JSON Output').first().json.output;\n\nlet experiencia = {};\nlet estudios = {};\nlet skills = {};\nfor (const input of $input.all()) {\n const data = input.json;\n if (data.experiencia_laboral !== undefined) {\n experiencia = data;\n } else if (data.titulos !== undefined) {\n estudios = data;\n } else if (data.skills !== undefined) {\n skills = data;\n }\n}\n\nlet nombres = base.nombres || null;\nlet apellidos = base.apellidos || null;\n\nif ((!nombres || !apellidos) && base.nombre_completo) {\n const parts = base.nombre_completo.trim().split(/\\s+/);\n if (parts.length >= 2) {\n nombres = parts.slice(0, -2).join(' ') || parts[0] || null;\n apellidos = parts.slice(-2).join(' ') || null;\n } else {\n nombres = base.nombre_completo;\n apellidos = null;\n }\n}\n\nreturn [\n {\n json: {\n aspirante: {\n nombres,\n apellidos,\n email: base.email || null,\n celular: base['teléfono'] || null,\n linkedin: base.linkedln || null,\n perfil: base.perfil || null,\n nombre_completo: base.nombre_completo || null\n },\n titulos: estudios.titulos || [],\n experiencia_laboral: experiencia.experiencia_laboral || [],\n skills: skills.skills || []\n }\n }\n];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-256,
|
||||
16
|
||||
],
|
||||
"id": "c53e8efa-2580-44e7-abce-af0637d01afc",
|
||||
"name": "Final JSON"
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Upload CV": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Text Python",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Text Python": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extraction Ollama",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extraction Ollama": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Clean JSON Output",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Clean JSON Output": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Experiencia LLM",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Estudios LLM",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Skills LLM",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Experiencia LLM": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Experiencia",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Estudios LLM": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Estudios",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Skills LLM": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Skills",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Experiencia": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Final JSON",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Estudios": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Final JSON",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Skills": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Final JSON",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "b5101a38-bc7d-42a7-bd4a-ef76a8cff507",
|
||||
"meta": {
|
||||
"instanceId": "c033503d00370ddf30aad0c1195ba31fe9ff2453e6cd31f043f20e143092248d"
|
||||
},
|
||||
"id": "g4rV6UMoouIUChNf",
|
||||
"tags": []
|
||||
}
|
||||
357
n8n/CV_Parser_Plus.json
Normal file
357
n8n/CV_Parser_Plus.json
Normal file
@@ -0,0 +1,357 @@
|
||||
{
|
||||
"name": "CV Parser Plus",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "upload-cv-completo",
|
||||
"responseMode": "lastNode",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [-1920, 0],
|
||||
"id": "webhook-plus-001",
|
||||
"name": "Upload CV",
|
||||
"webhookId": "webhook-plus-001"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://10.12.152.223:8000/extract-text/",
|
||||
"sendBody": true,
|
||||
"contentType": "multipart-form-data",
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"parameterType": "formBinaryData",
|
||||
"name": "file",
|
||||
"inputDataFieldName": "data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.3,
|
||||
"position": [-1648, 0],
|
||||
"id": "http-plus-001",
|
||||
"name": "Extract Text Python"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Extrae la siguiente información del texto del CV y devuelve ÚNICAMENTE un JSON válido sin formato adicional.\n\nREGLAS ESTRICTAS:\n- NO uses ```, ```json, ni ningún bloque de código\n- NO agregues texto antes ni después\n- Responde únicamente con el JSON, nada más\n- Para campos que no encuentres en el texto usa null\n\nTexto:\n{{ $json.text }}\n\nEstructura EXACTA:\n{\n \"nombre_completo\": string | null,\n \"nombres\": string | null,\n \"apellidos\": string | null,\n \"perfil\": string | null,\n \"teléfono\": string | null,\n \"teléfono2\": string | null,\n \"email\": string | null,\n \"tipo_identificación\": string | null,\n \"número_identificación\": string | null,\n \"fecha_nacimiento\": string | null,\n \"género\": string | null,\n \"nacionalidad\": string | null,\n \"estado_civil\": string | null,\n \"linkedln\": string | null,\n \"facebook\": string | null,\n \"twitter\": string | null,\n \"skype\": string | null,\n \"hobbies\": string | null,\n \"pais_residencia\": string | null,\n \"ciudad_residencia\": string | null,\n \"dirección\": string | null,\n \"barrio\": string | null,\n \"experiencia_laboral\": string | null,\n \"estudios\": string | null,\n \"certificaciones_cursos\": string | null,\n \"idiomas\": string | null,\n \"habilidades\": string | null\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [-1456, 0],
|
||||
"id": "ollama-extract-plus-001",
|
||||
"name": "Extraction Ollama",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nif (typeof raw === 'object') {\n raw = JSON.stringify(raw);\n}\nraw = raw.trim();\nraw = raw.replace(/```json\\s*/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nreturn [{ json: { output: parsed } }];\n} catch (e) {\nreturn [{ json: { output: { nombre_completo: null, perfil: null, teléfono: null, teléfono2: null, email: null, habilidades: null, experiencia_laboral: null, linkedln: null, estudios: null, certificaciones_cursos: null, idiomas: null, nombres: null, apellidos: null, tipo_identificación: null, número_identificación: null, fecha_nacimiento: null, género: null, nacionalidad: null, estado_civil: null, facebook: null, twitter: null, skype: null, hobbies: null, pais_residencia: null, ciudad_residencia: null, dirección: null, barrio: null } } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-1168, 0],
|
||||
"id": "clean-json-plus-001",
|
||||
"name": "Clean JSON Output"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Convierte la siguiente experiencia laboral en JSON estructurado.\n\nIMPORTANTE:\n- TODAS las experiencias deben ir DENTRO del array experiencia_laboral\n- NO devuelvas objetos fuera de ese array\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existe un dato usa null\n- actividades debe ser un array de strings\n\nTexto:\n{{ $json.output.experiencia_laboral }}\n\nEstructura EXACTA:\n\n{\n \"experiencia_laboral\": [\n {\n \"empresa\": string,\n \"cargo\": string,\n \"area\": string | null,\n \"fecha_inicio\": string,\n \"fecha_fin\": string | null,\n \"actividades\": [string],\n \"telefono_contacto\": string | null\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [-880, -256],
|
||||
"id": "ollama-exp-plus-001",
|
||||
"name": "Experiencia LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.experiencia_laboral)) {\n parsed.experiencia_laboral = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { experiencia_laboral: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-560, -256],
|
||||
"id": "parse-exp-plus-001",
|
||||
"name": "Parse Experiencia"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Convierte los siguientes estudios académicos en JSON estructurado.\nSOLO estudios formales (bachiller, técnico, tecnólogo, universitario, especialización, maestría, doctorado).\nNO incluyas cursos, seminarios, certificaciones, diplomados ni capacitaciones cortas.\n\nIMPORTANTE:\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existe un dato usa null\n- TODOS los estudios deben ir dentro del array formacion_academica\n- estado puede ser: Finalizado, En curso, Trunco, null\n\nTexto:\n{{ $json.output.estudios }}\n\nEstructura EXACTA:\n\n{\n \"formacion_academica\": [\n {\n \"nivel_educativo\": string,\n \"titulo\": string,\n \"institucion\": string | null,\n \"fecha_inicio\": string | null,\n \"fecha_fin\": string | null,\n \"estado\": string | null\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [-880, -64],
|
||||
"id": "ollama-formacion-plus-001",
|
||||
"name": "Formacion Academica LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.formacion_academica)) {\n parsed.formacion_academica = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { formacion_academica: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-560, -64],
|
||||
"id": "parse-formacion-plus-001",
|
||||
"name": "Parse Formacion Academica"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Convierte las siguientes certificaciones, cursos y capacitaciones en JSON estructurado.\nSOLO cursos, seminarios, certificaciones, diplomados y capacitaciones cortas.\nNO incluyas estudios formales (bachiller, técnico, tecnólogo, universitario, etc.).\n\nIMPORTANTE:\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existe un dato usa null\n- TODAS las certificaciones deben ir dentro del array certificaciones\n- tipo puede ser: Curso, Seminario, Certificación, Diplomado, Capacitación, Otro\n\nTexto:\n{{ $json.output.certificaciones_cursos }}\n\nEstructura EXACTA:\n\n{\n \"certificaciones\": [\n {\n \"nombre\": string,\n \"entidad\": string | null,\n \"fecha\": string | null,\n \"tipo\": string | null\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [-880, 128],
|
||||
"id": "ollama-cert-plus-001",
|
||||
"name": "Certificaciones LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.certificaciones)) {\n parsed.certificaciones = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { certificaciones: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-560, 128],
|
||||
"id": "parse-cert-plus-001",
|
||||
"name": "Parse Certificaciones"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Convierte los siguientes idiomas en JSON estructurado.\n\nIMPORTANTE:\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existe un dato usa null\n- TODOS los idiomas deben ir dentro del array idiomas\n- Los niveles pueden ser: Bajo, Medio, Alto, Nativo o null\n\nTexto:\n{{ $json.output.idiomas }}\n\nEstructura EXACTA:\n\n{\n \"idiomas\": [\n {\n \"idioma\": string,\n \"nivel_habla\": string | null,\n \"nivel_lectura\": string | null,\n \"nivel_escritura\": string | null,\n \"nivel_escucha\": string | null\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [-880, 320],
|
||||
"id": "ollama-idiomas-plus-001",
|
||||
"name": "Idiomas LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.idiomas)) {\n parsed.idiomas = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { idiomas: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-560, 320],
|
||||
"id": "parse-idiomas-plus-001",
|
||||
"name": "Parse Idiomas"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"value": "gemma4:e4b",
|
||||
"mode": "list",
|
||||
"cachedResultName": "gemma4:e4b"
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Extrae las habilidades del siguiente texto como un JSON estructurado.\n\nIMPORTANTE:\n- Debe existir UN SOLO objeto raíz\n- NO inventes información\n- Si no existen habilidades devuelve un array vacío\n- Todas las habilidades deben ir dentro del array skills\n\nTexto:\n{{ $json.output.habilidades }}\n\nEstructura EXACTA:\n\n{\n \"skills\": [string]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"system": "Eres un sistema que devuelve JSON válido.\n\nReglas OBLIGATORIAS:\n- Devuelve SOLO un objeto JSON\n- El JSON debe tener UN SOLO objeto raíz\n- NO uses markdown\n- NO expliques nada\n- NO agregues texto antes ni después"
|
||||
}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.ollama",
|
||||
"typeVersion": 1,
|
||||
"position": [-880, 512],
|
||||
"id": "ollama-skills-plus-001",
|
||||
"name": "Skills LLM",
|
||||
"credentials": {
|
||||
"ollamaApi": {
|
||||
"id": "sh3bLzH2GiM17GoH",
|
||||
"name": "Ollama account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "try {\nlet raw = $input.first().json.content || '';\nraw = raw.trim();\nraw = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nconst firstBrace = raw.indexOf('{');\nif (firstBrace === -1) throw new Error();\nlet depth = 0;\nlet inString = false;\nlet escaped = false;\nlet end = -1;\nfor (let i = firstBrace; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escaped) { escaped = false; }\n else if (c === '\\\\') { escaped = true; }\n else if (c === '\"') { inString = false; }\n } else {\n if (c === '\"') { inString = true; }\n else if (c === '{') { depth++; }\n else if (c === '}') {\n depth--;\n if (depth === 0) { end = i + 1; break; }\n }\n }\n}\nif (end === -1) {\n raw = raw.slice(firstBrace) + '}'.repeat(Math.max(0, depth));\n} else {\n raw = raw.slice(firstBrace, end);\n}\nconst parsed = JSON.parse(raw);\nif (!Array.isArray(parsed.skills)) {\n parsed.skills = [];\n}\nreturn [{ json: parsed }];\n} catch (e) {\nreturn [{ json: { skills: [] } }];\n}"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-560, 512],
|
||||
"id": "parse-skills-plus-001",
|
||||
"name": "Parse Skills"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const base = $('Clean JSON Output').first().json.output;\n\nlet experiencia = {};\nlet formacion = {};\nlet certificaciones = {};\nlet idiomas = {};\nlet skills = {};\n\nfor (const input of $input.all()) {\n const data = input.json;\n if (data.experiencia_laboral !== undefined) {\n experiencia = data;\n } else if (data.formacion_academica !== undefined) {\n formacion = data;\n } else if (data.certificaciones !== undefined) {\n certificaciones = data;\n } else if (data.idiomas !== undefined) {\n idiomas = data;\n } else if (data.skills !== undefined) {\n skills = data;\n }\n}\n\nlet nombres = base.nombres || null;\nlet apellidos = base.apellidos || null;\n\nif ((!nombres || !apellidos) && base.nombre_completo) {\n const parts = base.nombre_completo.trim().split(/\\s+/);\n if (parts.length >= 2) {\n nombres = parts.slice(0, -2).join(' ') || parts[0] || null;\n apellidos = parts.slice(-2).join(' ') || null;\n } else {\n nombres = base.nombre_completo;\n apellidos = null;\n }\n}\n\nreturn [{ json: { status: \"OK\" } }];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-256, 0],
|
||||
"id": "final-json-plus-001",
|
||||
"name": "Final JSON"
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Upload CV": {
|
||||
"main": [[{ "node": "Extract Text Python", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Extract Text Python": {
|
||||
"main": [[{ "node": "Extraction Ollama", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Extraction Ollama": {
|
||||
"main": [[{ "node": "Clean JSON Output", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Clean JSON Output": {
|
||||
"main": [[
|
||||
{ "node": "Experiencia LLM", "type": "main", "index": 0 },
|
||||
{ "node": "Formacion Academica LLM", "type": "main", "index": 0 },
|
||||
{ "node": "Certificaciones LLM", "type": "main", "index": 0 },
|
||||
{ "node": "Idiomas LLM", "type": "main", "index": 0 },
|
||||
{ "node": "Skills LLM", "type": "main", "index": 0 }
|
||||
]]
|
||||
},
|
||||
"Experiencia LLM": {
|
||||
"main": [[{ "node": "Parse Experiencia", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Formacion Academica LLM": {
|
||||
"main": [[{ "node": "Parse Formacion Academica", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Certificaciones LLM": {
|
||||
"main": [[{ "node": "Parse Certificaciones", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Idiomas LLM": {
|
||||
"main": [[{ "node": "Parse Idiomas", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Skills LLM": {
|
||||
"main": [[{ "node": "Parse Skills", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Experiencia": {
|
||||
"main": [[{ "node": "Final JSON", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Formacion Academica": {
|
||||
"main": [[{ "node": "Final JSON", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Certificaciones": {
|
||||
"main": [[{ "node": "Final JSON", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Idiomas": {
|
||||
"main": [[{ "node": "Final JSON", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Skills": {
|
||||
"main": [[{ "node": "Final JSON", "type": "main", "index": 0 }]]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": { "executionOrder": "v1" },
|
||||
"versionId": "plus-v1-001",
|
||||
"meta": {
|
||||
"instanceId": "c033503d00370ddf30aad0c1195ba31fe9ff2453e6cd31f043f20e143092248d"
|
||||
},
|
||||
"id": "flow-plus-001",
|
||||
"tags": []
|
||||
}
|
||||
Reference in New Issue
Block a user