2026-03-21 16:59:22 +00:00
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"name": "Chat SQL DataDictionary HTTP Stable v4",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"nodes": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"multipleMethods": true,
|
|
|
|
|
|
"httpMethod": [
|
|
|
|
|
|
"POST"
|
|
|
|
|
|
],
|
|
|
|
|
|
"path": "chat",
|
|
|
|
|
|
"responseMode": "lastNode",
|
|
|
|
|
|
"responseData": "allEntries",
|
|
|
|
|
|
"options": {}
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.webhook",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
2026-06-23 23:23:23 +00:00
|
|
|
|
-900,
|
|
|
|
|
|
200
|
2026-03-21 16:59:22 +00:00
|
|
|
|
],
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "9f56ce73-26a2-4429-a77d-bfc6a91fe5d7",
|
|
|
|
|
|
"name": "Webhook"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"jsCode": "const question = $('Webhook').first().json.body?.question || '';\nreturn [{ json: { question, ollamaBody: { model: 'qwen2.5:3b', stream: false, options: { temperature: 0, top_p: 0.1, num_predict: 10 }, messages: [ { role: 'system', content: `Sen bir niyet sınıflandırma motorusun. Sadece CHAT veya SQL döndür. Kullanıcı veritabanından veri istiyorsa, listele/getir/say/toplam/raporla/sorgula/ara/filtrele gibi amaç varsa SQL döndür. Selamlaşma, açıklama, yardım, teşekkür, genel sohbet ise CHAT döndür. Açıklama yazma.` }, { role: 'user', content: question } ] } } }];"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
-680,
|
|
|
|
|
|
200
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "e19a29c3-5833-4151-bd9c-d6c4cb49d2b0",
|
|
|
|
|
|
"name": "Build Intent Request"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"method": "POST",
|
|
|
|
|
|
"url": "http://ollama:11434/api/chat",
|
|
|
|
|
|
"sendBody": true,
|
|
|
|
|
|
"specifyBody": "json",
|
|
|
|
|
|
"jsonBody": "={{ $json.ollamaBody }}",
|
|
|
|
|
|
"options": {}
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
|
|
|
|
"typeVersion": 4.2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
-470,
|
|
|
|
|
|
200
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "2fddcd65-a682-4079-bbe1-67d63873d500",
|
|
|
|
|
|
"name": "Intent Ollama"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"jsCode": "const res = $input.first().json || {};\nconst content = String(res.message?.content || res.response || res.output || '').toUpperCase();\nconst question = $('Webhook').first().json.body?.question || '';\nconst tenantName = $('Webhook').first().json.body?.tenantName || 'Sozsoft';\nconst intent = content.includes('SQL') ? 'SQL' : 'CHAT';\nreturn [{ json: { question, tenantName, intent, intentRaw: res } }];"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
-250,
|
|
|
|
|
|
200
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "adb78e6c-97b9-4a9a-8914-c2a4d935893e",
|
|
|
|
|
|
"name": "Normalize Intent"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"conditions": {
|
|
|
|
|
|
"options": {
|
|
|
|
|
|
"caseSensitive": true,
|
|
|
|
|
|
"leftValue": "",
|
|
|
|
|
|
"typeValidation": "strict",
|
|
|
|
|
|
"version": 2
|
|
|
|
|
|
},
|
|
|
|
|
|
"conditions": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "989bc03a-0479-4cee-a071-b2f991c5def5",
|
|
|
|
|
|
"leftValue": "={{ $json.intent }}",
|
|
|
|
|
|
"rightValue": "SQL",
|
|
|
|
|
|
"operator": {
|
|
|
|
|
|
"type": "string",
|
|
|
|
|
|
"operation": "equals"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"combinator": "and"
|
|
|
|
|
|
},
|
|
|
|
|
|
"options": {}
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.if",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
-40,
|
|
|
|
|
|
200
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "0221a195-1c08-47be-adab-1539dddce98b",
|
|
|
|
|
|
"name": "Is SQL?"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"jsCode": "const question = $('Webhook').first().json.body?.question || '';\nreturn [{ json: { type:'chat', question, ollamaBody: { model: 'qwen2.5:3b', stream: false, options: { temperature: 0.2, top_p: 0.8, num_predict: 512 }, messages: [ { role: 'system', content: `Sen Türkçe konuşan yardımcı bir asistansın. Kullanıcı SQL/veri istemiyorsa kısa ve net cevap ver. Veritabanı sorgusu üretme.` }, { role: 'user', content: question } ] } } }];"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"position": [
|
2026-06-23 23:23:23 +00:00
|
|
|
|
180,
|
|
|
|
|
|
420
|
2026-03-21 16:59:22 +00:00
|
|
|
|
],
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "41617000-9033-4896-b04a-d2dfb48567ba",
|
|
|
|
|
|
"name": "Build Chat Request"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"method": "POST",
|
|
|
|
|
|
"url": "http://ollama:11434/api/chat",
|
|
|
|
|
|
"sendBody": true,
|
|
|
|
|
|
"specifyBody": "json",
|
|
|
|
|
|
"jsonBody": "={{ $json.ollamaBody }}",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"options": {}
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
|
|
|
|
"typeVersion": 4.2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
390,
|
|
|
|
|
|
420
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "b2f42e30-af31-447a-8e17-dc2e01bf2d96",
|
|
|
|
|
|
"name": "Chat Ollama"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"jsCode": "const res = $input.first().json || {};\nreturn [{ json: { type: 'chat', question: $('Webhook').first().json.body?.question || '', answer: res.message?.content || res.response || res.output || '' } }];"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
610,
|
|
|
|
|
|
420
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "c9fae17b-b4d7-48df-8556-47ccc56556fb",
|
|
|
|
|
|
"name": "Chat Response"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"jsCode": "const dictionaryQuery = `\nSELECT\n STRING_AGG(\n CAST(\n N'Varlık: ' + ISNULL(EntityName, N'')\n + N' | Tablo: ' + ISNULL(TableName, N'')\n + N' | Kolon: ' + ISNULL(ColumnName, N'')\n + N' | Tip: ' + ISNULL(DataType, N'')\n + N' | Açıklama: ' + ISNULL(ColumnDescription, N'')\n + N' | Eş Anlamlılar: ' + ISNULL(Synonyms, N'')\n + CASE WHEN IsDefaultSelectColumn = 1 THEN N' | Varsayılan gösterilecek kolon' ELSE N'' END\n + CASE WHEN IsActiveColumn = 1 THEN N' | Aktiflik kolonu' ELSE N'' END\n + CASE WHEN IsDeletedColumn = 1 THEN N' | Silinmiş kayıt kolonu' ELSE N'' END\n + CASE WHEN IsSensitive = 1 THEN N' | Hassas alan' ELSE N'' END\n AS NVARCHAR(MAX)),\n CHAR(10)\n ) AS DataDictionaryPrompt\nFROM dbo.AI_DataDictionary;\n`;\nreturn [{ json: { question: $json.question, tenantName: $json.tenantName || 'Sozsoft', dictionaryQuery } }];"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
180,
|
|
|
|
|
|
40
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "f92e673a-50ac-4551-8cff-a583f39e7ec3",
|
|
|
|
|
|
"name": "Build Data Dictionary Query"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"operation": "executeQuery",
|
|
|
|
|
|
"query": "={{ $json.dictionaryQuery }}"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.microsoftSql",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"typeVersion": 1.1,
|
|
|
|
|
|
"position": [
|
2026-06-23 23:23:23 +00:00
|
|
|
|
420,
|
|
|
|
|
|
40
|
2026-03-21 16:59:22 +00:00
|
|
|
|
],
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "61640dc9-12d9-4282-9277-b4760ac8f0e9",
|
|
|
|
|
|
"name": "Get Data Dictionary Prompt"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"jsCode": "const dict = String($input.first().json.DataDictionaryPrompt || '').trim();\nconst question = $('Webhook').first().json.body?.question || '';\nconst tenantName = $('Webhook').first().json.body?.tenantName || 'Sozsoft';\n\nconst tableNames = [...new Set([...dict.matchAll(/\\|\\s*Tablo:\\s*([^|\\n]+)/g)].map(m => m[1].trim()).filter(Boolean))];\nconst activeColumns = [...new Set([...dict.matchAll(/\\|\\s*Tablo:\\s*([^|\\n]+)\\s*\\|\\s*Kolon:\\s*([^|\\n]+)[^\\n]*Aktiflik kolonu/g)].map(m => `${m[1].trim()}.${m[2].trim()}`))];\nconst deletedColumns = [...new Set([...dict.matchAll(/\\|\\s*Tablo:\\s*([^|\\n]+)\\s*\\|\\s*Kolon:\\s*([^|\\n]+)[^\\n]*Silinmiş kayıt kolonu/g)].map(m => `${m[1].trim()}.${m[2].trim()}`))];\n\nconst userContent = `Kullanıcı isteği: ${question}\ntenantName: ${tenantName}\n\nVERI_SOZLUGU_CONTEXT:\n${dict || 'AI_DataDictionary tablosu boş veya okunamadı.'}`;\n\nreturn [{\n json: {\n question,\n tenantName,\n dictionaryAvailable: !!dict,\n dictionaryText: dict,\n dictionaryPreview: dict.substring(0, 1200),\n tableNames,\n activeColumns,\n deletedColumns,\n ollamaBody: {\n model: 'qwen2.5-coder:1.5b',\n stream: false,\n options: { temperature: 0, top_p: 0.05, num_predict: 512 },\n messages: [\n {\n role: 'system',\n content: `Sen Microsoft SQL Server için çalışan T-SQL üretim motorusun.\n\nKESİN ÇIKTI KURALLARI:\nSadece SQL döndür.\nCevabın ilk kelimesi mutlaka SELECT olmalı.\nAçıklama yazma.\nMarkdown kullanma.\nKod bloğu kullanma.\nBoş cevap verme.\nSELECT * kullanma.\nSadece SELECT üret.\nINSERT, UPDATE, DELETE, MERGE, DROP, ALTER, TRUNCATE, CREATE, EXEC, EXECUTE üretme.\n\nVERİ SÖZLÜĞÜ KURALLARI:\nKullanıcının Türkçe isteğini VERI_SOZLUGU_CONTEXT içindeki Varlık, Tablo, Kolon, Açıklama ve Eş Anlamlılar bilgilerine göre yorumla.\nTablo ve kolonları sadece VERI_SOZLUGU_CONTEXT içinde geçen değerlerden seç.\nVeri sözlüğünde olmayan tablo veya kolon uydurma.\nUsers, Orders, Customers gibi genel tablo adları üretme.\nListeleme isteklerinde sadece Varsayılan gösterilecek kolon alanlarını SELECT kısmına koy.\nAktif kayıt istenirse Aktiflik kolonu = 1 filtresi ekle.\nSilinmiş kayıt kolonu varsa her zaman Silinmiş kayıt kolonu = 0 filtresi ekle.\nHassas alanları döndürme.\n\nDATABASE KURALI:\ntenantName boşsa Sozsoft kullan.\nTüm tablo referansları kesinlikle [DatabaseName].dbo.TableName formatında olmalı.\nTablo adını asla çıplak yazma. Örneğin AbpUsers yazma, [Sozsoft].dbo.AbpUsers yaz.\n\nÖRNEK:\nKullanıcı isteği: Aktif kullanıcıların listesi\nDoğru cevap:\nSELECT Id, UserName, Name, Surname, Email FROM [Sozsoft].dbo.AbpUsers WHERE IsDeleted = 0 AND IsActive = 1\n\nUYGUN TABLO BULUNAMAZSA:\nSELECT 'UYGUN_TABLO_BULUNAMADI' AS ErrorMessage`\n },\n { role: 'user', content: userContent }\n ]\n }\n }\n}];"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"position": [
|
2026-06-23 23:23:23 +00:00
|
|
|
|
660,
|
|
|
|
|
|
40
|
2026-03-21 16:59:22 +00:00
|
|
|
|
],
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "734269a4-9b8d-44ea-a879-18cb919e75e9",
|
|
|
|
|
|
"name": "Build SQL Request"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"method": "POST",
|
|
|
|
|
|
"url": "http://ollama:11434/api/chat",
|
|
|
|
|
|
"sendBody": true,
|
|
|
|
|
|
"specifyBody": "json",
|
|
|
|
|
|
"jsonBody": "={{ $json.ollamaBody }}",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"options": {}
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
|
|
|
|
"typeVersion": 4.2,
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"position": [
|
2026-06-23 23:23:23 +00:00
|
|
|
|
900,
|
|
|
|
|
|
40
|
2026-03-21 16:59:22 +00:00
|
|
|
|
],
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "a8b8e100-1a8d-4fb7-8678-86eac042b4b1",
|
|
|
|
|
|
"name": "SQL Ollama"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"jsCode": "const res = $input.first().json || {};\nlet raw = res.message?.content || res.response || res.output || res.text || res.answer || '';\nraw = String(raw || '').trim();\n\nconst question = $('Webhook').first().json.body?.question || '';\nconst tenantName = $('Webhook').first().json.body?.tenantName || 'Sozsoft';\nconst buildNode = $('Build SQL Request').first().json || {};\nconst tableNames = buildNode.tableNames || [];\nconst deletedColumns = buildNode.deletedColumns || [];\n\nfunction cleanGeneratedSql(text) {\n let t = String(text || '')\n .replace(/```sql/gi, '')\n .replace(/```/g, '')\n .replace(/^SQL\\s*:/i, '')\n .trim();\n\n const selectIndex = t.search(/\\bSELECT\\b/i);\n if (selectIndex < 0) return '';\n t = t.substring(selectIndex).trim();\n\n const secondSelectMatch = t.substring(6).search(/\\bSELECT\\b/i);\n if (secondSelectMatch >= 0) t = t.substring(0, 6 + secondSelectMatch).trim();\n\n const semicolonIndex = t.indexOf(';');\n if (semicolonIndex >= 0) t = t.substring(0, semicolonIndex).trim();\n\n const cutPatterns = [/\\n\\s*Açıklama\\s*:/i, /\\n\\s*Explanation\\s*:/i, /\\n\\s*Bu sorgu/i, /\\n\\s*Not\\s*:/i];\n for (const p of cutPatterns) {\n const m = t.search(p);\n if (m > 0) t = t.substring(0, m).trim();\n }\n return t.trim();\n}\n\nfunction escapeRegExp(s) {\n return String(s).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction qualifyTables(sql) {\n let q = sql;\n const db = tenantName || 'Sozsoft';\n const uniqueTables = [...new Set(tableNames)].sort((a, b) => b.length - a.length);\n for (const table of uniqueTables) {\n const t = escapeRegExp(table);\n q = q.replace(new RegExp(`\\\\b(FROM|JOIN)\\\\s+(?!\\\\[)(?:dbo\\\\.)?(${t})\\\\b`, 'gi'), `$1 [${db}].dbo.$2`);\n }\n return q;\n}\n\nfunction addDefaultIsDeletedFilter(sql) {\n let q = sql.trim();\n for (const item of deletedColumns) {\n const [table, col] = String(item).split('.');\n if (!table || !col) continue;\n\n const tableRegex = new RegExp(`(\\\\[${escapeRegExp(tenantName)}\\\\]\\\\.dbo\\\\.|dbo\\\\.|\\\\b)${escapeRegExp(table)}\\\\b`, 'i');\n if (!tableRegex.test(q)) continue;\n\n const whereMatch = q.match(/\\bWHERE\\b([\\s\\S]*?)(\\bGROUP\\s+BY\\b|\\bORDER\\s+BY\\b|$)/i);\n const whereText = whereMatch ? whereMatch[1] : '';\n const colInWhere = new RegExp(`\\\\b${escapeRegExp(col)}\\\\b`, 'i').test(whereText);\n if (colInWhere) continue;\n\n const condition = `${col} = 0`;\n const orderGroupMatch = q.match(/\\b(GROUP\\s+BY|ORDER\\s+BY)\\b/i);\n if (/\\bWHERE\\b/i.test(q)) {\n if (orderGroupMatch) {\n const idx = orderGroupMatch.index;\n q = q.slice(0, idx).trim() + ` AND ${condition} ` + q.slice(idx).trim();\n } else {\n q = q + ` AND ${condition}`;\n }\n } else {\n if (orderGroupMatch) {\n const idx = orderGroupMatch.index;\n q = q.slice(0, idx).trim() + ` WHERE ${condition} ` + q.slice(idx).trim();\n } else {\n q = q + ` WHERE ${condition}`;\n }\n }\n }\n return q;\n}\n\nlet sql = cleanGeneratedSql(raw);\nsql = qualifyTables(sql);\nsql = addDefaultIsDeletedFilter(sql);\nsql = sql.replace(/;\\s*$/,'').trim();\n\nif (!sql) {\n return [{ json: { valid: false, type: 'sql_error', error: 'Üretilen çıktı içinde SELECT bulunamadı.', question, generatedText: raw, ollamaRawOutput: res, dictionaryAvailable: buildNode.dictionaryAvailable, dictionaryPreview: buildNode.dictionaryPreview } }];\n}\n\nif (!/^\\s*SELECT\\b/i.test(sql)) {\n return [{ json: { valid: false, type: 'sql_error', error: 'Üretilen çıktı SELECT ile başlamıyor.', question, generatedText: raw, cleanedText: sql, ollamaRawOutput: res, dictionaryAvailable: buildNode.dictionaryAvailable, dictionaryPreview: buildNode.dictionaryPreview } }];\n}\n\nconst forbidden = /\\b(INSERT|UPDATE|DELETE|MERGE|DROP|ALTER|TRUNCATE|CREATE|EXEC|EXECUTE|GRANT|REVOKE|BACKUP|RESTORE)\\b/i;\nif (forbidden.test(sql)) {\n return [{ json: { valid: false, type: 'sql_error', error: 'Güvenlik nedeniyle sadece SELE
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
2026-06-23 23:23:23 +00:00
|
|
|
|
1130,
|
|
|
|
|
|
40
|
2026-03-21 16:59:22 +00:00
|
|
|
|
],
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "07579d4a-b1ef-4a8f-b8d6-f646af9c938f",
|
|
|
|
|
|
"name": "Validate SQL"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"conditions": {
|
|
|
|
|
|
"options": {
|
|
|
|
|
|
"caseSensitive": true,
|
|
|
|
|
|
"leftValue": "",
|
|
|
|
|
|
"typeValidation": "strict",
|
|
|
|
|
|
"version": 2
|
|
|
|
|
|
},
|
|
|
|
|
|
"conditions": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "f4c65cdb-f275-42a6-971c-ea1f62d9ff87",
|
|
|
|
|
|
"leftValue": "={{ $json.valid }}",
|
|
|
|
|
|
"rightValue": true,
|
|
|
|
|
|
"operator": {
|
|
|
|
|
|
"type": "boolean",
|
|
|
|
|
|
"operation": "true",
|
|
|
|
|
|
"singleValue": true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"combinator": "and"
|
|
|
|
|
|
},
|
|
|
|
|
|
"options": {}
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.if",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
1350,
|
|
|
|
|
|
40
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "4f189011-b12c-465e-8de0-72c9bc1457ef",
|
|
|
|
|
|
"name": "SQL Valid?"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"operation": "executeQuery",
|
|
|
|
|
|
"query": "={{ $json.sql }}"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.microsoftSql",
|
|
|
|
|
|
"typeVersion": 1.1,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
1580,
|
|
|
|
|
|
-60
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "a2ec036a-e23b-4904-89aa-766523f0dd35",
|
|
|
|
|
|
"name": "Execute Generated SQL"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"jsCode": "const rows = $input.all().map(i => i.json);\nconst validate = $('Validate SQL').first().json || {};\nconst columns = rows.length ? Object.keys(rows[0]) : [];\nreturn [{ json: { type: 'sql_result', question: validate.question, sql: validate.sql, columns, rows, rowCount: rows.length } }];"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
1810,
|
|
|
|
|
|
-60
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "76a92057-357a-4acf-87f9-9bd52a55b85a",
|
|
|
|
|
|
"name": "Format SQL Result"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"parameters": {
|
|
|
|
|
|
"jsCode": "const e = $input.first().json || {};\nreturn [{ json: { type: 'sql_error', question: e.question, error: e.error || 'SQL üretilemedi.', generatedText: e.generatedText || null, debug: { dictionaryAvailable: e.dictionaryAvailable, dictionaryPreview: e.dictionaryPreview, ollamaRawOutput: e.ollamaRawOutput || null } } }];"
|
|
|
|
|
|
},
|
|
|
|
|
|
"type": "n8n-nodes-base.code",
|
|
|
|
|
|
"typeVersion": 2,
|
|
|
|
|
|
"position": [
|
|
|
|
|
|
1580,
|
|
|
|
|
|
160
|
|
|
|
|
|
],
|
|
|
|
|
|
"id": "c34f8a67-2b5c-4700-84fd-1171a4ce6485",
|
|
|
|
|
|
"name": "SQL Error Response"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"pinData": {},
|
|
|
|
|
|
"connections": {
|
|
|
|
|
|
"Webhook": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Build Intent Request",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
2026-06-23 23:23:23 +00:00
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Build Intent Request": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "Intent Ollama",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Intent Ollama": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "Normalize Intent",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Normalize Intent": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "Is SQL?",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
2026-03-21 16:59:22 +00:00
|
|
|
|
]
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"Is SQL?": {
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Build Data Dictionary Query",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Build Chat Request",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"Build Chat Request": {
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Chat Ollama",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"Chat Ollama": {
|
|
|
|
|
|
"main": [
|
2026-03-21 16:59:22 +00:00
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Chat Response",
|
|
|
|
|
|
"type": "main",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"index": 0
|
2026-06-23 23:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Build Data Dictionary Query": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
2026-03-21 16:59:22 +00:00
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Get Data Dictionary Prompt",
|
|
|
|
|
|
"type": "main",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"Get Data Dictionary Prompt": {
|
|
|
|
|
|
"main": [
|
2026-03-21 16:59:22 +00:00
|
|
|
|
[
|
|
|
|
|
|
{
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"node": "Build SQL Request",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Build SQL Request": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "SQL Ollama",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"SQL Ollama": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "Validate SQL",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Validate SQL": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "SQL Valid?",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"SQL Valid?": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "Execute Generated SQL",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "SQL Error Response",
|
|
|
|
|
|
"type": "main",
|
|
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"Execute Generated SQL": {
|
|
|
|
|
|
"main": [
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
"node": "Format SQL Result",
|
|
|
|
|
|
"type": "main",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"index": 0
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"active": false,
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"settings": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"executionOrder": "v1"
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"versionId": "074ecf8d-3021-4e36-b8cb-5fac6d27f68f",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"meta": {
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"templateCredsSetupCompleted": false
|
2026-03-21 16:59:22 +00:00
|
|
|
|
},
|
2026-06-23 23:23:23 +00:00
|
|
|
|
"id": "ChatSQLDataDictionaryHTTPStableV2",
|
2026-03-21 16:59:22 +00:00
|
|
|
|
"tags": []
|
|
|
|
|
|
}
|