357 lines
11 KiB
JSON
357 lines
11 KiB
JSON
{
|
|
"updatedAt": "2026-05-07T00:48:49.355Z",
|
|
"createdAt": "2026-05-07T00:48:49.355Z",
|
|
"id": "4YKnRQ9rOiQCeYEU",
|
|
"name": "Paperless \u2192 RAG v1.0",
|
|
"description": null,
|
|
"active": true,
|
|
"isArchived": false,
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "paperless/rag-ingest",
|
|
"options": {}
|
|
},
|
|
"id": "a86d7025-bbf0-4c1b-a59c-ad254ddc913e",
|
|
"name": "Paperless RAG Webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
0,
|
|
0
|
|
],
|
|
"webhookId": "f2eccecb-9935-425b-be6a-f57fe4473f34"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const data = $input.first().json;\nconst body = data.body || data;\n\nlet docId = body.document_id || body.id || body.doc_id || body.pk;\n\nif (!docId || typeof docId === 'string' && docId.includes('{')) {\n docId = null;\n}\n\nreturn [{ json: { docId } }];"
|
|
},
|
|
"id": "8f6af849-bdd9-466e-af81-84d057fd7ca1",
|
|
"name": "Extract Document ID",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
224,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"url": "=https://paperless.paccoco.com/api/documents/{{ $json.docId ? $json.docId + '/' : '?ordering=-added&page_size=1' }}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Authorization",
|
|
"value": "Token 4fb34ba35b4ab76a0715e8c56faf00a31de4fb5d"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "0b53d951-40cd-4ef0-ab81-3dea73f8b1d7",
|
|
"name": "Fetch Document",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
448,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const response = $input.first().json;\nconst doc = response.results ? response.results[0] : response;\n\nif (!doc || !doc.id) {\n throw new Error('No document found in response');\n}\n\nconst content = doc.content || '';\nconst title = doc.title || 'Untitled';\nconst correspondent = doc.correspondent_name || 'Unknown';\nconst created = doc.created || new Date().toISOString();\nconst docType = doc.document_type_name || 'unknown';\nconst tags = (doc.tags || []).join(', ');\n\n// Build rich text for RAG with metadata context\nconst ragText = `Document: ${title}\\nCorrespondent: ${correspondent}\\nType: ${docType}\\nTags: ${tags}\\nDate: ${created}\\n\\n${content}`;\n\nreturn [{\n json: {\n documentId: doc.id,\n title,\n ragText,\n source: `paperless-doc-${doc.id}`,\n metadata: {\n paperless_id: doc.id,\n title,\n correspondent,\n document_type: docType,\n created\n }\n }\n}];"
|
|
},
|
|
"id": "e3d506bc-1677-4825-b3d3-f6187156da22",
|
|
"name": "Prepare RAG Text",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
672,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "// Split text into chunks for embedding\nconst input = $input.first().json;\nconst text = input.ragText || '';\nconst source = input.source;\nconst metadata = input.metadata;\n\nconst CHUNK_SIZE = 500;\nconst OVERLAP = 50;\n\nconst chunks = [];\nconst sentences = text.split(/(?<=[.!?])\\s+/);\nlet current = '';\n\nfor (const sentence of sentences) {\n if ((current + ' ' + sentence).length > CHUNK_SIZE && current.length > 0) {\n chunks.push(current.trim());\n const words = current.split(' ');\n current = words.slice(-OVERLAP).join(' ') + ' ' + sentence;\n } else {\n current = current ? current + ' ' + sentence : sentence;\n }\n}\nif (current.trim()) chunks.push(current.trim());\n\nreturn chunks.map((chunk, i) => ({\n json: {\n chunk,\n chunkIndex: i,\n totalChunks: chunks.length,\n source,\n metadata\n }\n}));"
|
|
},
|
|
"id": "9a523501-3adb-41a3-b963-b6bb5adb25fe",
|
|
"name": "Chunk Text",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
880,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={\n \"model\": \"nomic-embed-text\",\n \"input\": \"{{ $json.chunk }}\"\n}",
|
|
"options": {
|
|
"batching": {
|
|
"batch": {
|
|
"batchSize": 5,
|
|
"batchInterval": 500
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"id": "0b631ea4-b11f-4533-960f-f5161ac798f3",
|
|
"name": "Get Embedding",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
1104,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const embeddingResponse = $input.first().json;\nconst chunkData = $('Chunk Text').item;\nconst embedding = embeddingResponse.data?.[0]?.embedding || [];\n\nconst pointId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);\n});\n\nreturn [{\n json: {\n id: pointId,\n vector: embedding,\n payload: {\n text: chunkData.json.chunk,\n source: chunkData.json.source,\n chunkIndex: chunkData.json.chunkIndex,\n totalChunks: chunkData.json.totalChunks,\n metadata: chunkData.json.metadata,\n ingested_at: new Date().toISOString()\n }\n }\n}];"
|
|
},
|
|
"id": "ad9dc677-e3a2-464e-a82e-2aa4d8b9b042",
|
|
"name": "Prepare Qdrant Point",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
1328,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "PUT",
|
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={\n \"points\": [\n {\n \"id\": \"{{ $json.id }}\",\n \"vector\": {{ JSON.stringify($json.vector) }},\n \"payload\": {{ JSON.stringify($json.payload) }}\n }\n ]\n}",
|
|
"options": {}
|
|
},
|
|
"id": "e977b536-d470-4da0-a970-794a858e8aa9",
|
|
"name": "Upsert to Qdrant",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
1552,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const ragText = $('Prepare RAG Text').first().json;\nconst chunks = $('Chunk Text').first().json;\nreturn [{\n json: {\n title: '\ud83d\udcda Document Added to RAG',\n message: `${ragText.title} has been chunked and ingested into the knowledge base (${chunks.totalChunks || '?'} chunks)`,\n priority: 3\n }\n}];"
|
|
},
|
|
"id": "fmt05-0001-0000-0000-000000000005",
|
|
"name": "Format Gotify Message",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
1520,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"message": "={{ $json.message }}",
|
|
"additionalFields": {
|
|
"priority": "={{ $json.priority }}",
|
|
"title": "={{ $json.title }}"
|
|
},
|
|
"options": {
|
|
"contentType": "text/plain"
|
|
}
|
|
},
|
|
"id": "ccdee208-b8f8-4ad4-a20c-94c4735d2e3d",
|
|
"name": "Notify via Gotify",
|
|
"type": "n8n-nodes-base.gotify",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
1760,
|
|
0
|
|
],
|
|
"credentials": {
|
|
"gotifyApi": {
|
|
"id": "SETUP_REQUIRED",
|
|
"name": "Paperless RAG"
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"connections": {
|
|
"Paperless RAG Webhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Extract Document ID",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Extract Document ID": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Fetch Document",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Fetch Document": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Prepare RAG Text",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Prepare RAG Text": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Chunk Text",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Chunk Text": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Get Embedding",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Get Embedding": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Prepare Qdrant Point",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Prepare Qdrant Point": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Upsert to Qdrant",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Upsert to Qdrant": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Format Gotify Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Format Gotify Message": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Notify via Gotify",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"binaryMode": "separate"
|
|
},
|
|
"staticData": null,
|
|
"meta": null,
|
|
"pinData": {},
|
|
"versionId": "3c20b423-8708-4540-8bf9-8db4880335a5",
|
|
"activeVersionId": "3c20b423-8708-4540-8bf9-8db4880335a5",
|
|
"versionCounter": 12,
|
|
"triggerCount": 1,
|
|
"tags": [
|
|
{
|
|
"updatedAt": "2026-05-06T22:39:46.476Z",
|
|
"createdAt": "2026-05-06T22:39:46.476Z",
|
|
"id": "NxHjuO4MMd5kdPR7",
|
|
"name": "paperless"
|
|
},
|
|
{
|
|
"updatedAt": "2026-05-06T22:39:33.284Z",
|
|
"createdAt": "2026-05-06T22:39:33.284Z",
|
|
"id": "7uuEyQrIlcuruZBk",
|
|
"name": "rag"
|
|
},
|
|
{
|
|
"updatedAt": "2026-05-06T22:38:51.678Z",
|
|
"createdAt": "2026-05-06T22:38:51.678Z",
|
|
"id": "S9FxzVfHLsHmyzyt",
|
|
"name": "ai"
|
|
}
|
|
],
|
|
"shared": [
|
|
{
|
|
"updatedAt": "2026-05-07T00:48:49.355Z",
|
|
"createdAt": "2026-05-07T00:48:49.355Z",
|
|
"role": "workflow:owner",
|
|
"workflowId": "4YKnRQ9rOiQCeYEU",
|
|
"projectId": "dxCRnBdX5uJizCGa",
|
|
"project": {
|
|
"updatedAt": "2026-05-06T01:10:37.484Z",
|
|
"createdAt": "2026-05-06T01:08:07.721Z",
|
|
"id": "dxCRnBdX5uJizCGa",
|
|
"name": "Wilfred Fizzlepoof <admin@paccoco.com>",
|
|
"type": "personal",
|
|
"icon": null,
|
|
"description": null,
|
|
"creatorId": "47b2227f-2fc2-4a08-bd35-ea157b92df0d"
|
|
}
|
|
}
|
|
],
|
|
"versionMetadata": {
|
|
"name": "Version 3c20b423",
|
|
"description": ""
|
|
}
|
|
} |