SSE streaming: прогресс по каждому файлу во время LLM (v1.0.53)
This commit is contained in:
@@ -61,7 +61,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="/nubes-logo.svg" alt="Nubes">
|
<img src="/nubes-logo.svg" alt="Nubes">
|
||||||
<span class="title">Сверка договоров — LLM <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.52 — Lucee</span></span>
|
<span class="title">Сверка договоров — LLM <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.53 — Lucee</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -332,7 +332,7 @@ document.getElementById('llmBtn').addEventListener('click', async function() {
|
|||||||
var diffBody = document.getElementById('diffBody');
|
var diffBody = document.getElementById('diffBody');
|
||||||
var diffStatus = document.getElementById('diffStatus');
|
var diffStatus = document.getElementById('diffStatus');
|
||||||
diffCard.style.display = 'block';
|
diffCard.style.display = 'block';
|
||||||
diffBody.innerHTML = '⏳ LLM-извлечение...';
|
diffBody.innerHTML = '';
|
||||||
diffStatus.textContent = '⏳ 0.0с';
|
diffStatus.textContent = '⏳ 0.0с';
|
||||||
|
|
||||||
var llmStart = Date.now();
|
var llmStart = Date.now();
|
||||||
@@ -340,28 +340,37 @@ document.getElementById('llmBtn').addEventListener('click', async function() {
|
|||||||
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
// ОДИН запрос — всё внутри: экстракция + сравнение
|
// SSE — прогресс по каждому файлу
|
||||||
try {
|
var es = new EventSource('/process.cfm?contract_id=' + contractId);
|
||||||
var resp = await fetch('/process.cfm?contract_id=' + contractId);
|
|
||||||
var data = await resp.json();
|
|
||||||
|
|
||||||
if (data.OK) {
|
es.onmessage = function(e) {
|
||||||
// Показать результаты экстракции
|
var d = JSON.parse(e.data);
|
||||||
var extr = data.EXTRACTED || [];
|
|
||||||
diffBody.innerHTML = '';
|
if (d.type === 'extract_start') {
|
||||||
extr.forEach(function(e) {
|
diffBody.innerHTML += '<div style="font-size:12px;color:var(--muted);margin-top:2px;" id="ext-' + d.supplement_id + '">⏳ ' + d.filename + ' → LLM... <span class="file-timer" data-start="' + Date.now() + '">0.0с</span></div>';
|
||||||
if (e.ERROR) {
|
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--destructive);">✗ ' + (e.FILENAME||e.SUPPLEMENT_ID) + ': ' + e.ERROR + '</div>';
|
|
||||||
} else if (e.SKIPPED) {
|
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--muted);">⏭ ' + (e.FILENAME||e.SUPPLEMENT_ID) + ' (уже извлечено, ' + e.ROWS_SAVED + ' строк)</div>';
|
|
||||||
} else {
|
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--green);">✓ ' + (e.FILENAME||e.SUPPLEMENT_ID) + ': ' + e.ROWS_SAVED + ' строк</div>';
|
|
||||||
}
|
}
|
||||||
});
|
else if (d.type === 'extract_done') {
|
||||||
|
var el = document.getElementById('ext-' + d.supplement_id);
|
||||||
|
if (el) el.innerHTML = '✓ <span style="color:var(--green);">' + d.filename + ': ' + d.count + ' строк (' + d.time_s + 'с)</span>';
|
||||||
|
}
|
||||||
|
else if (d.type === 'extract_skip') {
|
||||||
|
diffBody.innerHTML += '<div style="font-size:12px;color:var(--muted);margin-top:2px;">⏭ ' + d.filename + ' (уже извлечено, ' + d.count + ' строк)</div>';
|
||||||
|
}
|
||||||
|
else if (d.type === 'extract_error') {
|
||||||
|
var el = document.getElementById('ext-' + d.supplement_id);
|
||||||
|
if (el) el.innerHTML = '✗ <span style="color:var(--destructive);">' + d.filename + ': ' + d.error + '</span>';
|
||||||
|
else diffBody.innerHTML += '<div style="font-size:12px;color:var(--destructive);margin-top:2px;">✗ ' + d.filename + ': ' + d.error + '</div>';
|
||||||
|
}
|
||||||
|
else if (d.type === 'done') {
|
||||||
|
clearInterval(timerInterval);
|
||||||
|
es.close();
|
||||||
|
diffStatus.textContent = '✓ ' + d.total_time_s + 'с';
|
||||||
|
btn.innerHTML = '<i data-lucide="check" style="width:16px;height:16px;"></i> ✓ Готово';
|
||||||
|
document.getElementById('chatCard').style.display = 'block';
|
||||||
|
|
||||||
// Сравнение
|
// Сравнение
|
||||||
diffBody.innerHTML += '<div style="margin-top:8px;font-weight:600;">📊 Сравнение:</div>';
|
diffBody.innerHTML += '<div style="margin-top:8px;font-weight:600;">📊 Сравнение:</div>';
|
||||||
var all = data.ALL_CHANGES || [];
|
var all = d.all_changes || [];
|
||||||
if (all.length === 0) {
|
if (all.length === 0) {
|
||||||
diffBody.innerHTML += '<div style="color:var(--muted);font-size:12px;">Нет допников для сравнения.</div>';
|
diffBody.innerHTML += '<div style="color:var(--muted);font-size:12px;">Нет допников для сравнения.</div>';
|
||||||
} else {
|
} else {
|
||||||
@@ -369,7 +378,6 @@ document.getElementById('llmBtn').addEventListener('click', async function() {
|
|||||||
var s = ch.SUMMARY || {};
|
var s = ch.SUMMARY || {};
|
||||||
diffBody.innerHTML += '<div style="font-weight:600;margin-top:12px;">📄 Допник (' + ch.TYPE + ')</div>';
|
diffBody.innerHTML += '<div style="font-weight:600;margin-top:12px;">📄 Допник (' + ch.TYPE + ')</div>';
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;margin-left:8px;"><span style="color:var(--green);">+' + (s.ADDED||0) + '</span> <span style="color:#eab308;">~' + (s.CHANGED||0) + '</span> <span style="color:var(--destructive);">-' + (s.DELETED||0) + '</span></div>';
|
diffBody.innerHTML += '<div style="font-size:12px;margin-left:8px;"><span style="color:var(--green);">+' + (s.ADDED||0) + '</span> <span style="color:#eab308;">~' + (s.CHANGED||0) + '</span> <span style="color:var(--destructive);">-' + (s.DELETED||0) + '</span></div>';
|
||||||
diffStatus.textContent = 'изм:' + (s.CHANGED||0) + ' +' + (s.ADDED||0) + ' -' + (s.DELETED||0);
|
|
||||||
|
|
||||||
var changes = ch.CHANGES || [];
|
var changes = ch.CHANGES || [];
|
||||||
if (changes.length === 0) {
|
if (changes.length === 0) {
|
||||||
@@ -386,19 +394,44 @@ document.getElementById('llmBtn').addEventListener('click', async function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
diffBody.innerHTML = '<div style="color:var(--destructive);">✗ ' + (data.ERROR||'Неизвестная ошибка') + '</div>';
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
diffBody.innerHTML = '<div style="color:var(--destructive);">✗ ' + e.message + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(timerInterval);
|
|
||||||
diffStatus.textContent = '✓ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
|
||||||
|
|
||||||
btn.innerHTML = '<i data-lucide="check" style="width:16px;height:16px;"></i> ✓ Готово';
|
|
||||||
document.getElementById('chatCard').style.display = 'block';
|
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
else if (d.type === 'error') {
|
||||||
|
clearInterval(timerInterval);
|
||||||
|
es.close();
|
||||||
|
diffBody.innerHTML += '<div style="color:var(--destructive);margin-top:8px;">✗ ' + d.message + '</div>';
|
||||||
|
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить (LLM)';
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
es.onerror = function() {
|
||||||
|
clearInterval(timerInterval);
|
||||||
|
es.close();
|
||||||
|
if (diffBody.innerHTML === '') {
|
||||||
|
diffBody.innerHTML = '<div style="color:var(--destructive);">✗ Ошибка соединения</div>';
|
||||||
|
}
|
||||||
|
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить (LLM)';
|
||||||
|
btn.disabled = false;
|
||||||
|
lucide.createIcons();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Таймер для файловых строк
|
||||||
|
var fileTimerInterval = setInterval(function() {
|
||||||
|
var now = Date.now();
|
||||||
|
document.querySelectorAll('.file-timer').forEach(function(el) {
|
||||||
|
var start = parseInt(el.dataset.start);
|
||||||
|
if (start) el.textContent = ((now - start) / 1000).toFixed(1) + 'с';
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
// Сохранить интервалы для очистки
|
||||||
|
es._fileTimer = fileTimerInterval;
|
||||||
|
var origClose = es.close;
|
||||||
|
es.close = function() {
|
||||||
|
clearInterval(fileTimerInterval);
|
||||||
|
origClose.call(es);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function _dc(c, field) {
|
function _dc(c, field) {
|
||||||
|
|||||||
+55
-40
@@ -1,16 +1,25 @@
|
|||||||
<cfsetting showdebugoutput="no" enablecfoutputonly="true" requesttimeout="600">
|
<cfsetting showdebugoutput="no" enablecfoutputonly="true" requesttimeout="600">
|
||||||
<cfheader name="Content-Type" value="application/json; charset=utf-8">
|
<cfheader name="Content-Type" value="text/event-stream; charset=utf-8">
|
||||||
|
<cfheader name="Cache-Control" value="no-cache">
|
||||||
|
<cfheader name="Connection" value="keep-alive">
|
||||||
|
<cfheader name="X-Accel-Buffering" value="no">
|
||||||
|
|
||||||
<cfparam name="url.contract_id" default="">
|
<cfparam name="url.contract_id" default="">
|
||||||
<cfset result = {OK: false, ERROR: ""}>
|
|
||||||
|
<cfoutput>: ok
|
||||||
|
|
||||||
|
</cfoutput><cfflush>
|
||||||
|
|
||||||
<cftry>
|
<cftry>
|
||||||
<cfif NOT len(url.contract_id)>
|
<cfif NOT len(url.contract_id)>
|
||||||
<cfset result.ERROR = "contract_id required">
|
<cfoutput>data: #serializeJSON({type:"error",message:"contract_id required"})#
|
||||||
<cfelse>
|
|
||||||
<!--- Все допники договора с parsed_text --->
|
</cfoutput><cfflush>
|
||||||
|
<cfabort>
|
||||||
|
</cfif>
|
||||||
|
|
||||||
<cfquery name="supps" datasource="baza">
|
<cfquery name="supps" datasource="baza">
|
||||||
SELECT s.id as supp_id, s.type, d.id as doc_id, d.filename, d.elements_json, d.parsed_text
|
SELECT s.id as supp_id, s.type, d.id as doc_id, d.filename, d.elements_json
|
||||||
FROM supplements s
|
FROM supplements s
|
||||||
JOIN documents d ON s.document_id = d.id
|
JOIN documents d ON s.document_id = d.id
|
||||||
WHERE s.contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
WHERE s.contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||||
@@ -19,12 +28,14 @@
|
|||||||
</cfquery>
|
</cfquery>
|
||||||
|
|
||||||
<cfif supps.recordCount EQ 0>
|
<cfif supps.recordCount EQ 0>
|
||||||
<cfset result.ERROR = "Нет распарсенных файлов. Сначала нажмите Парсинг.">
|
<cfoutput>data: #serializeJSON({type:"error",message:"Нет распарсенных файлов. Сначала нажмите Парсинг."})#
|
||||||
<cfelse>
|
|
||||||
<cfset extractedInfo = []>
|
</cfoutput><cfflush>
|
||||||
<cfset extractedRows = {}> <!--- supp_id → [rows] --->
|
<cfabort>
|
||||||
|
</cfif>
|
||||||
|
|
||||||
|
<cfset extractedRows = {}>
|
||||||
|
|
||||||
<!--- Промпт из БД --->
|
|
||||||
<cfset promptText = "">
|
<cfset promptText = "">
|
||||||
<cfquery name="pq" datasource="baza">
|
<cfquery name="pq" datasource="baza">
|
||||||
SELECT prompt_text FROM prompts WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
SELECT prompt_text FROM prompts WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||||
@@ -33,7 +44,6 @@
|
|||||||
<cfset promptText = pq.prompt_text>
|
<cfset promptText = pq.prompt_text>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<!--- DEFAULT_PROMPT --->
|
|
||||||
<cfset defaultPrompt = 'Ты — анализатор договоров. Ниже текст спецификации услуг из договора облачного провайдера.
|
<cfset defaultPrompt = 'Ты — анализатор договоров. Ниже текст спецификации услуг из договора облачного провайдера.
|
||||||
|
|
||||||
Найди ВСЕ таблицы со списком услуг. Извлеки каждую строку в JSON-массив.
|
Найди ВСЕ таблицы со списком услуг. Извлеки каждую строку в JSON-массив.
|
||||||
@@ -60,14 +70,12 @@
|
|||||||
{parsed_text}
|
{parsed_text}
|
||||||
---'>
|
---'>
|
||||||
|
|
||||||
<!--- LLM API --->
|
|
||||||
<cfset apiKey = "sk-ucI5YvOticoOQ9Kuj5K9mQ">
|
<cfset apiKey = "sk-ucI5YvOticoOQ9Kuj5K9mQ">
|
||||||
|
<cfset llmStart = getTickCount()>
|
||||||
|
|
||||||
<!--- Шаг 1: LLM-извлечение для каждого допника --->
|
|
||||||
<cfloop query="supps">
|
<cfloop query="supps">
|
||||||
<cfset sid = supps.supp_id>
|
<cfset sid = supps.supp_id>
|
||||||
|
|
||||||
<!--- Проверить: уже извлечено? --->
|
|
||||||
<cfquery name="ex" datasource="baza">
|
<cfquery name="ex" datasource="baza">
|
||||||
SELECT row_num, name, price, qty, sum, date_start
|
SELECT row_num, name, price, qty, sum, date_start
|
||||||
FROM spec_rows WHERE supplement_id = <cfqueryparam value="#sid#" cfsqltype="cf_sql_varchar">
|
FROM spec_rows WHERE supplement_id = <cfqueryparam value="#sid#" cfsqltype="cf_sql_varchar">
|
||||||
@@ -75,17 +83,17 @@
|
|||||||
</cfquery>
|
</cfquery>
|
||||||
|
|
||||||
<cfif ex.recordCount GT 0>
|
<cfif ex.recordCount GT 0>
|
||||||
<!--- Уже извлечено — загрузить существующие --->
|
|
||||||
<cfset rows = []>
|
<cfset rows = []>
|
||||||
<cfloop query="ex">
|
<cfloop query="ex">
|
||||||
<cfset arrayAppend(rows, {ROW_NUM: ex.row_num, NAME: ex.name, PRICE: ex.price, QTY: ex.qty, SUM: ex.sum, DATE_START: ex.date_start})>
|
<cfset arrayAppend(rows, {ROW_NUM: ex.row_num, NAME: ex.name, PRICE: ex.price, QTY: ex.qty, SUM: ex.sum, DATE_START: ex.date_start})>
|
||||||
</cfloop>
|
</cfloop>
|
||||||
<cfset extractedRows[sid] = rows>
|
<cfset extractedRows[sid] = rows>
|
||||||
<cfset arrayAppend(extractedInfo, {SUPPLEMENT_ID: sid, FILENAME: supps.filename, ROWS_SAVED: ex.recordCount, SKIPPED: true})>
|
<cfoutput>data: #serializeJSON({type:"extract_skip",supplement_id:sid,filename:supps.filename,count:ex.recordCount})#
|
||||||
|
|
||||||
|
</cfoutput><cfflush>
|
||||||
<cfcontinue>
|
<cfcontinue>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<!--- textify: elements → текст --->
|
|
||||||
<cfset elements = deserializeJSON(supps.elements_json)>
|
<cfset elements = deserializeJSON(supps.elements_json)>
|
||||||
<cfset textLines = []>
|
<cfset textLines = []>
|
||||||
<cfloop array="#elements#" index="el">
|
<cfloop array="#elements#" index="el">
|
||||||
@@ -114,10 +122,13 @@
|
|||||||
</cfloop>
|
</cfloop>
|
||||||
<cfset parsedText = arrayToList(textLines, chr(10))>
|
<cfset parsedText = arrayToList(textLines, chr(10))>
|
||||||
|
|
||||||
<!--- Промпт --->
|
|
||||||
<cfset prompt = replace(len(promptText) ? promptText : defaultPrompt, "{parsed_text}", parsedText, "ALL")>
|
<cfset prompt = replace(len(promptText) ? promptText : defaultPrompt, "{parsed_text}", parsedText, "ALL")>
|
||||||
|
|
||||||
<!--- LLM --->
|
<cfoutput>data: #serializeJSON({type:"extract_start",supplement_id:sid,filename:supps.filename})#
|
||||||
|
|
||||||
|
</cfoutput><cfflush>
|
||||||
|
|
||||||
|
<cfset t0 = getTickCount()>
|
||||||
<cfset llmPayload = {
|
<cfset llmPayload = {
|
||||||
"model": "gpt-oss-120b",
|
"model": "gpt-oss-120b",
|
||||||
"messages": [{"role": "user", "content": prompt}],
|
"messages": [{"role": "user", "content": prompt}],
|
||||||
@@ -131,23 +142,24 @@
|
|||||||
<cfhttpparam type="body" value="#serializeJSON(llmPayload)#">
|
<cfhttpparam type="body" value="#serializeJSON(llmPayload)#">
|
||||||
</cfhttp>
|
</cfhttp>
|
||||||
|
|
||||||
|
<cfset elapsed = int((getTickCount() - t0) / 1000 * 10 + 0.5) / 10>
|
||||||
|
|
||||||
<cfif NOT (cfhttp.statusCode EQ 200 OR left(cfhttp.statusCode, 3) EQ "200")>
|
<cfif NOT (cfhttp.statusCode EQ 200 OR left(cfhttp.statusCode, 3) EQ "200")>
|
||||||
<cfset arrayAppend(extractedInfo, {SUPPLEMENT_ID: sid, FILENAME: supps.filename, ERROR: "LLM HTTP #cfhttp.statusCode#"})>
|
<cfoutput>data: #serializeJSON({type:"extract_error",supplement_id:sid,filename:supps.filename,error:"LLM HTTP #cfhttp.statusCode#",time_s:elapsed})#
|
||||||
|
|
||||||
|
</cfoutput><cfflush>
|
||||||
<cfcontinue>
|
<cfcontinue>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<cfset llmResp = deserializeJSON(cfhttp.fileContent)>
|
<cfset llmResp = deserializeJSON(cfhttp.fileContent)>
|
||||||
<cfset llmText = llmResp.choices[1].message.content>
|
<cfset llmText = llmResp.choices[1].message.content>
|
||||||
|
|
||||||
<!--- Вытащить JSON из ответа --->
|
|
||||||
<cfset jsonText = llmText>
|
<cfset jsonText = llmText>
|
||||||
<cfset p1 = find("```json", jsonText)>
|
<cfset p1 = find("```json", jsonText)>
|
||||||
<cfif p1 GT 0>
|
<cfif p1 GT 0>
|
||||||
<cfset jsonText = mid(jsonText, p1 + 7, len(jsonText))>
|
<cfset jsonText = mid(jsonText, p1 + 7, len(jsonText))>
|
||||||
<cfset p2 = find("```", jsonText)>
|
<cfset p2 = find("```", jsonText)>
|
||||||
<cfif p2 GT 0>
|
<cfif p2 GT 0><cfset jsonText = left(jsonText, p2 - 1)></cfif>
|
||||||
<cfset jsonText = left(jsonText, p2 - 1)>
|
|
||||||
</cfif>
|
|
||||||
<cfelse>
|
<cfelse>
|
||||||
<cfset p1 = find("{", jsonText)>
|
<cfset p1 = find("{", jsonText)>
|
||||||
<cfset p2 = find("}", reverse(jsonText))>
|
<cfset p2 = find("}", reverse(jsonText))>
|
||||||
@@ -164,7 +176,6 @@
|
|||||||
<cfset rows = extracted.rows>
|
<cfset rows = extracted.rows>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<!--- Сохранить spec_rows (DELETE + INSERT) --->
|
|
||||||
<cftransaction>
|
<cftransaction>
|
||||||
<cfquery datasource="baza">
|
<cfquery datasource="baza">
|
||||||
DELETE FROM spec_rows WHERE supplement_id = <cfqueryparam value="#sid#" cfsqltype="cf_sql_varchar">
|
DELETE FROM spec_rows WHERE supplement_id = <cfqueryparam value="#sid#" cfsqltype="cf_sql_varchar">
|
||||||
@@ -197,18 +208,22 @@
|
|||||||
<cfset arrayAppend(savedRows, {ROW_NUM: structKeyExists(r,'row_num')?r.row_num:0, NAME: structKeyExists(r,'name')?r.name:'', PRICE: structKeyExists(r,'price')?r.price:'', QTY: structKeyExists(r,'qty')?r.qty:'', SUM: structKeyExists(r,'sum')?r.sum:'', DATE_START: structKeyExists(r,'date_start')?r.date_start:''})>
|
<cfset arrayAppend(savedRows, {ROW_NUM: structKeyExists(r,'row_num')?r.row_num:0, NAME: structKeyExists(r,'name')?r.name:'', PRICE: structKeyExists(r,'price')?r.price:'', QTY: structKeyExists(r,'qty')?r.qty:'', SUM: structKeyExists(r,'sum')?r.sum:'', DATE_START: structKeyExists(r,'date_start')?r.date_start:''})>
|
||||||
</cfloop>
|
</cfloop>
|
||||||
<cfset extractedRows[sid] = savedRows>
|
<cfset extractedRows[sid] = savedRows>
|
||||||
<cfset arrayAppend(extractedInfo, {SUPPLEMENT_ID: sid, FILENAME: supps.filename, ROWS_SAVED: arrayLen(savedRows), SKIPPED: false})>
|
|
||||||
|
<cfoutput>data: #serializeJSON({type:"extract_done",supplement_id:sid,filename:supps.filename,count:arrayLen(savedRows),time_s:elapsed})#
|
||||||
|
|
||||||
|
</cfoutput><cfflush>
|
||||||
|
|
||||||
<cfcatch>
|
<cfcatch>
|
||||||
<cfset arrayAppend(extractedInfo, {SUPPLEMENT_ID: sid, FILENAME: supps.filename, ERROR: "JSON parse: " & cfcatch.message})>
|
<cfoutput>data: #serializeJSON({type:"extract_error",supplement_id:sid,filename:supps.filename,error:"JSON parse: " & cfcatch.message,time_s:elapsed})#
|
||||||
|
|
||||||
|
</cfoutput><cfflush>
|
||||||
</cfcatch>
|
</cfcatch>
|
||||||
</cftry>
|
</cftry>
|
||||||
</cfloop>
|
</cfloop>
|
||||||
|
|
||||||
<!--- Шаг 2: Differ (логика из differ.cfm, но на свежих данных) --->
|
<!--- Differ --->
|
||||||
<cfset allChanges = []>
|
<cfset allChanges = []>
|
||||||
|
|
||||||
<!--- Найти initial --->
|
|
||||||
<cfquery name="initSup" datasource="baza">
|
<cfquery name="initSup" datasource="baza">
|
||||||
SELECT id FROM supplements WHERE contract_id=<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar"> AND type='initial' ORDER BY created_at LIMIT 1
|
SELECT id FROM supplements WHERE contract_id=<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar"> AND type='initial' ORDER BY created_at LIMIT 1
|
||||||
</cfquery>
|
</cfquery>
|
||||||
@@ -276,14 +291,14 @@
|
|||||||
</cfloop>
|
</cfloop>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<cfset result = {
|
<cfset totalTime = int((getTickCount() - llmStart) / 1000 * 10 + 0.5) / 10>
|
||||||
OK: true,
|
<cfoutput>data: #serializeJSON({type:"done",total_time_s:totalTime,all_changes:allChanges})#
|
||||||
CONTRACT_ID: url.contract_id,
|
|
||||||
EXTRACTED: extractedInfo,
|
</cfoutput><cfflush>
|
||||||
ALL_CHANGES: allChanges
|
|
||||||
}>
|
<cfcatch>
|
||||||
</cfif>
|
<cfoutput>data: #serializeJSON({type:"error",message:cfcatch.message,detail:cfcatch.detail})#
|
||||||
</cfif>
|
|
||||||
<cfcatch><cfset result = {OK: false, ERROR: cfcatch.message, DETAIL: cfcatch.detail}></cfcatch>
|
</cfoutput><cfflush>
|
||||||
|
</cfcatch>
|
||||||
</cftry>
|
</cftry>
|
||||||
<cfoutput>#serializeJSON(result)#</cfoutput>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user