feat(flows):allineamento filtri

This commit is contained in:
2026-03-21 20:30:54 +00:00
parent 8394861bb3
commit 734a1a9440
14 changed files with 4186 additions and 144 deletions

View File

@@ -51,7 +51,7 @@
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1952,
-1696,
0
]
},
@@ -66,7 +66,7 @@
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-1696,
-1440,
0
],
"webhookId": "ccbdaca8-c592-4c22-88a3-0e18b8272a26",
@@ -110,8 +110,8 @@
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
-1696,
464
-1456,
512
]
},
{
@@ -411,8 +411,8 @@
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2032,
288
2048,
-16
],
"webhookId": "60fefda0-215b-49e8-86ae-15ef4b03d89f",
"credentials": {
@@ -431,8 +431,8 @@
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2032,
-64
2048,
304
]
},
{
@@ -469,8 +469,8 @@
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2288,
-64
2304,
304
],
"credentials": {
"httpHeaderAuth": {
@@ -488,8 +488,8 @@
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2512,
-64
2528,
304
]
},
{
@@ -501,8 +501,8 @@
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
2736,
-64
2752,
304
],
"webhookId": "wait-pl-multi"
},
@@ -527,8 +527,8 @@
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2960,
-64
2976,
304
],
"credentials": {
"httpHeaderAuth": {
@@ -546,8 +546,8 @@
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3184,
-64
3200,
304
]
},
{
@@ -566,8 +566,8 @@
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3392,
-64
3408,
304
],
"credentials": {
"httpHeaderAuth": {
@@ -589,8 +589,8 @@
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
3616,
-64
3632,
304
],
"webhookId": "3be53ead-f628-4ec4-99d4-f378224c57ed",
"credentials": {
@@ -611,8 +611,8 @@
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3840,
-64
3856,
304
]
},
{
@@ -626,9 +626,43 @@
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
4064,
-64
4080,
304
]
},
{
"id": "n_memoria_code",
"name": "🧠 Salva in Memoria",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3200,
400
],
"parameters": {
"jsCode": "// ─── 🧠 Salva in Memoria: memory_facts + Qdrant knowledge ────────────────────\n// Eseguito in parallelo dopo Paperless - Patch Metadati\n// Non bloccante: errori vengono loggati ma non interrompono il flusso\n\nconst OLLAMA_URL = 'http://ollama.ai.svc.cluster.local:11434';\nconst QDRANT_URL = 'http://qdrant.persistence.svc.cluster.local:6333';\nconst QDRANT_KEY = '__Montecarlo00!';\nconst COLLECTION = 'knowledge';\n\nconst parsed = $('Parse Risposta GPT').first().json;\nconst docData = $('Estrai Document ID').first().json;\nconst ocrText = ($('FileWizard - Stato OCR').first().json.result_preview || '').substring(0, 3000);\nconst normIn = $('Normalizza Input').first().json;\n\nconst docId = docData.doc_id;\nconst title = parsed.title || docData.title || normIn.filename || 'Documento';\nconst sourceRef = `paperless-${docId}`;\n\n// TTL by doc type\nconst dtype = (parsed.document_type_name || '').toLowerCase();\nlet ttlDays = 365;\nif (dtype.includes('bolletta') || dtype.includes('fattura')) ttlDays = 180;\nelse if (dtype.includes('cedolino')) ttlDays = 730;\nelse if (dtype.includes('ricevuta')) ttlDays = 90;\nconst expiresAt = new Date(Date.now() + ttlDays * 86400000).toISOString();\n\n// Text to embed: title + OCR excerpt\nconst embedText = `${title}\\n\\n${ocrText}`.substring(0, 4000);\n\n// ── 1. Embedding via Ollama ────────────────────────────────────────────────\nlet embedding = null;\ntry {\n const embedRes = await this.helpers.httpRequest({\n method: 'POST',\n url: `${OLLAMA_URL}/api/embed`,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ model: 'nomic-embed-text', input: embedText })\n });\n embedding = embedRes.embeddings?.[0] || null;\n} catch(e) {\n console.error('Ollama embed error:', e.message);\n}\n\n// ── 2. Qdrant upsert ───────────────────────────────────────────────────────\nlet qdrantId = null;\nif (embedding && embedding.length === 768) {\n const { v4: uuidv4 } = require('uuid');\n qdrantId = uuidv4();\n try {\n await this.helpers.httpRequest({\n method: 'PUT',\n url: `${QDRANT_URL}/collections/${COLLECTION}/points`,\n headers: { 'content-type': 'application/json', 'api-key': QDRANT_KEY },\n body: JSON.stringify({ points: [{\n id: qdrantId,\n vector: embedding,\n payload: {\n user_id: 'martin',\n source: 'paperless',\n source_ref: sourceRef,\n doc_id: docId,\n title,\n category: 'finance',\n doc_type: parsed.document_type_name || '',\n correspondent: parsed.correspondent_name || '',\n created_date: parsed.created_date || '',\n tags: Array.isArray(parsed.tag_ids) ? parsed.tag_ids : [],\n date: new Date().toISOString(),\n action_required: false\n }\n }]})\n });\n } catch(e) {\n console.error('Qdrant upsert error:', e.message);\n qdrantId = null;\n }\n}\n\n// ── 3. Prepare for Postgres node ──────────────────────────────────────────\nconst pompeoNote = `Documento \"${title}\" archiviato su Paperless (doc_id=${docId}). ` +\n `Tipo: ${parsed.document_type_name || 'N/D'}. ` +\n `Corrispondente: ${parsed.correspondent_name || 'N/D'}. ` +\n `Fonte: ${normIn._source === 'email' ? 'email da ' + (normIn.from || '?') : 'Telegram'}.`;\n\nconst detail = {\n paperless_doc_id: docId,\n doc_type: parsed.document_type_name || '',\n correspondent: parsed.correspondent_name || '',\n created_date: parsed.created_date || '',\n tag_ids: Array.isArray(parsed.tag_ids) ? parsed.tag_ids : [],\n source: normIn._source,\n is_duplicate: docData.is_duplicate || false,\n qdrant_embedded: !!qdrantId\n};\n\nreturn [{ json: {\n source_ref: sourceRef,\n category: 'finance',\n subject: title.substring(0, 200).replace(/'/g, \"''\"),\n detail: JSON.stringify(detail),\n action_required: false,\n action_text: '',\n pompeo_note: pompeoNote.substring(0, 500).replace(/'/g, \"''\"),\n entity_refs: JSON.stringify({ people: [], places: [], products: [], amounts: [] }),\n expires_at: expiresAt,\n qdrant_id: qdrantId\n}}];"
}
},
{
"id": "n_pg_memoria",
"name": "💾 Upsert Memoria",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
3420,
400
],
"credentials": {
"postgres": {
"id": "mRqzxhSboGscolqI",
"name": "Pompeo — PostgreSQL"
}
},
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO memory_facts\n (user_id, source, source_ref, category, subject, detail, action_required, action_text, pompeo_note, entity_refs, expires_at)\nVALUES (\n 'martin',\n 'email',\n '{{ $json.thread_id }}',\n '{{ $json.category }}',\n '{{ $json.subject }}',\n '{{ $json.detail }}',\n {{ $json.action_required }},\n '{{ $json.action_text }}',\n '{{ $json.pompeo_note }}',\n '{{ $json.entity_refs }}'::jsonb,\n {{ $json.expires_at ? \"'\" + $json.expires_at + \"'::timestamptz\" : \"NULL\" }}\n)\nON CONFLICT (user_id, source, source_ref) WHERE source_ref IS NOT NULL DO UPDATE SET\n subject = EXCLUDED.subject,\n detail = EXCLUDED.detail,\n action_required = EXCLUDED.action_required,\n action_text = EXCLUDED.action_text,\n pompeo_note = EXCLUDED.pompeo_note,\n entity_refs = EXCLUDED.entity_refs,\n expires_at = EXCLUDED.expires_at",
"options": {}
}
}
],
"connections": {
@@ -926,6 +960,11 @@
"node": "Telegram - Conferma Upload",
"type": "main",
"index": 0
},
{
"node": "🧠 Salva in Memoria",
"type": "main",
"index": 0
}
]
]
@@ -951,6 +990,17 @@
}
]
]
},
"🧠 Salva in Memoria": {
"main": [
[
{
"node": "💾 Upsert Memoria",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
@@ -959,7 +1009,7 @@
"availableInMCP": false
},
"triggerCount": 2,
"versionId": "5865a3df-1ea4-4983-a60e-cbc8997b89f8",
"versionId": "f0e2579d-f423-4465-a144-9f03572ba94e",
"owner": {
"type": "personal",
"projectId": "Hdttz401OqqtObPo",