395 lines
12 KiB
JSON
395 lines
12 KiB
JSON
{
|
|
"updatedAt": "2026-05-06T23:25:33.293Z",
|
|
"createdAt": "2026-05-06T23:25:29.005Z",
|
|
"id": "gYyOggC4J98keLbj",
|
|
"name": "RAG Pipeline v1.0",
|
|
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
|
"active": false,
|
|
"isArchived": false,
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "rag/ingest",
|
|
"responseMode": "lastNode",
|
|
"responseData": "allEntries",
|
|
"options": {}
|
|
},
|
|
"id": "31ce8413-d15c-490f-92d1-956ef214e9a3",
|
|
"name": "Ingest Webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
0,
|
|
0
|
|
],
|
|
"webhookId": "411ad4e3-eb9f-49e2-97c4-2f30e01dd609"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "// Split incoming text into chunks for embedding\nconst input = $input.first().json.body || $input.first().json;\nconst text = input.text || input.content || '';\nconst source = input.source || 'manual';\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 // Keep overlap\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": "5d5aa0b1-c84b-48f6-9b5e-618cb05e5e61",
|
|
"name": "Chunk Text",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
224,
|
|
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": "89c692c6-b987-409c-b5c1-3533097d06e3",
|
|
"name": "Get Embedding",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
448,
|
|
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 => { const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); });\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": "fec59066-958f-4b96-8224-6f4e48ad5abb",
|
|
"name": "Prepare Qdrant Point",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
672,
|
|
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": "9404c8fe-46ae-4d8a-bf35-35ecba4939c0",
|
|
"name": "Upsert to Qdrant",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
880,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "rag/query",
|
|
"responseMode": "lastNode",
|
|
"responseData": "allEntries",
|
|
"options": {}
|
|
},
|
|
"id": "1a5c921c-0351-4a5e-a3e4-0aab71313af1",
|
|
"name": "Query Webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
0,
|
|
304
|
|
],
|
|
"webhookId": "aad4fe04-74b0-4136-80e9-380a7e4db1c2"
|
|
},
|
|
{
|
|
"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.body.query || $json.query }}\"\n}",
|
|
"options": {}
|
|
},
|
|
"id": "0a294ba5-a890-4dfa-8ae7-ba25cd0a4534",
|
|
"name": "Embed Query",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
224,
|
|
304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points/search",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={\n \"vector\": {{ JSON.stringify($json.data[0].embedding) }},\n \"limit\": 5,\n \"with_payload\": true\n}",
|
|
"options": {}
|
|
},
|
|
"id": "2cb68891-c57e-4006-b3ac-c290aec28220",
|
|
"name": "Search Qdrant",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
448,
|
|
304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const searchResults = $input.first().json.result || [];\nconst query = $('Query Webhook').first().json.body?.query || $('Query Webhook').first().json.query || '';\n\nconst context = searchResults.map((r, i) => \n `[${i+1}] (score: ${r.score.toFixed(3)}) ${r.payload.text}`\n).join('\\n\\n');\n\nreturn [{\n json: {\n query,\n context,\n sources: searchResults.map(r => ({\n source: r.payload.source,\n score: r.score,\n text_preview: r.payload.text.substring(0, 100) + '...'\n }))\n }\n}];"
|
|
},
|
|
"id": "d32ee814-81d0-4ea6-8f28-59632d09b1b9",
|
|
"name": "Build Context",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
672,
|
|
304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "={{'Bearer ' + $env.LITELLM_API_KEY}}"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={\n \"model\": \"medium\",\n \"messages\": [\n {\n \"role\": \"system\",\n \"content\": \"You are a helpful knowledge base assistant. Answer the user's question using ONLY the provided context. If the context doesn't contain enough information, say so. Cite which source numbers you used.\"\n },\n {\n \"role\": \"user\",\n \"content\": \"Context:\\n{{ $json.context }}\\n\\nQuestion: {{ $json.query }}\"\n }\n ],\n \"max_tokens\": 500,\n \"temperature\": 0.2\n}",
|
|
"options": {}
|
|
},
|
|
"id": "e1360454-8457-40ce-b179-670c182da59e",
|
|
"name": "LiteLLM Answer",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
880,
|
|
304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const response = $input.first().json;\nconst contextData = $('Build Context').first().json;\n\nreturn [{\n json: {\n answer: response.choices?.[0]?.message?.content || 'No answer generated',\n sources: contextData.sources,\n query: contextData.query\n }\n}];"
|
|
},
|
|
"id": "fe16d3a1-0d11-442f-b941-56fb74804601",
|
|
"name": "Format Response",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
1104,
|
|
304
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Ingest Webhook": {
|
|
"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
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Query Webhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Embed Query",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Embed Query": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Search Qdrant",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Search Qdrant": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Build Context",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Build Context": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "LiteLLM Answer",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"LiteLLM Answer": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Format Response",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"binaryMode": "separate"
|
|
},
|
|
"staticData": null,
|
|
"meta": null,
|
|
"pinData": {},
|
|
"versionId": "832248e5-106e-4fc4-8ebd-a5d0034e2b14",
|
|
"activeVersionId": "832248e5-106e-4fc4-8ebd-a5d0034e2b14",
|
|
"versionCounter": 14,
|
|
"triggerCount": 2,
|
|
"tags": [
|
|
{
|
|
"updatedAt": "2026-05-06T22:38:51.678Z",
|
|
"createdAt": "2026-05-06T22:38:51.678Z",
|
|
"id": "S9FxzVfHLsHmyzyt",
|
|
"name": "ai"
|
|
},
|
|
{
|
|
"updatedAt": "2026-05-06T22:39:33.284Z",
|
|
"createdAt": "2026-05-06T22:39:33.284Z",
|
|
"id": "7uuEyQrIlcuruZBk",
|
|
"name": "rag"
|
|
}
|
|
],
|
|
"shared": [
|
|
{
|
|
"updatedAt": "2026-05-06T23:25:29.005Z",
|
|
"createdAt": "2026-05-06T23:25:29.005Z",
|
|
"role": "workflow:owner",
|
|
"workflowId": "gYyOggC4J98keLbj",
|
|
"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 832248e5",
|
|
"description": ""
|
|
}
|
|
}
|